intel technology journal q4, 1999...register file to eliminate redundant references to array...

72
Intel Technology Journal Q4, 1999 Preface Lin Chao Editor Intel Technology Journal With the close of the year 1999, it is appropriate that we look forward to Intel's next generation architecture--the Intel Architecture (IA)-64. The IA-64 represents a significant shift for Intel architecture: it moves from 32-bits to 64-bits. Targeted for production in mid-2000, the Itanium™ processor will be the first IA-64 processor. One of Intel's key aims is to facilitate a transition of this magnitude. To this end, Intel and key industry suppliers are working to ensure that a complete set of "ingredients" is available for the IA-64 architecture. This includes operating systems, compilers, and application development tools that are "64-bit capable." In this issue of the Intel Technology Journal, you will learn about Intel's efforts in IA-64 software technology. In the first papers, Intel's own IA-64 compiler efforts are discussed. The first paper gives an overview of Intel's production compiler, code named "Electron." We then move to the second and third papers where software development tools for the IA-64 architecture are discussed. As is often the case with a brand new architecture, software companies start developing software in advance of actual hardware. SoftSDV (a software-based system development vehicle) is a tool that simulates IA-64 hardware platforms in lieu of actually having the hardware. This tool assists engineers in porting commercial operating systems and applications to the IA-64. Intel's IA-64 Assembler is described in the fourth paper. The Assembler can simplify IA-64 assembly language programming. Validation is a critical function in the testing of new circuits such as those on IA-64 silicon. The fourth paper describes the porting of the Linux* and Mach* operating systems and how they run on software simulators to exercise operating system related functionality in the IA-64 architecture. The final two papers discuss the floating-point functions on the IA-64 architecture. Fast and accurate computation of transcendental functions (e.g. sin, cos, and exp) and the implementation of floating-point operations are also discussed. Copyright © Intel Corporation 1999. This publication was downloaded from http://www.intel.com/. Legal notices at http://www.intel.com/sites/corporate/tradmarx.htm

Upload: others

Post on 17-Jul-2020

0 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

Preface Lin Chao Editor Intel Technology Journal With the close of the year 1999, it is appropriate that we look forward to Intel's next generation architecture--the Intel Architecture (IA)-64. The IA-64 represents a significant shift for Intel architecture: it moves from 32-bits to 64-bits. Targeted for production in mid-2000, the Itanium™ processor will be the first IA-64 processor. One of Intel's key aims is to facilitate a transition of this magnitude. To this end, Intel and key industry suppliers are working to ensure that a complete set of "ingredients" is available for the IA-64 architecture. This includes operating systems, compilers, and application development tools that are "64-bit capable." In this issue of the Intel Technology Journal, you will learn about Intel's efforts in IA-64 software technology. In the first papers, Intel's own IA-64 compiler efforts are discussed. The first paper gives an overview of Intel's production compiler, code named "Electron." We then move to the second and third papers where software development tools for the IA-64 architecture are discussed. As is often the case with a brand new architecture, software companies start developing software in advance of actual hardware. SoftSDV (a software-based system development vehicle) is a tool that simulates IA-64 hardware platforms in lieu of actually having the hardware. This tool assists engineers in porting commercial operating systems and applications to the IA-64. Intel's IA-64 Assembler is described in the fourth paper. The Assembler can simplify IA-64 assembly language programming. Validation is a critical function in the testing of new circuits such as those on IA-64 silicon. The fourth paper describes the porting of the Linux* and Mach* operating systems and how they run on software simulators to exercise operating system related functionality in the IA-64 architecture. The final two papers discuss the floating-point functions on the IA-64 architecture. Fast and accurate computation of transcendental functions (e.g. sin, cos, and exp) and the implementation of floating-point operations are also discussed.

Copyright © Intel Corporation 1999. This publication was downloaded from http://www.intel.com/. Legal notices at http://www.intel.com/sites/corporate/tradmarx.htm

Page 2: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

The Challenges of New Software for a New Architecture

By Richard Wirt, Intel Fellow and Director, MicroComputer Software Labs

Intel Corp.

In this Q4, 1999 issue of the Intel Technology Journal, we look at some of the software efforts that have gone into bringing out the new IA-64 architecture. Early in the development of the IA-64 architecture, we set very aggressive goals for the software compilers, floating point performance, and simulation environment.

Over the years, the compiler has become a very important factor in contributing to the utilization of a processor's architecture. The Intel IA-64 compiler, code named Electron, was considered part of the IA-64 architecture. We had compiler architects working side by side with CPU architects. This was very important since the IA-64 is a new architecture that exploits many new concepts, and the microarchitecture depends on the compiler to manage many of the resource dependencies. Not only has this compiler served to bring up many of the initial operating systems and applications, it has also served to help other third party compiler vendors to understand how to generate code for this new architecture. The compiler has met the challenge of pushing the boundaries and of achieving our initial architecture goals.

Being a new architecture, the IA-64 architecture provided Intel with the opportunity to take another look at the floating point on the IA-64 architecture. We set a goal of making it faster, making it fully IEEE compliant, and of achieving a near 0.5 units in the last place of precision for the

transcendental functions libraries. The width of the architecture allowed us to take another look at the traditional algorithms. Since divide and square root are executed in software in the Itanium™ processor implementation of the IA-64 architecture, we have also formally proven the algorithms used.

Perhaps the biggest challenge was to simultaneously bring up and debug a new architecture, new chipsets, new compilers, and new versions of the operating systems. To do this, we set our sights on building a full system-level simulator that functionally behaved faithfully at the register and I/O ports levels as the first platforms were being built. Additionally, we wanted to be able to add performance simulators for the CPU, caches, and chipsets. The software simulator became known as the SoftSDV. It met its requirements to be functionally equivalent to the first software development vehicles manufactured for use by the ISVs and OS venders. Well before first silicon was available for the Itanium processor, we were running the firmware, all the major operating systems, and many major applications on the SoftSDV. This served its purpose very well, as we were able to run the same binaries on the real SDVs, using new silicon, within hours of their availability from Intel's manufacturing fabs.

With this issue of the Intel Technology Journal, we hope you will get a better insight into the software behind Intel's new 64-bit

The Challenges of New Software for a New Architecture 1

Page 3: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

architecture and gain an appreciation of the outstanding efforts of the many people on the software teams that contributed to the IA-64 software technologies.

Copyright © Intel Corporation 1999. This publication was downloaded from http://www.intel.com/. Legal notices at http://www.intel.com/sites/corporate/tradmarx.htm

The Challenges of New Software for a New Architecture 2

Page 4: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

An Overview of the Intel IA-64 Compiler 1

An Overview of the Intel® IA-64 Compiler

Carole Dulong, Microcomputer Software Laboratory, Intel CorporationRakesh Krishnaiyer, Microcomputer Software Laboratory, Intel CorporationDattatraya Kulkarni, Microcomputer Software Laboratory, Intel Corporation

Daniel Lavery, Microcomputer Software Laboratory, Intel CorporationWei Li, Microcomputer Software Laboratory, Intel Corporation

John Ng, Microcomputer Software Laboratory, Intel CorporationDavid Sehr, Microcomputer Software Laboratory, Intel Corporation

Index words: IA-64, predication, speculation, compiler optimization, loop transformations, scalaroptimizations, profiling, interprocedural optimizations

ABSTRACT

The IA-64 architecture is designed with a uniquecombination of rich features so that it overcomes thelimitations of traditional architectures and providesperformance scalability for the future. The IA-64 featuresexpose new opportunities for the compiler to optimizeapplications. We have incorporated into the Intel IA-64compiler the key technology necessary to exploit thesenew optimization opportunities and to boost theperformance of applications on the IA-64 hardware. Inthis paper, we provide an overview of the Intel IA-64compiler, discuss and illustrate several optimizationtechniques, and explain how these optimizations helpharness the power of IA-64 for higher applicationperformance.

INTRODUCTIONThe IA-64 architecture has a rich set of features includingcontrol and data speculation, predication, large registerfiles, and an advanced branch architecture[7, 13]. These features allow the compiler to optimizeapplications in new ways. To this end, the Intel IA-64compiler incorporates the key technology necessary toexploit new optimization opportunities and to boost theperformance of applications on IA-64 systems.

The Intel IA-64 compiler targets three main goals whilecompiling an application: i) to minimize the overhead ofmemory accesses, ii) to minimize the overhead ofbranches, and iii) to maximize instruction-level parallelism.The compilation techniques in the compiler takeadvantage of the IA-64 architectural features that are

expressly designed to alleviate these very overheads. Forinstance, memory operations are eliminated by effectivelyusing the large register file. Optimizations use rotatingregisters to reduce the overhead of software registerrenaming in loops. Predication is used in many situations,such as removing hard-to-predict branches andimplementing an efficient prefetching policy. The compileruses control and data speculation to eliminate redundantloads, stores, and computations.

In the first section of this paper, we present the high-levelsoftware architecture of the Intel IA-64 compiler. We thendescribe profile-guided and interprocedural optimizations,respectively. Memory disambiguation, a key analysistechnique that enables several optimizations, is thendiscussed. We follow this with a description of memoryoptimizations. The design provisions for supportingparallelism at both coarse and fine granularity arediscussed next followed by a section on scalaroptimizations, which are aimed at eliminating redundantcomputations and expressions. Finally, we brieflydescribe code generation and scheduling techniques inthe compiler.

THE ARCHITECTURE OF THE INTELIA-64 COMPILERThe software architecture of the Intel IA-64 compiler isshown in Figure 1. The compiler incorporates i) state-of-the-art optimization techniques known in the compilercommunity, ii) optimization techniques that are extendedto include the resources and features in the IA-64, and iii)new optimization techniques designed to fully leveragethe IA-64 features for higher application performance.

Page 5: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

An Overview of the Intel IA-64 Compiler 2

Many of these techniques are described in subsequentsections of this paper.

The compiler has a common intermediate representationfor C*, C++*, and FORTRAN90*, so that a majority of theoptimization techniques are applicable irrespective of thesource language (although certain optimization techniquestake advantage of the special aspects of the sourcelanguage).

Information about the program execution behavior, profileinformation, can be very useful in optimizing programs.The components in the Intel IA-64 compiler are designedto be aware of profile information [22], so that thecompiler can select and tune optimizations for the targetapplication when run-time profile information is available.Interprocedural analysis and optimization [16] haveproven to be effective in optimizing applications byexposing opportunities across procedure call boundaries.

The optimizations in the Intel IA-64 compiler can begrouped into high-level optimizations including memoryoptimization, and parallelization and vectorization; scalaroptimizations; and scheduling and code generation,which together achieve the three optimization goalsmentioned in the introduction.

These high-level optimizations include loop-based andregion-based control and data transformations to i)improve memory access locality, ii) expose coarse grainparallelism, iii) vectorize, and iv) expose higher instruction-level parallelism. The high-level optimization techniquesare typically applied to program structures at a higher levelof abstraction than those in many other optimizations.Therefore, the Intel IA-64 compiler elevates the commonintermediate language while applying high-leveloptimizations, and it represents loop structures and arraysubscripts explicitly. This facilitates efficient access andupdate of program structures.

Some of the high-level optimizations in the Intel IA-64compiler are linear loop transformations [17, 18], loopfusion, loop tiling, and loop distribution [16], which canimprove the cache locality of array references. Loopunroll and jam [14] and loop unrolling exploit the largeregister file to eliminate redundant references to arrayelements and to expose more parallelism to the schedulerand code generator. Scalar replacement of memoryreferences [14, 15] is a technique to replace memoryreferences by compiler-generated temporary scalarvariables, which are eventually mapped to registers.Finally, the compiler also inserts the appropriate type ofprefetches [7, 19, 20] for data references so as to overlapthe memory access latency with computation. Thesetransformations are described in detail in later sections ofthis paper.

A primary objective of scalar optimizations is to minimizethe number of computations and the number of referencesto memory. Scalar optimizations achieve this objective bya natural extension to a well known optimization, calledpartial redundancy elimination (PRE) [1,2,11], whichminimizes the number of times an expression is evaluated.We have extended the PRE of the IA-64 compiler toeliminate both redundant computations and redundantloads of the same or known values. Moreover, theextended PRE uses control and data speculation toincrease the number of loads that can be eliminated. Thecounterpart of PRE, called partial dead store elimination(PDSE), is used to remove redundant stores to memory.PDSE moves stores downward in the program’s flow inorder to expose and eliminate stores that have the samevalue.

Scheduling and code generation make effective use ofpredication, speculation, and rotating registers by if-conversion, global code scheduling, software pipelining,and rotating register allocation.

Optimizations in the IA-64 compiler are supported bystate-of-the-art analysis techniques. Memorydisambiguation determines whether two memoryreferences potentially access the same memory location.This information is critical in hiding memory latency,because knowing that a store does not interfere with alater load is essential to scheduling memory referencesearlier. We also use data reuse and exact array datadependence information to guide certain optimizations.

Profiler

C++Front End

Interprocedural Analysis andOptimizations

Memory Optimizations,Parallelization and Vectorization

Global Scalar Optimizations

Predication, Scheduling, RegisterAllocation and Code Generation

FORTRAN 90Front End

Page 6: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

An Overview of the Intel IA-64 Compiler 3

Figure 1: Organization of the Intel IA-64 compiler

PROFILE-GUIDED OPTIMIZATIONSThe compiler may be able to take the fullest advantage ofthe IA-64 architecture when accurate information aboutthe program execution behavior, called profileinformation, is available. Profile information consists of afrequency for each basic block and a probability for eachbranch in the program.

The Intel IA-64 compiler gathers profile information aboutthe specified program and annotates the intermediatelanguage for the program with this information. Thecompiler supports two modes for determining profileinformation: static and dynamic. Static profiling, as thename suggests, is collected by the compiler without anytrial runs of the program. The compiler uses a collection ofheuristics to estimate the frequencies and probabilities,based on knowledge of “typical” program characteristics.Static information is necessarily approximate because itmust be general enough to work with all programs. Thecompiler uses static profiling information whenever theoptimizer is active, unless the developer selects dynamicprofiling.

Instrumented compilationprof_gen

Instrumented execution

Feedback compilation

instrumentedcode

dynamicinformation files

exe + merged dynamicinformation files

Figure 2: Steps in dynamic profile-guided compilation

Dynamic profiling information, or profile feedback, isgathered in a three-step process as shown in Figure 2.Instrumented compilation is the first step, where theapplication developer compiles all or part of theapplication with the prof_gen option, which producesexecutable code instrumented to collect profileinformation. The developer then runs the instrumentedcode one or more times with “typical” input sets to gatherexecution profiles. Finally, the developer compiles theapplication again, this time using the prof_use option,which combines the gathered profiles and annotates theinternal representation of the program with the observedfrequencies and probabilities. Many optimizations thenread the information and use it to guide their behavior.The Intel IA-64 compiler uses profile information to guideseveral optimizations:

1. The compiler uses profile information to integrateprocedures that are most frequently executed intotheir call sites, thereby providing the benefits of largerprogram scope while minimizing code growth.

2. Profile information is also used to guide the layout ofprocedures and blocks within procedures to reduceinstruction cache and TLB misses.

3. Finally, the compiler uses profile information to makethe best use of machine instruction width andspeculation features. By knowing the program’sexecution behavior at scheduling time, the instructionscheduler is capable of selecting the right candidatesfor speculation.

INTERPROCEDURAL ANALYSIS ANDOPTIMIZATIONIA-64’s Explicitly Parallel Instruction Computing (EPIC)architecture makes it possible to execute a large number ofinstructions in a single clock cycle. Therefore, schedulingto fill instruction words is of vital importance to thecompiler. As with other processors, effective use ofinstruction caches and branch prediction are alsoimportant. Traditionally, compilers have operated on oneprocedure of the program at a time. However, suchintraprocedural analysis and optimization is no longersufficient to fully exploit IA-64’s architectural features.The interprocedural optimizer in the Intel IA-64 compiler isprofile-guided and multifile capable, so that it canefficiently provide analysis and optimization for very largeregions of application code.

The Intel IA-64 compiler provides extensive support forinterprocedural analysis and optimization. One set of keyfeatures provided by the compiler is for points-to analysis,mod/ref analysis, side effect propagation, and constantpropagation. The optimizer and scheduler for the IA-64compiler may need to move instructions over large regionsin order to fill scheduling slots. In order to moveoperations over large regions, the compiler frequentlyrequires knowledge of memory references within theregion. Points-to analysis aids this process by accuratelydetermining which memory locations may be referenced bya memory reference. Figure 3 illustrates this with threememory references. If the store to an address in r37 isknown not to store to the same object as the objectpointed to by r33, then the second load may beeliminated. Furthermore, because of IA-64’s dataspeculation feature, it may be possible to eliminate theload even if the accesses might infrequently conflict.Similarly, moving memory references across function callsrequires knowledge of what is modified or referenced bythe function call. This is provided by mod/ref analysis.

Page 7: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

An Overview of the Intel IA-64 Compiler 4

Analysis and optimization for IA-64 also expose the needfor larger program scope for the IA-64 compared totraditional optimizers. To give the optimizer and codegenerator larger scope, the interprocedural optimizerprovides several forms of procedure integration: inlining,cloning, and partial inlining. Inlining replaces a call site bythe body of the function that would be invoked, and itprovides the fullest opportunity for optimization, albeitwith potentially large increases in code size. Cloning andpartial inlining are used to specialize functions toparticular call sites, thereby providing many of thebenefits of inlining while not increasing code sizesignificantly.

ld4 r32=[r33]…st4 [r37]=r34…ld4 r35=[r33]

Figure 3: An example of a situation requiring point-toanalysis information

The compiler attempts to produce the best performancewithout increasing code size, as large code size can causepoor use of instruction cache and TLBs. In order toreduce the impact of code size, while retaining as muchoptimization as possible, the compiler uses profileinformation and targets procedure integration to onlythose sites where it is most effective. Moreover, profileguidance with knowledge of the function call graph isused to lay out functions in an order that minimizesdynamic code size, which is especially important for TLBefficiency.

Memory DisambiguationThe effectiveness and legality of many compileroptimizations rely on the compiler’s ability to accuratelydisambiguate memory references. For example, thecompiler can eliminate a large number of loads and storeswith accurate memory disambiguation. Accurateinformation about memory independence can help exploitmore instruction-level parallelism. The code schedulerrequires accurate memory disambiguation to aggressivelyreorder loads and stores. The legality and effectiveness ofloop transformations rely on the availability of accurateand detailed data-dependence information. The remainderof this section illustrates the different kinds of analysesprovided in the Intel IA-64 compiler for memorydisambiguation.

The simplest disambiguation cases are direct scalar orstructure references. Figure 4 shows a pair of direct

structure references. The compiler may disambiguatethese two memory references either by determining that aand b are different memory objects or that field1 and field2are non-overlapping fields.

a.field1 = ..

.. = b.field2

Figure 4: Disambiguation of direct structure references

Figure 5 shows a pair of indirect references. In general, inorder to disambiguate this pair of memory references, thecompiler must perform points-to analysis [12], whichdetermines the set of memory objects that each pointercould possibly point to. Because the pointer p or q couldbe a global variable or a function parameter, the points-toanalysis performed by the Intel IA-64 compiler isinterprocedural. In some cases, two indirect referencescan be disambiguated based on the pointer types. Forexample, in an ANSI C* conforming program, a pointer to afloat and a pointer to an int cannot point to the samememory object.

*p = ..

.. = *q

Figure 5: Disambiguation of indirect references

Various other language rules and simple information areuseful in providing disambiguation information, evenwhen the more expensive analyses are turned off. Forexample, parameters in programs that conform to theFORTRAN* standard are independent of each other andof common block elements. Therefore, an indirectreference cannot access the same location as a directaccess to a variable that has not had its address taken.

do i= 0, n a(i) = a(i-1) + a(i-2);enddo

Figure 6: Disambiguation of array references

Figure 6 shows an example loop with loop-carried arraydependencies. The value written to a(i) in one iteration isread as a(i-1) one iteration later, and as a(i-2) two iterationslater. The Intel IA-64 compiler performs array data-dependence analysis using a series of dependence tests,and it determines accurate dependence direction anddistance information.

Page 8: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

An Overview of the Intel IA-64 Compiler 5

Function calls can inhibit optimization. Figure 7 shows anexample where a function call may inhibit dead storeelimination. If the function foo() reads *p, then the firststore to *p is not dead. Interprocedural mod/refinformation [10] is used to determine the set of memorylocations written/read as a result of a function call.

*p = ..

foo();

*p = ..

Figure 7: Disambiguation of a memory reference and afunction call

MEMORY OPTIMIZATIONSProcessor speed has been increasing much faster thanmemory speed over the past several generations ofprocessor families. This phenomenon is true for theIA-64 processor family as well. Indeed, the speeddifferential is expected to be even larger for the IA-64processors, since IA-64 is a high-performancearchitecture. As a result, the compiler must be veryaggressive in memory optimizations in order to bridge thegap. The Intel IA-64 compiler applies loop-based andregion-based control and data transformations in order toi) improve data access behavior with memoryoptimizations, ii) expose coarse grain parallelism, iii)vectorize, and iv) expose higher instruction-levelparallelism. In the compiler, we implemented numerouswell known and new transformations, and moreimportantly, we combined and tuned thesetransformations in special ways so as to exploit the IA-64features for higher application performance.

In this section, we illustrate a chosen few memoryoptimization techniques in the compiler, and we explainhow these transformations help harness the power of theIA-64 processor implementations for higher applicationperformance. Memory optimization techniques in the IntelIA-64 compiler include, but are not limited to, i) cacheoptimizations, ii) elimination of loads and stores, and iii)data prefetching. All these transformations are supportedby exact data dependence and temporal and spatial datareuse analyses algorithms. The compiler also appliesseveral other well known optimization techniques such assecondary induction variable elimination, constantpropagation, copy propagation, and dead codeelimination.

Cache OptimizationsCaches are an important hardware means to bridge the gapbetween processor and memory access speeds. However,programs, as originally written, may not effectively utilizeavailable cache. Hence, we have implemented several looptransformations to improve the locality of data reference inapplications. With improved locality of data reference, themajority of data references will be to higher and fasterlevels of memory hierarchy, so that data references incurmuch smaller overheads. The linear loop transformations,loop fusion, loop distribution, and loop block-unroll-and-jam are some of the transformations implemented inthe compiler.

do i = 1, 1000 do j = 1, 1000 c(j) = c(j) + a(i, j) * b(j) enddoenddo

do j = 1, 1000 do i = 1, 1000 c(j) = c(j) + a(i, j) * b(j) enddoenddo

Figure 8: An example of a linear loop transformation

Linear Loop TransformationsLinear loop transformations are compoundtransformations representing sequences of loop reversal,loop interchange, loop skew, and loop scaling [17,18].Loop reversal reverses the execution order of loopiterations, whereas loop interchange interchanges theorder of loop levels in a nested loop. Loop skew modifiesthe shape of the loop iteration space by a compiler-determined skew factor. Loop scaling modifies a loop tohave non-unit strides. As a combined effect, linear looptransformations can dramatically improve memory accesslocality. They can also improve the effectiveness of otheroptimizations, such as scalar replacement, invariant codemotion, and software pipelining. For example, the loopinterchange in Figure 8 makes references to arrays b and cboth inner loop invariants, besides improving the accessbehavior of array a.

Loop FusionLoop fusion combines adjacent conforming nested loopsinto a single nested loop [16]. Loop fusion is effective inimproving cache performance, since it combines the cachecontext of multiple loops into a single new loop. Thus,data reuse across nested loops is within the same newnested loop. It also increases opportunities for reducingthe overhead of array references by replacing them withreferences to compiler-generated scalar variables. Loopfusion also improves the effectiveness of data prefetching.Loop fusion in the Intel IA-64 compiler is more aggressivethan that in compilers for IA-32 or RISC processors, for

Page 9: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

An Overview of the Intel IA-64 Compiler 6

example, since loop fusion in the IA-64 takes advantage ofa large number of available registers. In the loop on theright-hand side of Figure 9, cache locality is improvedbecause the accesses to array a are reused within the sameloop. Further, it enables the compiler to replace referencesto arrays a and d with references to compiler-generatedscalar variables.

do i = 1, 1000 a(i) = xenddodo i = 1, 1000 c(i) = a(i-1) + d(i-1) d(i) = c(i)enddo

do i = 1, 1000 a(i) = x c(i) = a(i-1) + d(i-1) d(i) = c(i)enddo

Figure 9: An example of a loop fusion

Loop Block-Unroll-Jam

Loop unroll and jam unrolls the outer loops and fuses theunrolled copies together [14]. As a result, several outerloop iterations are merged into a single iteration in the newloop nest. For example, the i loop in the two-dimensionalloop on the left-hand side of Figure 10 is unrolled by afactor of two. The two resulting loop nests (one for theeven values of i and one for the odd values of i) arejammed together to obtain the loop on the right-hand sideof Figure 10.

do i = 1, 2*n do j = 1, 2*n b(j, i) = a(j, i-1) + a(j, i) + a(j, i+1)

enddoenddo

do i = 1, 2*n, 2 do j = 1, 2*n b(j, i) = a(j, i-1) + a(j, i) + a(j, i+1) b(j, i+1) = a(j, i) + a(j, i+1) + a(j, i+2) enddoenddo

Figure 10: An example of a loop unroll and jam

When all loops in a loop nest are blocked, loop blockingor tiling transforms an n-dimensional loop nest into a 2n-dimensional loop nest, where the inner n-loops togetherscan the iterations in a block or tile of the original iterationspace. Loop blocking is key to improving the cacheperformance of libraries and applications that manipulatelarge matrices of data items.

The design of the Intel IA-64 compiler unifies loopblocking, unroll and jam, and inner loop unrolling.

Traditionally, compilers implement loop blocking, loopunroll and jam, and (inner) loop unrolling separately. Inthe process, such compilers use more than one cost modeland multiple code-generation mechanisms. Whereas infact, the three transformations are closely related. Loopblocking is a unification of strip-mining and interchangetransformations. Outer loop unrolling and jamming can beviewed as blocking of the outer loops with block sizesequal to corresponding unroll factors, followed byunrolling the local iteration spaces corresponding to ablock or a tile. Inner loop unrolling is a special case ofblocking, where only the innermost loop is strip-mined andunrolled. All of the three transformations focus onbringing as many “related” array accesses and associatedcomputations as possible into inner loops. In the processof doing so the outer loop unroll and jam and the innerloop unroll increase the size of the loop body.

Loop DistributionThe effect of loop distribution on loop structure is theopposite of loop fusion [16]. Loop distribution splits asingle nested loop into multiple adjacent nested loops thathave a similar loop structure. The computation and arrayaccesses in the original loop are distributed across newlyformed nested loops. Besides enabling othertransformations, loop distribution spreads the potentiallylarge cache context of the original loop into different newloops, so that the new loops have manageable cachecontexts and higher cache hit rates.

LOAD AND STORE ELIMINATIONThe IA-64 architecture has a much larger register file thantraditional architectures. The IA-64 compiler takesadvantage of this to eliminate loads and stores byeffectively registering the memory references. In thissection, we describe two optimization techniques thateliminate loads and stores: scalar replacement andregister blocking.

Scalar ReplacementScalar replacement [14,15] is a technique to replacememory references with compiler-generated temporaryscalar variables, which are eventually mapped to registers.Most back-end optimization techniques map arrayreferences to registers when there is no loop-carried datadependence. However, the back-end optimizations do nothave accurate dependence information to replace memoryreferences with loop-carried dependence by scalarvariables. Scalar replacement, as implemented in the IntelIA-64 compiler, also replaces loop invariant memoryreferences with scalar variables defined at the appropriatelevels of loop nesting.

Page 10: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

An Overview of the Intel IA-64 Compiler 7

For an example of scalar replacement of memoryreferences, consider the loop on the left-hand side ofFigure 11. In the transformed loop, all the read referencesto array a are replaced by compiler-inserted temporaryscalar variables. In particular, note the replacement ofloop-carried data reuse of a(i-1), which is replaced by ascalar variable saved from a previous iteration. In otherwords, the technique is capable of scalar replacing forloop independent as well as for loop-carried (either by aninput or flow dependence) data reuses.

do i=2,n a(i)=a(i-1)* ... … =a(i)-a(i-1)enddo

t1 = a(1)do i=2,n t2 = t1 * … a(i) = t2 = t2 – t1 t1 = t2

enddo

Figure 11: An example of a scalar variable replacement

The IA-64 architecture provides rotating registers , whichare rotated one register position each time a special loopbranch instruction is executed. This hardware featureenables the compiler to map the compiler-inserted scalarsdirectly onto the rotating registers. In particular,assignment statements of the form t1=t2 in the exampleabove do not have any computational overhead at allbecause the assignment is implicitly affected by therotation of registers.

Scalar replacement of memory references uses thedirection vectors and dependence types in the datadependence graph to determine the memory referencesthat should be replaced by scalars and to determine howto perform the book-keeping required for the replacement.The compiler examines the data dependence graph foreach loop and partitions the memory references based onwhether the corresponding data dependencies are input,flow, or output dependencies. Memory references withineach group are sorted by dependence distance andtopological order. Memory references with loop-independent and loop-carried flow dependence areprocessed first, followed by memory references with loop-carried output dependence.

Register BlockingRegister blocking turns loop-carried data reuse into loop-independent data reuse. Register blocking transforms aloop into a new loop where the loop body containsiterations from several adjacent original loop iterations.Register blocking is similar to loop blocking or tiling, withrelatively smaller tile sizes, followed by an unrolling of theiterations in the tile. Register blocking is demonstrated inthe example in Figure 12. Register blocking takes

advantage of the large register file to map the references tomany of the common array elements in adjacent loopiterations onto registers.

do j=1,2*m do j=1,2*m,2 do i=1,2*n do i=1,2*n,2 a(i,j) = a(i-1,j) + a(i-1,j-1) a(i,j) = a(i-1,j)+a(i-1,j-1) enddo a(i+1,j) = a(i,j)+a(i,j-1) enddo a(i,j+1) = a(i-1,j+1)+a(i-1,j)enddo a(i+1,j+1)= a(i,j+1)+a(i,j) enddo enddo

Figure 12: An example of register blocking

The original loop on the left-hand side of this figure hastwo distinct array read references in every iteration. Theregister blocked loop on the right-hand side of the figurehas only six distinct array read references for every fouriterations in the original loop. Note that two of the sixreferences are loop independent reuses. In the IntelIA-64 compiler design, register blocking is followed byscalar replacement of memory references, since registerblocking exposes new opportunities for scalar replacementof memory references.

DATA PREFETCHINGData prefetching is an effective technique to hide memoryaccess latency. It works by overlapping time to access amemory location with time to compute as well as time toaccess other memory locations [7, 19, 20]. Dataprefetching inserts prefetch instructions for selected datareferences at carefully chosen points in the program, sothat referenced data items are moved as close to theprocessor as possible before the data items are actuallyused. Note that the data prefetch instructions do notnormally block the instruction stream and do not raiseexceptions. Prefetching is complementary to techniquesthat optimize memory accesses such as looptransformations, scalar replacement of memory references,and other locality optimizations. The data prefetchingalgorithm implemented in the Intel IA-64 compiler makesuse of data prefetch instructions and other dataprefetching support features available on the IA-64.

The cost incurred while prefetching data arises from theadded overhead of executing prefetch instructions as wellas instructions that generate the addresses for prefetcheddata items. The prefetch instructions will occupy memoryslots, thereby increasing resource usage. Compute-intensive applications normally have sufficient freememory slots. However, the benefits from prefetchinghave to be weighed against the increase in resource usagein memory-intensive applications. One must avoidprefetching for data already in the cache, because suchprefetches result in an overhead and are of no benefit.Data prefetches should be issued at the right time: they

Page 11: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

An Overview of the Intel IA-64 Compiler 8

should be sufficiently early so that the prefetched dataitem is available in cache before its use; they should besufficiently late so that the prefetched data item is notevicted from the cache before its use. Prefetch distancedenotes how far ahead a prefetch is issued for an arrayreference. This distance is estimated based on thememory latency, the resource requirements in the loop,and data-dependence information.

We implemented a data prefetching technique that utilizesdata-locality analysis to selectively prefetch only thosedata references that are likely to suffer cache misses. Forexample, if a data reference within a loop exhibits spatiallocality by accessing locations that fall within the samecache line, then only the first access to the cache line willincur a miss. Thus this reference can be selectivelyprefetched under a conditional of the form (i mod L) == 0,where i is the loop index and L denotes the cache line size.When multiple references access the same cache line, thenonly the leading reference needs to be prefetched.Similarly, if a data reference exhibits temporal locality,then only the first access must be prefetched.

do j = 1,n do i = 1,m a(i,j) = a(i,j) + b(0,i) + b(0,i+1)

enddoenddo

do j = 1,n do i = 1,m a(i,j) = a(i,j) + b(0,i) + b(0,i+1) if (mod(i,8) == 0) call prefetch(a(i+k, j)) if (j == 1) call prefetch(b(0, i+k+1))

enddoenddo

Figure 13: An example of data prefetching

In the example in Figure 13, the compiler inserts prefetchesfor arrays a and b. The references to array a have spatiallocality, whereas the references to array b have temporallocality with respect to the j loop iterations. Note that thecalls to the prefetch intrinsic function finally map to theprefetch instructions in IA-64. In this example, k is theprefetch distance computed by the compiler.

The conditional statements used to control the dataprefetching policy can be removed by loop unrolling,strip-mining, and peeling. However, this may result incode expansion, which can cause increased instructioncache misses. The predication support in IA-64 providesan efficient way of adding prefetch instructions. Theconditionals within the loop are converted to predicatesthrough if-conversion, thus changing control dependencyinto data dependency. The large number of registersavailable in IA-64 enables prefetch addresses to be stored

in registers obviating the need for register spill and fillwithin loops.

The IA-64 architecture provides support for memoryaccess hints that enable the compiler to orchestrate datamovement between memory hierarchies efficiently [7].Data can be prefetched into different levels of cachedepending on the access patterns. For example, if a datareference does not exhibit any kind of reuse, then it can beprefetched using a special nta hint to reduce cachepollution. This kind of architectural support for datamovement enables the compiler to perform better datareuse analysis across loop bodies so that unnecessaryprefetches are avoided.

PARALLELIZATION ANDVECTORIZATIONSupport for OpenMP*, automatic parallelization,vectorization, and load-pair optimization are all included inthe design of the IA-64 compiler. The design takesadvantage of native support for parallelism on the IA-64,which includes semaphore instructions such as exchange,compare-and-exchange, and fetch-and-add, in addition tothe fused multiply accumulate instruction (fma). Thesupport for parallelism on IA-64 also includes SIMD, i.e.,parallel arithmetic operations on 1, 2, and 4 bytes of data.In order to exploit the fine grain locality of data access inapplications, IA-64 provides load instructions thatsimultaneously load a pair of double floating-pointprecision data items.

ParallelizationOpenMP is an industry standard to specify sharedmemory parallelism. It consists of a set of compilerdirectives, library routines, and environment variables thatprovide a model for parallel programming aimed atportability across shared memory systems from differentvendors.

An alternative approach to parallelization is to let thecompiler automatically detect parallelism and generateparallel code. The Intel IA-64 compiler has accurate data-dependence information to determine loops that can beparallelized.

VectorizationThe IA-64 floating-point SIMD operations can furtherimprove the performance of floating-point applications.IA-64 provides the capability of doing multiple floating-point operations at the same time. The traditional loopvectorization techniques can be used to exploit thisfeature.

Page 12: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

An Overview of the Intel IA-64 Compiler 9

do j = 1, 1000 y(j) = y(j) + a*x(j)enddo

do j = 1, 1000, 2 t1,t2 = ldfpd(x(j),x(j+1)) t3,t4 = ldfpd(y(j),y(j+1)) y(j) = t3 + a*t1 y(j+1) = t4 + a*t2enddo

Figure 14: An example of the use of load-pairs

Load-PairsIA-64 provides high bandwidth instructions that load apair of floating-point numbers at a time [7]. Such load-pairinstructions take a single memory issue slot, thus possiblyreducing the initiation interval of the software pipelinedloop. Data alignment is required to make this work.Special instructions in IA-64 can be used to avoidpossible code expansion. For example, the loop in Figure14 has three memory operations per iteration. By usingload-pair operations, the number of memory references canbe reduced to two per iteration.

SCALAR OPTIMIZATIONSA primary objective of scalar optimizations is to minimizethe number of computations and the number of referencesto memory. Partial redundancy elimination (PRE) [1, 2, 11]is a well known scalar optimization technique thatsubsumes global common subexpression elimination (CSE)and loop invariant code motion. CSE removes expressionsthat are always redundant (redundant on all control flowpaths). PRE goes beyond CSE by attempting to removeredundancies that occur only on some control flow paths.In this paper, we highlight the use of scalar optimizationsto eliminate loads and stores.

Traditional PREAn expression at program point p in the program controlflow graph (CFG) is fully redundant if the same expressionis already available. An expression e is said to beavailable at a point p if along every control flow path fromthe program entry to p there is an instance of e that is notsubsequently killed by a redefinition of its operands.Figure 15 shows an example of a fully redundantexpression and its elimination by CSE. The redundancy isremoved by saving the value of the redundant expressionin a temporary variable and then later reusing that valueinstead of reevaluating the expression.

c = a + b d = a + b

e = a + b

t1 = a + b c = t1

t1 = a + b d = t1

e = t1

a) b)

Figure 15: (a) expression a + b is fully available, (b)elimination of common subexpression

An expression e is partially available at a point p if there isan instance of e along only some of the control flow pathsfrom the program entry to p. Figure 16 shows an exampleof a partially redundant expression and PRE. The partialredundancy is removed by inserting a copy of theredundant expression on the control flow paths where it isnot available, making it fully redundant.

c = a + b

e = a + b

t1 = a + b c = t1

t1 = a + b

e = t1

a) b)

Figure 16: (a) expression a + b is partially available (b)elimination of partial redundancy

PRE can move the loop invariant to outside the loop asshown in Figure 17. The expression *q is available on theloop back-edge, but not on entry to the loop. Afterinserting t2 = *q in the loop preheader, *q is fullyavailable and can be removed from the loop.

t 2 = * q a ( i ) = a ( i ) + t 2 a ( i ) = a ( i ) + t 2

t 2 = * q

a ) b )

Figure 17: Example of loop-invariant code motion

Page 13: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

An Overview of the Intel IA-64 Compiler 10

Note, however, that the optimizer must be careful not toinsert a copy of an expression at a point that would causethe expression to be evaluated when it was not evaluatedin the original source code. Figure 18 shows such anexample. The insertion of an expression e at a programpoint p is said to be down-safe if along every control flowpath from p to the program exit there is an instance of esuch that the inserted expression is available at each laterinstance. In Figure 18, the insertion of t1 = *q is notdown-safe. There are two aspects to down-safety. Thefirst is that an unsafe insertion may create an incorrectprogram. For example, in Figure 18, the expression *q isexecuted before checking if q is a null pointer. Second, anunsafe insertion reduces the number of instructions alongone path at the expense of another path. In Figure 18, theredundancy is eliminated for the left-most path, but anextra instruction, t1 = *q, is executed on the right-mostpath.

a) b)

yn

c = *q (q == Null) ?

e = *q

d = a + b

yn

t1 = *q(q == Null) ?

e = t1

d = a + b

t1 = *q c = t1

Figure 18: (a) expression *q is partially available, (b)violation of down-safety

Extended PRE for IA-64The standard PRE algorithm removes all the redundanciespossible with safe insertions. We have extended PRE touse control speculation to remove redundancies on onecontrol flow path, perhaps at the expense of another, lessimportant control flow path. In the example in Figure 18,assume that the left-most control flow path is executedmuch more frequently than the right-most path. If theredundancy on the left-most path could be removedwithout producing an incorrect program, overallperformance would be improved even though an extrainstruction is executed on the right-most path.

a) b)

yn

c = *q (q == Null) ?

e = *q

d = a + b

yn

t2 = qld t1 = [t2] c = t1

t3 = qld.s t1 = [t3](q == Null) ?

chk.s t1 e = t1

d = a + b

recoveryblock

Figure 19: Redundancy elimination using controlspeculation

Figure 19 shows how the redundancy in Figure 18 couldbe removed using the IA-64 support for controlspeculation. The insertion of *q is done using aspeculative load, and a check instruction is added in placeof the redundant load. Executing the check is preferable toexecuting the redundant load because the check does notuse memory system resources and because the latency ofthe load is hidden by executing it earlier. Also, eliminationof the redundant load may expose further opportunities forredundancy elimination in the instructions that depend onthe load.

Removal of redundant loads can sometimes be inhibitedby intervening stores. In Figure 20 (a), the loop-invariantload *p cannot be removed unless the compiler can provethat the store *q does not access the same memorylocation. The process of determining whether or not twomemory references access the same location is calledmemory disambiguation and was described earlier in thispaper.

If the compiler can determine that there is an unknown, butsmall probability that *p and *q access the same memorylocation, the loop invariant load and the add that dependson it can be removed using the IA-64 support for dataspeculation as shown in Figure 20 (b). The insertion of *pin the preheader is done using an advanced load, and acheck instruction is added in place of the originalredundant load. If the store *q accesses the same memorylocation as the load *p, a branch to a recovery code blockwill be taken at the check instruction. The recovery blockcontains code to reload *p and re-execute t4=t2 + t3. Ifthe store *q and load *p access different memorylocations, then only the check is executed instead of theredundant load and add.

Page 14: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

An Overview of the Intel IA-64 Compiler 11

*q = t1 t2 = *pt4 = t2 + t3

*q = t1chk.a t2

t5 = pld.a t2 = [t5]t4 = t2 + t3

a) b)

recoveryblock

Figure 20: Removal of loop-invariant load using dataspeculation

Partial Dead Store EliminationIn contrast to PRE which removes redundant loads, PartialDead Store Elimination (PDSE) removes redundant storesin the program. Figure 21 shows an example of PDSE. Thepartial redundancy is removed by inserting a copy of thepartially dead store into the control flow paths where it isnot dead, making it fully redundant.

*p = c

*p = c

a)

*p = t1

b )

*p = t1

t1 = c

Figure 21: (a) store *p is partially dead, (b) elimination ofpartially dead store

As with PRE, the compiler must be careful when insertingstores to avoid executing a store when it should not beexecuted. Figure 22 shows an example of an incorrectinsertion of a store. In Figure 22b, the store*p = t1 on the right is executed even if the path containingd=a+b is executed. In the original program in Figure 22a,no store to *p is executed when the path containingd=a+b is executed.

*p = c

*p = c

a)

*p = t1

b)

*p = t1

t1 = cd = a + b d = a + b

Figure 22: Example of incorrect insertion of a store

Figure 23 shows how the redundancy in Figure 22 couldbe removed using a predicated store. In Figure 23b, theredundancy on the left-most path is removed by insertinga predicated store. Instructions are required to set thepredicate p2 to 1 when the store should be executed, andto 0 when it should not be executed. In Figure 23b,suppose that the left-most path is executed much morefrequently than the right-most path. On the left-most path,executing p2=1 is preferable to executing the store,because the p2=1 does not use memory system resources.In some cases, an appropriate instruction to set p2 mayalready exist as a result of the if-conversion or anotheroptimization, thereby reducing the cost of predicating thestore.

*p = c

*p = c

a)

*p = t1

b)

(p2) *p = t1

t1 = cp2 = 1

d = a + bp2 = 0d = a + b

Figure 23: Elimination of partially dead store usingpredication

THE SCHEDULER AND CODEGENERATORThe scheduler and code generator in the compiler makeeffective use of predication, speculation, and rotatingregisters by global code scheduling, software pipelining,and rotating-register allocation. In this section, weprovide an overview of predication techniques, software-pipelining, global code scheduling, and register allocation.

PredicationBranches can decrease application performance byconsuming hardware resources at execution time and byrestricting instruction-level parallelism. Predication is oneof several features IA-64 provides to improve the

Page 15: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

An Overview of the Intel IA-64 Compiler 12

efficiency of branches [7]. Predication is the conditionalexecution of an instruction that is based on a qualifyingpredicate, where the qualifying predicate is a predicateregister whose value determines whether or not theinstruction must execute normally. If the predicate is true,the instruction updates the computation state; otherwise,it generally behaves like a nop. The execution of most IA-64 instructions is gated by a qualifying predicate. Thevalues of predicate registers can be set with a variety ofcompare and test bit instructions. Predicated executionavoids branches, and it simplifies compiler optimizationsby converting a control dependence to a datadependence.

The Intel IA-64 compiler eliminates branches throughpredication and thus improves the quality of the code’sschedule. The benefits are particularly pronounced forbranches that are hard to predict. The compiler uses atransformation called if-conversion, where conditionalexecution is replaced with predicated instructions. For asimple example, look at the following code sequence:

if (a <b) s = s + aelse s = s + b

can be rewritten in IA-64 without branches as

cmp.lt p1, p2 = a,b(p1) s = s + a(p2) s = s + b

Since instructions from opposite sides of the conditionalare predicated with complementary predicates, they areguaranteed not to conflict, and the compiler has morefreedom when scheduling to make the best use ofhardware resources.

Predication enables the compiler to perform upward anddownward code motion with the aim of reducing thedependence height. This is possible because predicatingan instruction replaces a control dependence with a datadependence. If the data dependence is less constrainingthan the control dependence, such a transformation mayimprove the instruction schedule. The compiler also usespredication to efficiently implement software pipeliningdiscussed in the next section.

Note that predication may increase the critical path lengthbecause of unbalanced dependence heights or over-usageof particular resources, such as those associated withmemory operations. The compiler has to weigh this costagainst the profitability of predication by considering

various factors such as the branch mispredictionprobabilities, miss cost, and parallelism.

The IA-64 supports special parallel compare instructionsthat allow compound expressions using the relationaloperators and and or to be computed in a single cycle.These instructions can be used to reduce the control pathby reducing the total number of branches. IA-64 also hasthe support of multiway branches, where differentpredicates can be used to branch to different targetswithin an instruction group.

Software PipeliningSoftware pipelining [3,4] in the Intel IA-64 compilerimproves the performance of a loop by overlapping theexecution of several iterations. This improves theutilization of available hardware resources by increasingthe instruction-level parallelism. Figure 24 shows severaloverlapped loop iterations.

iter 1

iter 2

iter 3

use r1

def r1

def r1

def r1

use r1

use r1

Stage A

Stage B

Stage C

Figure 24: Pipelined loop iterations

Analogous to hardware pipelining, each iteration isdivided into stages. In this example, each iteration isdivided into three stages, and up to three iterations areexecuted simultaneously. The number of cycles betweenthe start of successive iterations in a software-pipelinedloop is called the Initiation Interval (II), and each stage isII cycles in length.

Software-pipelined loops have three execution phases: theprolog phase, in which the software pipeline is filled; thesteady-state kernel phase, in which the pipeline is full; andthe epilog phase, in which the pipeline is drained. In RISCarchitectures, these three execution phases areimplemented using three distinct blocks of code as shownin Figure 25.

In IA-64, rotating predicates [5, 6, 7] are used to controlthe execution of the stages during the prolog and epilogphases, so that only the kernel loop is required. Thisreduces code size. During the first iteration of the kernel

Page 16: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

An Overview of the Intel IA-64 Compiler 13

loop, stage A of source iteration 1 is enabled. Duringkernel iteration 2, stage A of source iteration 2 and stage Bof source iteration 1 are enabled, and so on. During theepilog phase, the hardware support sequentially disablesstages.

In order to illustrate another advantage of IA-64 supportfor software-pipelining, consider register r1 defined earlyin each iteration and used late in each iteration, as shownin Figure 24. When the iterations overlap, the separatelifetimes also overlap. The definitions of r1 from iterationstwo and three overwrite the value of r1 that is needed bythe first iteration. In RISC architectures, the kernel loopmust be unrolled three times so that each of the threeoverlapped lifetimes can be assigned to different registersto avoid clobbering of values [4, 6]. In IA-64, on the otherhand, unrolling of the kernel loop is unnecessary becauserotating registers can be used to perform renaming of theregisters, thus reducing the code size [5, 6, 7].

The Intel IA-64 compiler uses a software pipeliningalgorithm called modulo scheduling [8]. In moduloscheduling, a minimum candidate II is computed prior toscheduling. This candidate II is the maximum of theresource-constrained minimum II and the recurrence-constrained (dependence cycle constrained) minimum II.

prolog

epilog

kernel loop

Figure 25: Execution phases in software-pipelined loops:IA-64 supports kernel only software-pipelined loops

The Intel compiler pipelines both counted loops and whileloops. Loops with control flow or with early exits aretransformed, using if-conversion, into single block loopssuitable for pipelining. Outer loops can also be pipelined,and several optimizations are done to reduce therecurrence-constrained II.

Global Code SchedulingThe Intel IA-64 compiler contains both a global codescheduler (GCS) [9] and a fast local code scheduler. TheGCS is the primary scheduler, and it schedules code overacyclic regions of control flow. The local code scheduler

rearranges code within a basic block and is run afterregister allocation to schedule the spill code.

The GCS allows arbitrary acyclic control flow within thescheduling scope referred to as a scheduling region.There is no restriction placed on the number of entries intoor exits from the scheduling region. The GCS also enablescode scheduling across inner loops by abstracting themaway through nesting. The GCS employs a new path-based data dependence representation that combinescontrol flow and data-dependence information to makedata analysis easy and accurate.

Most scheduling techniques find it difficult to make gooddecisions on the generation and scheduling ofcompensation code. This problem is addressed by theGCS using wavefront scheduling and deferredcompensation. The GCS schedules along all the paths in aregion simultaneously. The wavefront is a set of blocksthat represents a strongly independent cut set of theregion. Instructions are only scheduled into blocks on thewavefront. The wavefront can be thought of as theboundary between scheduled and yet to be scheduledcode in the scheduling region.

Control flow in program code can make the task of codemotion difficult and complicated. In the GCS, tailduplication is done at the instruction level and is referredto as P-ready code motion. An instruction is duplicatedbased on a cost and profitability analysis.

Register AllocationRegister allocation refers to the task of assigning theavailable registers to variables such that if two variableshave overlapping live ranges, they are assigned separateregisters. In doing so, the register allocator attempts tomaximally utilize all the available registers. The largenumber of architectural registers in IA-64 enables multiplecomputations to be performed without having tofrequently spill and copy intermediate data to memory.Register allocation can be formulated as a graph coloringproblem where nodes in the graph represent live ranges ofvariables and edges represent a temporal overlap of liveranges. Nodes sharing an edge must be assigned differentcolors or registers.

When using predication, it is particularly common forsequences of instructions to be predicated withcomplementary predicates. In such cases, it is possible touse the same registers for two separate variables, evenwhen their live ranges seem to overlap. This is becausethe compiler can figure out that only one of the variableswill be updated depending on the predicate values. Forexample, in the code sequence of Figure 26, the same

Page 17: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

An Overview of the Intel IA-64 Compiler 14

register is allocated for both v1 and v2 since p1 and p2 arecomplementary predicates.

(p1) v1 = 10(p2) v2 = 20 ;;(p1) st4 [v10]= v1(p2) v11 = v2 + 1 ;;

----à

(p1) r32 = 10(p2) r32 = 20 ;;(p1) st4 [r33]= r32(p2) r34 = r32 + 1 ;;

Figure 26: An example of register allocation

CONCLUSIONIn this paper, we provided an overview of the Intel IA-64compiler. We described the organization of the compiler,as well as the features and functionality of severaloptimization techniques. The compiler applies region andloop-level control and data transformations, as well asglobal optimizations, to programs. All the optimizationtechniques in the compiler are aware of profile informationand effectively use interprocedural analysis information.The optimizations effectively target three main goals whilecompiling an application: i) to minimize the overhead ofmemory accesses, ii) to minimize the overhead ofbranches, and iii) to maximize instruction-level parallelism.We described how the optimization techniques in the IntelIA-64 compiler take advantage of the IA-64 architecturalfeatures for improved application performance. Weillustrated the techniques with example codes, and wehighlighted the benefits as a result of specificoptimizations. The Intel IA-64 compiler incorporates allthe infrastructure and technology necessary to leveragethe IA-64 architecture for improved integer and floating-point performance.

ACKNOWLEDGMENTSWe thank the members of the IA-64 compiler team for theircontributions to the compiler technology described in thispaper. We also thank the reviewers for their excellent anduseful suggestions for improvement.

REFERENCES[1] E. Morel and C. Renvoise, “Global optimization by

suppression of partial redundancies,” Comm. ACM,22(2), February 1979, pp. 96-103.

[2] F. Chow, S. Chan, R. Kennedy, S. Liu, R. Lo, and P. Tu,“A new algorithm for partial redundancy elimination

based on SSA form,” in Proceedings of the ACMSIGPLAN '97 Conference on Programming LanguageDesign and Implementation, June 1997, pp. 273-286.

[3] B. R. Rau and C. D. Glaeser, “Some SchedulingTechniques and an Easily Schedulable HorizontalArchitecture for High-Performance ScientificComputing,” in Proceedings of the 20th AnnualWorkshop on Microprogramming andMicroarchitecture, October 1981, pp. 183-198.

[4] M. S. Lam, “Software Pipelining: An EffectiveScheduling Technique for {VLIW} Machines,” inProceedings of the ACM SIGPLAN 1988 Conferenceon Programming Language Design andImplementation, June 1988, pp. 318-328.

[5] J. C. Dehnert, P. Y. Hsu, and J. P. Bratt, “OverlappedLoop Support in the Cydra 5,” in Proceedings of theThird International Conference on ArchitecturalSupport for Programming Languages and OperatingSystems, April 1989, pp. 26-38.

[6] B. R. Rau, M. S. Schlansker, and P. P. Tirumalai, “CodeGeneration Schema for Modulo-Scheduled Loops,” inProceedings of the 25th Annual InternationalSymposium on Microarchitecture, December 1992, pp.158-169.

[7] IA-64 Application Developer’s Architecture Guide,Order Number 245188-001, May 1999.

[8] B. R. Rau, “Iterative Modulo Scheduling: An Algorithmfor Software Pipelining Loops,” in Proceedings of the27th International Symposium on Microarchitecture,December 1994, pp. 63-74.

[9] J. Bharadwaj, K.N. Menezes, and C. McKinsey,“Wavefront Scheduling: Path-Based DataRepresentation and Scheduling of Subgraphs,” toappear in Proceedings of the 32nd AnnualInternational Symposium on Microarchitecture(MICRO32), (Haifa, Israel), December 1999.

[10] K. D. Cooper and K. Kennedy, “Interprocedural Side-Effect Analysis in Linear Time,” in Proceedings of theACM SIGPLAN '88 Conference on ProgrammingLanguage Design and Implementation, June 1988, pp.57-66.

[11] J. Knoop, O. Ruthing, and B. Steffen, “Lazy codemotion” in Proceedings of the ACM SIGPLAN '92Conference on Programming Language Design andImplementation, pp. 224-234, June 1992.

[12] B. Steensgaard, “Points-to Analysis in Almost LinearTime” in Proceedings of the Twenty-Third AnnualACM SIGPLAN-SIGACT Symposium on Principles ofProgramming Languages, pp. 32-41, January 1996.

Page 18: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

An Overview of the Intel IA-64 Compiler 15

[13] C. Dulong, “The IA-64 Architecture at Work,” IEEEComputer, July 1998.

[14] S. Carr, “Memory-Hierarchy Management” Ph.D.Thesis, Rice University, July 1994.

[15] S. Carr and K. Kennedy, “Scalar Replacement in thePresence of Conditional Control Flow,” TechnicalReport CRPC-TR92283, Rice University, November1992.

[16] S. Muchnik, Advanced Compiler DesignImplementation, Morgan Kaufman, 1997.

[17] M. Wolf and M. Lam, A Loop Transformation Theoryand an Algorithm to Maximize Parallelism, ParallelDistributed Systems, Volume 2 (4), pp. 452-471,October, 1991.

[18] W. Li and K. Pingali, “A Singular LoopTransformation Framework Based on Non-SingularMatrices ,” International Journal of ParallelProgramming, Volume 22 (2), 1994.

[19] T. Mowry, “Tolerating Latency Through Software-Controlled Data Prefetching,” Ph.D. Thesis, StanfordUniversity, March 1994, Technical Report CSL-TR-94-626.

[20] V. Santhanam, E. Gornish, and W. Hsu, “DataPrefetching on the HP PA-8000,” in Proceedings of the24th Annual International Symposium on ComputerArchitecture, June 1997, pp. 264-273.

AUTHORS’ BIOGRAPHIESCarole Dulong has been with Intel for over nine years.She is the co-manager of the IA-64 compiler group. Priorto joining Intel’s Microcomputer Software Laboratory, shewas with the IA-64 architecture group, where she headedthe IA-64 multimedia architecture definition and the IA-64experimental compiler development. Her e-mail [email protected].

Rakesh Krishnaiyer received a B.Tech. degree incomputer science and engineering from the IndianInstitute of Technology, Madras in 1993, and an M.S. andPh.D. degree from Syracuse University in 1995 and 1998,respectively. He currently works on high-leveloptimizations for the IA-64 compiler at Intel. His researchinterests include compiler optimizations, high-performanceparallel and distributed computing systems, and computerarchitecture. His e-mail address [email protected].

Dattatraya Kulkarni received his Ph.D. degree incomputer science from the University of Toronto,Toronto, Canada in 1997. He has been working oncompiler optimization techniques for uniprocessor and

multiprocessor systems for the past nine years. He iscurrently with the Intel IA-64 compiler team. His e-mail [email protected].

Daniel Lavery received a Ph.D. degree in electricalengineering from the University of Illinois in 1997. As amember of the IMPACT research group under ProfessorWen-mei Hwu, he developed new software pipeliningtechniques. Since joining Intel in 1997, he has worked onthe architecture and compilers for IA-64. He is currentlyan IA-64 compiler developer in Intel's MicrocomputerSoftware Laboratory. His e-mail address [email protected].

Wei Li leads and manages the high-level optimizer groupfor IA-64. He has published many research papers in theareas of compiler optimizations, parallel and distributedcomputing, and scalable data mining. He served on theprogram committees for parallel and distributed computingconferences. He received his Ph.D. degree in computerscience from Cornell University, and he was on the facultyat the University of Rochester before joining Intel. His e-mail address is [email protected].

John Ng received an M.S. degree in computer sciencefrom Rutgers University in 1975 and a B.Sc. degree inmathematics from Illinois State University in 1973. Hejoined the Intel IA-64 compiler team three years ago. Priorto that he was a Senior Programmer at IBM Corporation.He has been working on compiler optimizations,vectorization, and parallelization since 1982. His e-mail [email protected].

David Sehr received his B.S. degree in physics andmathematics from Butler University in 1985. He receivedhis M.S. and Ph.D. degrees from the University of Illinoisworking under the direction of David Padua and LaxmikantKale. His thesis work was in the area of restructuringcompilation of logic languages. He joined Intel in 1992and since that time has worked on loop optimizations,scalar optimizations, and interprocedural and profile-guided optimizations for IA-32 and IA-64. He is currentlythe group leader for the IA-64 compiler scalar optimizer.His e-mail address is [email protected].

Page 19: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

SoftSDV: A Presilicon Software Development Environment for the IA-64 Architecture 1

SoftSDV: A Presilicon Software DevelopmentEnvironment for the IA-64 Architecture

Richard Uhlig, Microprocessor Research Labs, Oregon, Intel Corp.Roman Fishtein, MicroComputer Products Lab, Haifa, Intel Corp.Oren Gershon, MicroComputer Products Lab, Haifa, Intel Corp.

Israel Hirsh, MicroComputer Products Lab, Haifa, Intel Corp.Hong Wang, Microprocessor Research Labs, Santa Clara, Intel Corp.

Index words: presilicon software development, dynamic binary translation, dynamic resource analysis,processor performance simulation, IO-device simulation

AbstractNew instruction-set architectures (ISAs) live or diedepending on how quickly they develop a large softwarebase. This paper describes SoftSDV, a presilicon soft-ware-development environment that has enabled at leasteight commercial operating systems and numerous largeapplications to be ported and tuned to IA-64, well inadvance of Itanium™ processor’s first silicon. IA-64versions of Microsoft Windows ∗ 2000 and Trillian Linux*that were developed on SoftSDV booted within ten daysof the availability of the Itanium processor.

SoftSDV incorporates several simulation innovations,including dynamic binary translation for fast IA-64 ISAemulation, dynamic resource analysis for rapid softwareperformance tuning, and IO-device proxying to link asimulation to actual hardware IO devices for operatingsystem (OS) and device-driver development. We describehow SoftSDV integrates these technologies into acomplete system that supports the diverse requirements ofsoftware developers ranging from OS, firmware, andapplication vendors to compiler writers. We detailSoftSDV’s methods and comment on its speed, accuracy,and completeness. We also describe aspects of theSoftSDV design that enhance its flexibility and maintain-ability as a large body of software.

∗ Other brands and names are the property of theirrespective owners.

INTRODUCTIONThe traditional approach to fostering software develop-ment for a new ISA such as IA-64 is to supplyprogrammers with a hardware platform that implements thenew ISA. Such a platform is commonly known as asoftware-development vehicle (SDV) and suffers from akey dependency: it cannot be assembled until first siliconof the processor has been manufactured. This paperdescribes how Intel eliminated this dependency for IA-64by building an SDV entirely in software through thesimulation of all processor and IO-device resourcespresent in an actual hardware IA-64 SDV. This simulationenvironment, which we call SoftSDV, has enabledsubstantial development of IA-64 software, well inadvance of an Itanium processor’s first silicon.

A principal design goal for SoftSDV is that it supportdevelopment all along the software stack, from firmwareand device drivers to operating systems and applications(see Figure 1). The performance of each of these layers ofsoftware is dependent upon optimizing compilers, whichthemselves must be carefully tuned to IA-64 [1, 2]. Eachof these types of software development has a different setof requirements with respect to simulation speed, accu-racy, and completeness.

Application developers, for example, are primarilyinterested in simulation speed, whereas optimizing-compiler writers value accuracy in processor-resourcemodeling so that they can evaluate the effectiveness oftheir code-generation algorithms. OS, firmware, anddevice-driver developers, on the other hand, requirecompleteness in the modeling of platform IO devices and

Page 20: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

SoftSDV: A Presilicon Software Development Environment for the IA-64 Architecture 2

system-level processor functions (e.g., virtual-memorymanagement, interrupt processing, etc.).

Not only do requirements vary based on the type of code,but their relative importance shifts depending on the stageof software development. Early efforts to port and debugcode to IA-64 are best aided by very fast ISA emulation,whereas successive tuning of code for performancerequires ever-increasing levels of simulation accuracy.Similarly, in the early stages of porting an OS to IA-64, it issufficient to model a basic set of boot IO devices.However, once that OS is up and running, the meaning ofsimulation “completeness” expands to include arbitrarynew IO devices as OS developers seek to port as manydevice drivers to IA-64 as possible.

Often overlooked, but equally important features of asimulation infrastructure are its flexibility and maintain-ability. SoftSDV has been under development for nearlyas long as IA-64 and has had to track and rapidly adapt toimprovements in the ISA definition; flexibility andmaintainability of the simulation infrastructure wereabsolutely essential. But here too, the requirementschange over time. Early in the development of a new ISA,design changes are frequent, and a simulation environ-ment must adapt quickly. Later, as the hardware definitionbecomes more concrete, flexibility gradually becomes less

important, and it can be traded for increased simulationspeed or accuracy.

These diverse and shifting requirements underscore afundamental truth of simulation: a single technique or toolcannot meet the needs of all possible types of softwaredevelopment at all times. SoftSDV acknowledges this factthrough an extensible design that accommodates the bestfeatures of multiple innovative simulation technologies ina single, common infrastructure. The resulting systemenables IA-64 software developers to select the combina-tion of simulation speed, accuracy, and completeness thatis most appropriate to their particular needs at a giventime, while at the same time preserving the flexibility andmaintainability of the overall simulation infrastructure.

In the next section, we review related work and then brieflyoverview the hardware components typically found in anIA-64 SDV. We then present the software architecture ofSoftSDV and describe each of its component processormodules and IO-device modules in detail. We concludewith a discussion of results and a summary of ourexperiences and lessons learnt.

Figure 1: The SoftSDV software architecture

IA-32 Host Platform

IA-32 Host Operating System

Host PCI Bus

IODeviceModules

PCI ProxyModule

Graphics Storage

Comm. Pseudo

SofSDV is astandardprocess onthe Host OS

SC

SI

Eth

erne

t

US

B C

ontro

ller

Source Code

CompilerFor IA-64

CrossCompilation /Development

ServiceModules

Disk Image

Display

Tracing

Debug

Kernel AbstractionsEventsSpacesData Items

Increasing Accuracy

Increasing Speed

Processor Modules

ISA Emulator

Resource Analyzer

Microarch Simulator

IA-6

4 S

oftw

are

Sta

ck text

IA-64 Firmware IA-64 Device Drivers

IA-64 Operating System

Application ApplicationApplication ... ISV Developers

IHV Developers

OSV Developers

Simulated IA-64Environment

IA-64Code

SoftSDVKernel

Page 21: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

SoftSDV: A Presilicon Software Development Environment for the IA-64 Architecture 3

RELATED WORKA survey of recent simulation research reveals a continu-ous tension between the conflicting goals of simulationspeed, accuracy, completeness, and flexibility.

A good example of a technique that achieves very highsimulation speeds at the expense of accuracy is dynamicbinary translation. This method for fast ISA emulationworks by translating instructions from a target ISA intoequivalent sequences of host ISA instructions. Bycaching and reusing the translated code, an emulator candramatically reduce the fetch-and-decode overhead oftraditional instruction interpreters. Cmelik and Keppeldescribe an early implementation of this method in theirShade∗ simulator, and they provide an excellent survey ofseveral related techniques [3]. The Embra simulator hasshown that dynamic binary translation can be extended tosupport the simulated execution of a full OS by emulatingprivileged instructions, virtual-to-physical addresstranslation, and exception and interrupt processing [4].

Fast ISA emulators such as Shade and Embra are unableto predict processor performance at a detailed clock-cyclelevel. When simulation accuracy and flexibility are ofprimary importance, a better approach is often that of asimulation tool set, such as SimpleScalar, which enablesrapid construction and detailed simulation of a range ofcomplex microarchitectures [5]. SimpleScalar has indeedexhibited a high degree of flexibility as evidenced by itsextensive use in the research community for a variety ofmicroarchitecture studies. But this flexibility and accuracycomes at a cost: detailed SimpleScalar simulations can bemore than 1,000 times slower than native hardware,whereas fast ISA emulators like Shade and Embra exhibitslowdowns ranging from 10 to 40, depending on the typeof workload that they simulate. These numbers illustratethe compromises that simulators must make betweenspeed, accuracy and flexibility, with Shade and Embrarepresenting one end of the spectrum and SimpleScalarsituated near the other end.

Many other intermediate points along the speed-accuracy-flexibility spectrum are possible. FastCache, for example,extends dynamic binary translation techniques to simulatethe performance of simple data-cache structures withslowdowns in the range of 2-7, but it is limited with respectto other forms of microarchitecture simulation [6]. At theexpense of some flexibility, FastSim uses memoization(result caching) to model a full out-of-order processormicroarchitecture with slowdowns ranging from 190 to 360,a speedup of roughly 8-15 relative to comparable SimpleS- ∗ Other brands and names are the property of theirrespective owners.

calar simulations [7]. Another way to trade speed foraccuracy is sampling: running only certain portions of atarget workload through a detailed performance simulator.If the samples are chosen carefully and are of sufficientlength, they can predict the performance of the entireworkload with far less simulation time, but at the expenseof some increased error [8].

SimOS [9] and SimICS∗ [10] are both good examples ofsimulation systems that have attained a high level ofcompleteness. Both extend their simulations beyond theprocessor to include IO-device models, and they are ableto support the simulated execution of complete operatingsystems as a result.

SoftSDV uses many techniques similar to those describedabove. However, because of the unique capabilities of IA-64 and the diversity of software development thatSoftSDV must support, we found we had to reexaminemany of the techniques in a new context. To explain someof the issues, we briefly overview the components in atypical IA-64 SDV in the next section.

COMPONENTS OF AN IA-64 SDVAt the core of an actual hardware IA-64 SDV platform isone or more Itanium processors that implement the IA-64ISA. For the purposes of this paper, the most relevantaspects of IA-64 are the following:1

• Memory Management: An IA-64 processor translates64-bit virtual memory accesses through split instruc-tion and data TLBs, which are refilled by softwaremiss handlers with a hardware assist. The TLB en-forces page-level read, write, and execute permissionsfor up to four privilege levels.

• Predication: Most IA-64 instructions can be executedconditionally, depending on the value of an optionalpredicate register associated with the instruction.

• Data Speculation: IA-64 supports an advanced-loadoperation, which enables a compiler to move loadsahead of other memory operations even when thecompiler is unable to disambiguate neighboring mem-ory references. The address from an advanced load isinserted into an Advanced Load Address Table(ALAT), which must be checked for data dependen-cies on subsequent memory operations. A load-check operation examines the ALAT and invokes fix-up code whenever necessary.

1 More details regarding IA-64 are available from the IntelDeveloper’s Web Site [2].

Page 22: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

SoftSDV: A Presilicon Software Development Environment for the IA-64 Architecture 4

• Control Speculation: An IA-64 compiler can moveloads before branches that guard their execution. If aspeculative load causes an exception, the exception isdeferred, and a Not-a-Thing (NaT) bit associated withthe destination register is set instead. NaT bits arepropagated as a side effect of any uses of the specu-lative load value, and they are checked after thecontrolling branch finally executes.

• Large Register Set: IA-64 includes 128 generalregisters and 128 floating-point registers, along withnumerous other special-purpose registers. IA-64 reg-isters assist loop-pipelining scheduling through theirsupport of rotating registers.

Predication, speculation, and abundant registers are allpart of a key principle behind the design of IA-64: toenable a compiler to explicitly expose instruction-levelparallelism (ILP) to the hardware microarchitecture[1, 2]. By helping to relieve the hardware of finding ILP,IA-64 makes possible higher-frequency processorimplementations. The compiler expresses instructionparallelism to the hardware by collecting instructions intoinstruction groups, which are independent instructionsthat can be executed at the same time. As we will see, theabove IA-64 features create new challenges and opportu-nities for processor-simulation techniques.

In addition to an Itanium processor, an IA-64 SDVtypically contains a chipset (e.g., 460GX), with support forPCI and USB IO-device busses, and a basic collection ofIO devices suitable for running an operating system. Thisincludes an IO streamlined APIC interrupt controller andan assortment of keyboard, mouse, storage, network, andgraphics devices. Since a hardware SDV typicallycontains a number of expansion PCI slots, and a USB hostcontroller, the range of devices it supports is limited onlyby the availability of devices designed to these busstandards. Our goal with SoftSDV was to provide thesame level of IO-device support.

SOFTWARE ARCHITECTUREThe software architecture of SoftSDV is based on asimulation kernel that is extended through the addition ofmodules (see Figure 1 and Table 1). A SoftSDV moduleeither models a hardware platform component (such as aprocessor or IO device), or it implements a simulationservice (such as a trace-analysis tool). We call these twotypes of modules component modules and servicemodules, respectively.

SoftSDV modules share data and communicate with oneanother through a set of abstractions provided by thekernel: spaces, events, data items and time.

• SoftSDV spaces are used to model how platformcomponents communicate through physical-addressspace, IO-port space, PCI-configuration space, andother linearly addressable entities such as disk imagesand graphics framebuffers. Modules can createspaces and then register access-handler functionswith the kernel for a certain address region in a space.When another module reads or writes an address inthat range, the SoftSDV kernel routes the access tothe registered handler. SoftSDV spaces enable an IO-device module, for example, to specify how its controlregisters behave by mapping them to specific ad-dresses in IO-port space.

• SoftSDV events enable a module to request notifica-tion of some occurrence inside another module.Events can be used to model IO-device interrupts, orto collect event traces, such as a sequence of OS con-text switches, which might be analyzed by a trace-processing module.

• SoftSDV data items provide a mechanism for modulesto name and share their state with other modules.Named data items enable a processor module, for ex-ample, to make its simulated register values availableto a debugging tool. Some data items are managed bythe SoftSDV kernel itself and they are sometimes usedto specify configuration data that modules can query

Module Characteristics Typical Usage

Fast ISA Emulator Highest speed, no cycle-accurate perf.data

Rapid IA-64 app. and OS development

Resource Analyzer Medium speed/accuracy, limited flexibility Large application, OS and compiler tuning

ProcessorModules

Microarch Simulator Highest accuracy and flexibility Advanced compiler and microarch co-design

Basic IO Devices Provide platform-modeling completeness Early OS porting with standard boot devicesIO-deviceModules

IO-Proxy Module Links to arbitrary PCI and USB devices Advanced device-driver development

Table 1: Standard SoftSDV component modules

Page 23: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

SoftSDV: A Presilicon Software Development Environment for the IA-64 Architecture 5

to determine how they should function during asimulation.

• SoftSDV time enables modules to synchronize theirinteractions to a common clock and to schedule theexecution of future events. A disk-controller model,for example, can register a callback function with thekernel to be called after some pre-computed delay thatrepresents the time for a simulated seek latency.

SoftSDV has a standard set of tracing and debuggingmodules that are built upon the abstractions above.SoftSDV events enable the construction of tracingmodules that specify, by event name, items to be traced.Traceable events include instructions, addresses, IO-device accesses, OS events, etc. SoftSDV traces aresuitable for trace-driven cache and processor performancesimulators, and they can also provide input to the Vtuneperformance analyzer [11]. The SoftSDV debugger usesSoftSDV events and data items for setting instruction anddata breakpoints, reading memory values, examiningregister values for a specific simulated processor, etc.

SoftSDV also includes a standard set of componentmodules, including three IA-64 processor models, eachoffering different levels of simulation speed, accuracy, andflexibility. For completeness in platform modeling,SoftSDV also includes an assortment of IO-device modelsand a special IO-proxying module that links the simulationto actual PCI and USB devices installed in the simulationhost machine. Table 1 summarizes these standardcomponent modules and serves as an outline for theremainder of this paper, in which we detail the techniquesused by each of these modules.

FAST IA-64 EMULATIONSoftSDV’s fastest processor module is an emulator thatimplements the full IA-64 instruction set, including allprivileged operations, address translation, and interrupt-handling functions required to support operating systems.The principal goal of the emulator is speed; it is unable toreport cycle-accurate performance figures.

SoftSDV’s emulator uses dynamic binary translation toconvert IA-64 instructions into sequences of host (IA-32)instructions called capsules, which consist of a prologand a body (see Figure 2). The capsule body emulates theIA-64 instruction in terms of IA-32 instructions executedon the host machine. In the example shown in Figure 2, a64-bit Add instruction is emulated by a sequence of IA-32instructions that loads the required source registers from asimulated IA-64 register file (held in host memory),performs the simulated operation (a 64-bit Add using the32-bit Add operations of IA-32), and then stores the resultback to the simulated register file.

The capsule prolog serves two purposes. First, itimplements behavior that is common to all IA-64 instruc-tions, such as predication and control speculation. Aportion of the prolog code checks each instruction’squalifying predicate, and it jumps over the capsule to thenext instruction if the predicate is false. Similarly, anotherportion of the prolog examines the NaT bits of all sourceoperands, and it propagates any set values to destinationregisters, or it generates a fault as dictated by thesemantics of the instruction. A second use for the prologis to implement a variety of simulation services, such asexecution tracing, instruction counting, and executionbreakpoints, which we discuss later.

The emulator translates instructions only as needed inunits of basic blocks, which it caches in a translationdatabase. Since capsules require on average 25 IA-32instructions for each IA-64 instruction that they emulate, alarge simulated workload can quickly consume hostmemory, causing the host OS to begin paging to disk. Theemulator limits the maximum size of its translation cache toprevent host-OS paging, choosing instead to retranslateIA-64 instructions, a far faster operation.

SoftSDV capsules eliminate the need for a full fetch-decode-execute loop. Instruction emulation begins with anindirect branch that goes directly to a capsule corre-sponding to the current simulated instruction pointer (IP).Since capsules are linked directly from one to another,execution proceeds from capsule to capsule until anuntranslated instruction is encountered, and control

TranslationProcess

Original IA-64 Code

mov eax , r2.low ; load r2mov ebx , r2.highmov ecx , r3.low ; load r3mov edx , r3.highadd ecx, eax ; 64-bit addadc edx, ebxmov r1.low, ecx ; store r1mov r1.high, edx

Capsule Body (for Add)

if (!p6) jmp over capsule

if (ica==0) jmp to ica_event

decrement ica counter

r1.NaT = r2.NaT | r3.NaT

Capsule Prolog (generic)

.....

if (ica==0) jmp to ica_event

decrement ica counter

Capsule Prolog (generic)

(p6) ADD r1 = r2, r3

SUB r13 = r17, r6

LD1 r2 = [r12] ;;

BR LOOP

...

...

12Timer

ICA Event-Delay List

7Keyboard

Equivalent IA-32 Code

Sub Capsule Body

Load Capsule Body

Add Capsule Body

Branch Capsule Body

Capsule Prolog

Capsule Prolog

Capsule Prolog

Capsule Prolog

Figure 2: Binary translation

Page 24: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

SoftSDV: A Presilicon Software Development Environment for the IA-64 Architecture 6

transfers back to the translator.

SoftSDV executes nearly all IA-64 instructions as cap-sules, but certain complex instructions are emulated withcalls to C* functions. Throughout the development ofSoftSDV, we considered moving to capsule-basedimplementations for various complex operations, but wefrequently found that the resultant reduction in flexibilityand maintainability of the emulator did not justify theoptimization. We also considered more aggressiveintercapsule optimizations, such as those used by Shade∗and Embra (e.g., avoiding loads/stores to the simulatedregisters in host memory when neighboring instructionsuse the same register). Unfortunately, with the very largeIA-64 register set, and the limited number of IA-32 hostregisters, we found such optimizations ineffective.Intercapsule optimizations suffer the further side effect oflimiting the granularity at which debug breakpoints canconveniently be set.

Address TranslationSoftSDV models simulated physical memory by allocatingpages of host virtual memory to hold the contents ofsimulated data. For any simulated memory reference, theemulator must first translate a simulated virtual address toa simulated physical address (V2P), and then furthertranslate this physical address to its allocated hostaddress (P2H). The composition of these two translation

functions provides a full translation from virtual to hostmemory (V2H = P2H(V2P(Virtual))).

The emulator uses a data structure called a V2H cache toaccelerate address translation (see Figure 3). When an IA-64 load/store operation or branch/call instruction refer-ences a virtual address, the emulator first searches a V2Hcache using highly efficient assembly code called from thecapsule. In the common case, the translation is found, andthe memory reference is quickly satisfied. In the case of aV2H miss, a full translation sequence (V2P and P2H) isactivated by simulating an access to the TLB and an OS-managed page-table structure. If the translation succeeds,the V2H table is updated, and execution returns to thecapsule. If the search fails to find a valid translation, thena simulated page fault or protection violation has oc-curred, and SoftSDV raises an exception for the simulatedOS to handle.

The emulator implements V2H tables as variable-sizedcaches of valid address mappings. This is in contrast toEmbra’s MMU relocation array, which is a fixed-size 4-MB table that maps every possible page in a 32-bit virtualaddress space [4]. While the Embra approach guaranteesa 100% hit rate for any valid mapping, we found thisapproach unusable for modeling a 64-bit virtual addressspace. Since V2H refills are a principal source of slow-downs, the emulator uses a number of optimizationtechniques to accelerate refills, and it supports configur-able V2H table sizes to tune hit rates to the requirementsof a given workload.

Page ProtectionThe emulator implements page protections by building aV2H cache for each type of memory access: V2H-read,V2H-write, and V2H-execute. A read-only page, forexample, present in the V2H-read cache does not containan entry in the V2H-write cache. By dividing the V2Hcaches in this way, the emulator avoids performingprotection checks explicitly in each capsule; it merelygenerates code to access the V2H cache appropriate to thedesired operation (e.g., load, store, branch), and a V2Hmiss enforces the protection. The emulator builds a set ofread-write-execute V2H caches for each privilege level (i.e.,12 V2H caches in all) and simply switches between themwhen privilege levels change. The use of multiple V2Hcaches is a memory-speed tradeoff. At the expense ofextra memory devoted to the V2H caches, the emulatorsimplifies the protection-checking code in each capsule,thus accelerating overall instruction-emulation times.

Speculative Data AccessesData speculation with advanced loads requires specialtreatment by the emulator. The capsule for an advanced

Virtual To Host Access Function

Virtual To Host

Access Function

64bit Virtual Address

Tag=4 Set=55

....

.

54

55

56

57

58

....

.

0x838000+offset

Host addressV2H Hit

....

.

54

55

56

57

58

....

....

.

54

55

56

57

58

....

.....

Tag: 3

Host:0x839000

Tag: 4

Host:0x838000

Tag: 6

Host:0x837000

Invalid

Entry

Tag: 2

Host:0x843000

.....

....

54

55

56

57

58

....

V2H access cache(one per privilege level)

Offset

Tag=8 Set=57

V2H Miss

Offset

64bit Virtual Address

63-25 24-12 11-0Bits Division

Exception

Access simulatedTLB

SimulatedMMU

Found ?

N

Y

Allocate on demand inPP Data Base

Virtual To

Physical Physical To Host

Update V2H Cacheand return

Figure 3: Emulating IA-64 address translation

Page 25: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

SoftSDV: A Presilicon Software Development Environment for the IA-64 Architecture 7

load includes code that inserts the load address into asimulated ALAT. The capsules for any subsequent storeinstructions include highly optimized code (11 hostinstructions) that searches the ALAT for an addressmatch. Any entries with matching addresses are removedfrom the ALAT so that a subsequent load-check operationwill invoke the appropriate fix-up code.

Instruction FetchingRather than point directly to translated code capsules,V2H-execute entries reference a page of pointers to codecapsules. This level of indirection solves two problems.First, since the emulator lazily translates instructions abasic block at a time, many portions of a code page may beuntranslated; these cases are handled by pointing to a do-translate function, rather than a capsule entry point.Second, since capsules are of varying size, their entrypoints are not simple to calculate given only a virtualaddress for an instruction; the pointers-to-capsules pagegreatly simplifies simulation of branch instructions andarbitrary jumps to code, such as from a debugger.

The emulator avoids V2H-execute lookups by directlychaining a branch-instruction capsule to its target-instruction capsule, provided they reside on the samepage. For cross-page branches, the chain passes throughthe V2H-execute cache to ensure that the code page isaccessible.

The emulator supports self-modifying code by preventinga page from residing in both the V2H-write and V2H-execute caches at the same time. Any attempt to write to acode page causes a V2H-write miss, which causes thepage to be removed from the V2H-execute cache. Anysubsequent attempt to execute instructions from the pagecauses a V2H-execute miss, which results in a lazyretranslation of the modified code page.

Interrupt ProcessingThe emulator can potentially run for long periods of timewithout ever leaving its capsules. This presents a problembecause other SoftSDV modules (such as simulated IOdevices) might need to interrupt processor execution. Theemulator provides a solution to this through a mechanismcalled an instruction-count action (ICA). ICA is based ona sorted list of event-delay pairs that define a set ofcallback functions to be executed after some number ofinstructions have executed. The delay of the event at thehead of the ICA list is decremented in the prolog of eachcapsule (see Figure 2). When the delay reaches 0, theevent is triggered by calling its associated callbackfunction. After completion of the callback function, theevent-delay pair is deleted from the ICA list.

The ICA mechanism is integrated with the SoftSDV timeabstraction, providing a coarse-grained notion of timebased on number of instructions executed. It is also idealfor implementing certain IA-64 debugging facilities, suchas single-step instruction execution, which wouldotherwise require retranslation of instruction capsules.

Multiprocessor SimulationThe emulator can simulate a platform with up to 32processors in a symmetric shared-memory configuration.Only one processor is simulated at a time, with switchesbetween simulated processors scheduled in a simpleround-robin order with a configurable time slice. Allprocessors share memory, translations, and all simulatedplatform devices. Capsules indirectly access simulatedprocessor state and processor-specific emulator state(e.g., V2H caches, ICA lists, etc.) through a pointer, whichenables rapid switching between simulated processors viaa simple pointer change.

DYNAMIC RESOURCE ANALYSISThe emulator described in the previous section is ideal forrapid porting of operating systems and applications to IA-64. Once the porting is complete, a software developermay be interested in tuning code for performance, whichrequires trading some simulation speed for increasedaccuracy. SoftSDV’s second processor module offers justthis: it aims to achieve a level of performance-predictionaccuracy that is within 10% of a detailed Itanium microar-chitecture simulator (which we describe in the nextsection), while retaining the highest possible simulationspeed.

SoftSDV applies three principles to achieve this goal.First, it tightly integrates performance analysis with its fastIA-64 emulator, enabling it to overcome the bottlenecks oftraditional trace-driven performance simulation. Second, itassumes an in-order processor pipeline and selectivelymodels only those processor resources that have thegreatest effect on overall performance (e.g., first-levelcaches, branch prediction, functional units, etc.). Third, itcaches the results of its resource analysis to speed futureperformance simulation. We call this collection ofmethods dynamic resource analysis, and we refer to thisSoftSDV module as the resource analyzer.

Processor and Memory ResourcesProcessor performance analysis ultimately boils down toaccounting for the resource requirements of executingcode. Take, for example, the code shown in Figure 4,which shows a subtract and a load in one instructiongroup and an add and a branch in a second instruction

Page 26: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

SoftSDV: A Presilicon Software Development Environment for the IA-64 Architecture 8

group (the notation “;;” separates instruction groups).The load operation:

LD1 r2 = [r12] ;;

requires the resources of a source-address register (r12), adestination register (r2), and a data-cache line and readport. Similarly, the add instruction:

(p6) ADD r1 = r2, r3

requires the resources of an ALU functional unit, apredicate register (p6), two source registers (r2 and r3),and a destination register (r5).

A processor examines instruction groups to find collec-tions of instructions (called an issue group) that can bedispatched in parallel subject to the resource constraintsof its execution pipeline. When all the required resourcesof all instructions in an issue group are available, theinstructions can execute immediately; otherwise, they stall.In the example in Figure 4, the load feeds the add throughregister r2. If the load stalls because of a data cache miss,then the add will stall as well because it requires the r2resource. The resource analyzer identifies such resourcedependencies by dividing its operation between twophases, each of which is tightly integrated with the IA-64emulator. A static resource-analysis phase is invoked bythe emulator’s translator, and a dynamic-analysis phase isinvoked by translated code capsules.

Static Resource AnalysisLike the IA-64 emulator, the resource analyzer examinescode lazily, as it is first encountered. Whenever the IA-64emulator completes translation of a new basic block ofcode, it calls the resource analyzer, which examines thebasic block to find issue groups and then staticallydetermines the microarchitectural resources required byeach issue group. The analyzer caches this resource listto avoid the cost of repeating the analysis each time theissue group executes (see Figure 4). The resource listincludes both sources, which are required for the issuegroup to execute, and destinations, which are resourcesthat will be available after the issue group executes.

Dynamic Analysis PhaseThe dynamic phase of the analyzer is invoked by emulatorcapsules after execution of each basic block. The analyzerexamines instruction resource requirements by modeling asimplified Itanium microarchitecture consisting of a frontend, a back end, and caches.

The analyzer’s back end keeps track of the availability ofcore pipeline resources (e.g., register files, functionalunits, etc.) that could stall the execution of an issue group.Each resource that is required for the execution of thecurrent issue group is checked for its availability in thecurrent cycle. If it is not available, then the issue groupstalls execution, and the cycle counter is advanced to thetime when all required resources will be available and theissue group can enter the execution stage. Next, theresource state is updated to reflect the results of thecurrent issue group. For each destination of the instruc-tions in the issue group (excluding loads), the clock cyclein which these resources will be available for use bysubsequent instructions is calculated by adding eachinstruction’s latency to the cycle in which the issue groupwas dispatched.

The analyzer’s front end models branch prediction andinstruction-fetching mechanisms in accordance with theItanium microarchitecture. Since all instructions traversethe front end several cycles before they reach the backend, the resource analyzer maintains two separate cyclecounts, one for the back-end execution (as describedpreviously) and one for the front end. The two cyclecounters are synchronized through a model of a decou-pling buffer, which specifies the cycle in which an issuegroup can be dispatched. If the issue group is not ready,the decoupling buffer stalls the back end. Conversely, ifthe decoupling buffer is full, it causes front-end stalls.

The analyzer models instruction and data caches toaccount for performance lost due to cache misses. Thesecache models are referenced by both the front end (forinstruction fetches) and the back end (for data references).

SUB r17, r6, ALU r13 LD1 r12, Memory Unit r2

ADD p6, r2, r3, ALU r1 BR Branch Unit IP

TranslationProcess

Original IA-64 Code Equivalent IA-32 Code

Sub Capsule Body

Load Capsule Body

Add Capsule Body

Branch Capsule Body

Capsu le Pro log

Capsu le Pro log

Capsu le Pro log

Capsu le Pro log

Call Analyzer()

StaticResourceAnalysis

Resource ListIssue Groups Sources Destinations

#1#2

Dynamic analyzercalled during code execution.

(p6) ADD r1 = r2, r3

SUB r13 = r17, r6

L D 1 r 2 = [ r 1 2 ] ; ;

B R L O O P

...

...

Figure 4: Resource analysis for two issue groups

Page 27: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

SoftSDV: A Presilicon Software Development Environment for the IA-64 Architecture 9

The IA-64 emulator maintains its own copy of the first-level data cache and achieves speed through integrationwith its V2H translation tables in an approach similar tothat of Lebeck’s FastCache [6]. This copy contains asubset of the data-cache lines simulated by the resourceanalyzer. When the emulator detects a hit, it continuesexecution without calling the analyzer. In the rare casewhen the emulator suspects a cache miss, it calls theanalyzer for verification. When a miss occurs in theanalyzer cache, the analyzer updates its own cachecontents and informs the emulator about the cache line itreplaced.

The end results of the analysis described above are a setof performance metrics (e.g., branch-prediction accuracy,cache misses, stall cycles, etc.), which are mapped ontoSoftSDV data items so that they can be read by a perform-ance visualization tool, such as the Vtune performanceanalyzer.

DETAILED PROCESSORMICROARCHITECTURE SIMULATIONThe dynamic resource analyzer described previously isideal for rapid tuning of compilers, operating systems, andlarge applications, where approximate performance of afixed microarchitecture is acceptable. For other applica-tions, such as compiler tuning for a new microarchitectureunder development, a further shift along the speed-accuracy-flexibility spectrum is needed. To deal with suchsituations, SoftSDV works together with a simulationtoolset for exploration of IA-64 microarchitectural designs.SoftSDV’s third processor module, a detailed model of theItanium microarchitecture, is written using this toolset, asare other future IA-64 microarchitectures currently underdevelopment by Intel.

IA-64 Microarchitecture Simulation ToolsetThe toolset consists of two main components: an event-driven simulation engine, and a set of facilities forfunctional evaluation of IA-64 instruction semantics.

The event-driven simulation engine provides a flexible setof primitives for modeling microarchitectural resources,arbitration, and accounting of processor cycles. Aprocessor pipeline is modeled as a set of communicatingfinite state machines through which tokens representinginstructions or data are evaluated on a cycle-by-cyclebasis. Whenever a token cannot acquire a certain resource(e.g., a latch or port), a stall condition is encountered andits cycle penalty is accounted for. The total execution timeof a workload is derived from the accumulation of cyclesspent by all tokens that traverse the pipeline.

The functional evaluation of IA-64 instruction semanticsis provided by a set of four interfaces called by differentstages of the event-driven microarchitecture model:

• Fetch provides a decoded instruction, given an IP.

• Execute computes the results of an instruction in thecontext of some microarchitectural state.

• Retire commits the microarchitectural state to thepermanent architectural state.

• Rewind rolls back the unretired microarchitecturalstate to previously defined values.

By dividing the interfaces in this way, a processor modelis able to express complex microarchitectural interactionsinvolving speculative instruction execution.

Speed-Accuracy Modes and SamplingLike the resource analyzer, the Itanium microarchitecturemodel is tightly integrated with the IA-64 emulatorthrough an interface that enables the sharing of thearchitectural state (memory and processor registers). Thisinterface makes it possible to dynamically changesimulation speed and accuracy as a workload runs. It ispossible, for example, to rapidly advance through thesimulated boot of an OS with the fast IA-64 emulator.Then, prior to the execution of some workload of interest,the state of the processor’s caches is initialized usingmemory traces produced by the emulator. When detailedsimulation is desired, the microarchitectural simulatorreads the current machine state from the emulator, andbegins its simulation. After running in detailed mode forsome time, execution can return to the fast mode toadvance deeper into a workload’s execution.

SoftSDV supports both uniform sampling at some regular,predefined period, and event-based sampling. For event-based sampling, special markers are compiled aroundregions of interest in a workload. SoftSDV dynamicallyrecognizes such markers as a workload executes, andgenerates corresponding SoftSDV events, which aremonitored by the processor modules to determine whenthey should switch between speed-accuracy modes.

IO-DEVICE MODULESSoftSDV provides a standard collection of IO-devicemodels suitable for supporting the simulated execution ofa complete IA-64 operating system, including its basicdevice drivers. These include selected 460GX chipset andboardset functions (such as interrupt controllers, periodictimers, serial port, mouse, keyboard, etc.) as well as modelsfor assorted storage and graphics controllers (such asIDE/ATA, ATAPI CD-ROM, VGA, etc.).

Page 28: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

SoftSDV: A Presilicon Software Development Environment for the IA-64 Architecture 10

A SoftSDV device model typically consists of two halves:a front half that implements the device interface asexpected by a simulated device driver, and a back half thatsimulates the device function. The implementation ofmany device models is a straightforward conversionbetween device commands and host-OS services. Modelsfor the keyboard and mouse, for example, field host-OSwindow events (keypress up/down, pointer movement,etc.) and convert them into equivalent device events (akeyboard interrupt and scancode). Similarly, the serial-port model connects to the actual serial port of the hostmachine through host-OS calls, and it ferries data backand forth between the simulated and actual serial ports.

IO-Device ServicesSince many devices offer a similar function (e.g., storage),but with differing interfaces (e.g., IDE/ATA, ATAPI,SCSI), we identified points in common among devicemodels and structured them as reusable IO-deviceservices, encapsulated in their own SoftSDV modules. Adisk-image service, for example, provides functionalityuseful for any storage model. It holds the contents of asimulated disk in a file or a raw disk partition in the hostmachine, and has the ability to log all changes to thesimulated disk’s contents. An off-line utility can theneither commit or discard those changes at the end of asimulation. The disk-image service maps itself onto aSoftSDV space, where its functionality can be accessed byany disk-interface model.

Similarly, a display service maps a generic flat framebuffersurface onto a SoftSDV space, and it reflects accesses tothis surface in a host-OS window, or directly to a seconddedicated display attached to the simulation host machine.This structuring relieves graphics-adapter models from thedetails of how a framebuffer is displayed so that they canfocus instead on simulated processing of graphicscommands. It also enhances SoftSDV maintainability,since it decouples graphics models from the host-OSwindowing system; when porting to a new host OS, onlythe display service and not each graphics model must berewritten.

Pseudo DevicesSome SoftSDV modules model a fictitious device, or onlysome aspect of an actual device. We have, for example,built a pseudo-device module that maps the resourcerequirements of multiple devices into PCI configurationspace. Although not backed by actual PCI-device models,these headers present to a simulated OS the illusion of aplatform with multiple PCI devices, and thus enable rapidtesting of the OS’s device configuration and plug-and-play algorithms. Such a test environment is, in fact, farmore convenient than an actual hardware platform since

there is no need to physically populate actual PCI slotswith various devices to create different test cases. Wehave experimented with other types of pseudo devices,such as an IO-monitoring service, which would enable thereplaying of keyboard and mouse input to a graphicalwindowing system in a reproducible and OS-independentmanner.

IO-PROXY MODULEDue to the broad diversity and sheer number of IO devicesin existence, we realized early in the development ofSoftSDV that it would be impossible to write softwaremodels of all IO devices of interest. Indeed, some IOdevices are so complex that modeling them in softwarewould be considerably more work than the ultimate goal ofporting their device drivers to IA-64.

An alternative solution is to link existing hardware devicesdirectly to SoftSDV so that they can be accessed andprogrammed by simulated IA-64 device drivers underdevelopment. SoftSDV accomplishes this through acombination of hardware and software components,shown in Figure 5. The hardware components consist ofan arbitrary target PCI device plugged into a custom-designed riser board that performs memory and interrupt-remapping functions. The software components include aSoftSDV module and a host OS device driver, whichfunction as proxies for interactions between the actual

SoftSDV

Simulated OS

Host OS

TargetPCI Device

Simulated Device Driver

IO ProxyModule

IO Proxy Driver

Actual BARs

com

man

ds

Virtual BARs

control

in

terr

upts

Sim

ulat

edP

hysi

cal

Mem

ory

com

man

ds

mem

ory

rem

ap

memory

accesses

inte

rrup

ts

RiserBoard Logic

256 MB

192 MB

128 MB

64 MB

0 MB

Reserved forH

ost OS

Reserved forS

oftSD

V

re

ma

pp

ed

m

em

or

y

ac

ce

ss

es

b

y

t ar

ge

t P

CI

de

vi c

e

Host Memory

SoftSDVKernel

Figure 5: Linking PCI devices to SoftSDV

Page 29: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

SoftSDV: A Presilicon Software Development Environment for the IA-64 Architecture 11

hardware PCI device and the simulated device driver. Theproxy components support three forms of interactionbetween the target device and the simulated device driverrunning on SoftSDV:

• commands issued from the driver to the device via IO-port and memory-mapped IO accesses

• physical-memory accesses by the device

• interrupts from the device to the simulated driver

To support the largest possible set of devices, SoftSDVenables each of these types of interactions withoutrequiring any specific knowledge of the target PCI device.The following sections detail how each of the three formsof interaction are supported.

Command ProxyingThe key difficulty in proxying commands from thesimulated device driver is determining the location of thecontrol registers for arbitrary target devices. Fortunately,standard PCI configuration mechanisms provide a meansfor accomplishing this.

All PCI devices support a configuration header visible tosoftware through well established locations in IO-portspace. The PCI configuration header includes a set ofBase Address Registers (BARs), which the host proxydevice driver reads to determine the size and location ofthe device’s control registers in IO-port space or physical-address space. The proxy driver passes this informationup to the proxy SoftSDV module, which registers accesshandlers for those regions of address space with theSoftSDV kernel. The registered handler ensures that theproxy module is called whenever the simulated devicedriver sends commands to the device. The proxy moduleblindly passes such commands down to the proxy driver,which in turn issues them to the actual hardware device.

The actual solution is somewhat more complex because bythe time the SoftSDV simulation starts running, the hostOS will have already configured the target device’s BARsto avoid conflicts with other host IO devices.2 Theproblem is that SoftSDV models an entirely different(simulated) view of the platform, so the simulated OS mayattempt to configure the target device’s control registersto a different set of locations that might conflict with otherhost IO devices. The proxy code solves this problem bypresenting a set of virtual BAR values to the simulated

2 BAR registers are programmable to enable plug-and-playsoftware to assign conflict-free locations for devicecontrol registers.

OS, and it remaps these values to the actual BAR valuesused by the target device.

Remapping Physical Memory AccessesAfter the simulated driver sends the device commands, thetarget device will typically try to access physical memoryto retrieve or deposit data associated with the command.This is a problem because the device is directed to performthe operations in simulated physical memory, but thedevice will operate on actual host memory belonging tothe host OS, causing it to crash.

The solution is to partition the host physical memorybetween the host OS and the simulated OS. During boot,the host OS is configured not to use a certain amount ofhost physical memory (in the example in Figure 5, thereserved region is 64MB, starting at 192MB in hostmemory). The reserved region is then mapped to thesimulated memory in the SoftSDV user process.

This alone is not sufficient to solve the problem since thedevice will still be programmed to use physical memory inthe range of 0-64MB when it should instead be accessingmemory in the range of 192-256MB. The proxy codecould, in principle, interpret and modify the commands itintercepts from the simulated driver and remap addressesas appropriate before passing the commands to the actualdevice. Unfortunately, such a solution requires specificknowledge of the device and its commands. SoftSDVinstead uses a small amount of hardware remapping logiclocated on the riser board between the target device andits host PCI slot. The remapping logic relocates allmemory accesses made by the target device to the upperpartition of memory, based on flexible configurationsettings that specify the size of memory and the locationof the partition.

Interrupt ProxyingWhen the target device generates an interrupt, it is fieldedby the proxy code and sent to the simulated device driver.Two problems make it difficult to perform these steps in adevice-independent manner. First, since interrupt lines arecommonly shared between PCI devices, the proxy codemust determine whether the interrupt is coming from thetarget device or from some other host device. Second, theinterrupt line must be temporarily deactivated; otherwise,SoftSDV (which runs as an ordinary user-level process ofthe host OS) will be continually interrupted, and thesimulated device driver will never have a chance to run.

Both of these operations are performed with the help ofsome additional logic on the riser board that enables thehost proxy driver to sense and mask the interrupt from thedevice, before it is driven onto the shared PCI interruptline. When an interrupt occurs, the proxy driver uses this

Page 30: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

SoftSDV: A Presilicon Software Development Environment for the IA-64 Architecture 12

logic to determine if the interrupt is in fact originating fromthe target device. If so, it temporarily masks the interruptand passes control to the simulated device driver runningin SoftSDV. The simulated driver, which understands thespecifics of the device, then properly dismisses theinterrupt at its source in the target device.

RESULTS AND EXPERIENCESTable 2 summarizes the speed of SoftSDV’s three proces-sor modules when running selected SPEC95 benchmarksand when booting Microsoft 64-bit Windows∗ 2000. Italso reports accuracy of the performance analyzer relativeto the microarchitecture simulator.3 All experiments wereperformed on a 500-MHz Intel Pentium III processor-based system with 512 MB of memory.

The emulator offers simulation speeds ranging from 10 to25 million instructions per second (MIPS) for the SPECbenchmarks, and about 3 MIPS for the simulated Win-dows∗ boot. The lower execution rate for the OS boot isdue to frequent address-space generation and processswitching, which increases V2H translation overheads.Even so, at an average rate of 3 MIPS, SoftSDV is able toperform a simulated Windows boot in less than sevenminutes, and is able to boot most other IA-64 operatingsystems in under ten minutes. The performance accuracyof the emulator, however, is limited to reporting the totalnumber of instructions executed by a workload.

With error rates ranging from about 1% to 7% andaveraging 3%, the dynamic resource analyzer exceededour goal of predicting cycle-level processor performanceto within 10% of the detailed Itanium microarchitecturesimulator, and it does so at simulation rates ranging fromabout 160 to 250 thousand instructions per second (KIPS).These speeds are sufficient for tuning compilers and largeapplications, which often require millions if not billions ofinstructions to be executed. These results suggest that byexplicitly exposing ILP, IA-64 compilers not only enablesimpler, higher-frequency processor implementations, butthey also make possible very fast processor performanceanalysis. The methods used by the analyzer do, however,have their limitations. The analyzer depends on theexistence of a reference microarchitectural simulatoragainst which it can be calibrated. Also, its flexibility is ∗ Other brands and names are the property of theirrespective owners.3 Table 2 does not report the accuracy of the other twoprocessor modules because the emulator does not provideperformance results and because we use the microarchi-tecture simulator as the baseline for performance (i.e., forthe purposes of this paper, we consider its error to be 0%).

somewhat limited, since it assumes an in-order microarchi-tecture and is unable to model hardware-controlledspeculative execution.

When flexibility and highest accuracy are required, there isno substitute for detailed microarchitecture simulation.The IA-64 microarchitecture toolset has proven itselfflexible enough to rapidly explore design options, both forItanium processors and for future IA-64 microarchitecturescurrently under development at Intel. The tradeoff for thisflexibility and accuracy is a much lower speed of simula-tion, in the range of 1 to 2 KIPS.

We found building state-sharing mechanisms between thethree processor models to be a very powerful capability.Had each processor simulator only been able to workindependently, methods such as sampling performanceover extended regions of large workloads would neverhave been possible. With sampling, we are able to freelyselect the level of speed and accuracy required for a givensimulation. For the SPEC benchmarks, our experience hasbeen that uniform sampling ratios of between 15:1 and 80:1yield simulation results that are statistically very close tofull simulation. Table 2 reports effective KIPS rates fordetailed microarchitecture simulation when a samplingratio of 40:1 is used. The speedups relative to fullsimulation are not a full factor of 40 because simulationspeeds are somewhat lower during the beginning of eachdetailed sample, and because the simulation still includesthe overhead of running the emulator/analyzer during fastmode. Nevertheless, sampling effectively increases thespeed of simulation by more than an order of magnitude.

Our original plans were to develop software models for allimportant IO devices, but we quickly realized that thisapproach was intractable, which led us to develop the IO-proxy module. The IO-proxying approach became

AnalyzerWorkload EmulatorSpeed(MIPS) Speed

(KIPS)Accu-racy

(% error)

MicroarchSimulator Speed(KIPS)

go 15.4 237 1.18% 15.0

m88ksim 14.8 252 1.04% 34.8

gcc 10.5 224 4.96% 17.2

compress 25.3 180 7.19% 18.4

li 13.5 227 4.06% 27.2

ijpeg 28.3 162 1.97% 27.8

perl 10.5 212 1.21% 18.6

vortex 11.8 158 2.79% 22.7

NT boot 3.00 211

Page 31: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

SoftSDV: A Presilicon Software Development Environment for the IA-64 Architecture 13

particularly important for more complex device types, suchas SCSI controllers, ethernet interfaces, graphics adapters,and USB host controllers, all of which successfully workwith the technique. Not only did this approach save effortin writing IO-device models in software, but it also bettersupported a broad range of OS’s. Had we selectedparticular SCSI or ethernet controllers to model, we wouldbe limited to supporting OS’s that had drivers for thoseparticular devices. With the IO-proxy module, OSdevelopers can select virtually any PCI or USB device forwhich their OS has a corresponding driver.

We achieved our goals of supporting IA-64 softwaredevelopment all along the software stack. SoftSDVsuccessfully runs:

• several commercial operating systems, includingMicrosoft 64-bit Windows∗ 2000, Trillian Linux∗, IBMMonterey-64∗, Sun Solaris ∗, Compaq Tru64∗, NovellModesto∗, and HP-UX∗.

• device drivers for at least a dozen complex PCIdevices ranging from graphics and ethernet adapters,to SCSI and USB host controllers.

• numerous large applications

• three well-tuned IA-64 optimizing compilers

These layers of code were all working together, allexercised before silicon, and all ready for bring-up.

The real test for SoftSDV came after the availability ofactual hardware SDVs. IA-64 versions of Windows 2000and Trillian Linux* that were developed on SoftSDVbooted within ten days of the availability of Itaniumprocessor’s first silicon. Drivers for complex devices,such as SCSI disks, ethernet adapters, and graphicscontrollers, quickly followed over the next week, and thefirst MP operating systems were running a week after that.Many of the problems encountered were due to setup andconfiguration issues with the hardware SDV platform.Once resolved, other operating systems have beenbrought up even more quickly. IBM Monterey-64, forexample, was up and running in under three hours on aqualified Itanium processor SDV.

For further discussion of the issues involved in portingoperating systems to IA-64, please see “Porting OperatingSystem Kernels to the IA-64 Architecture for PresiliconValidation Purposes” [12], which appears in this sameissue of the Intel Technology Journal.

∗ Other brands and names are the property of theirrespective owners.

CONCLUSIONCentral to the design of SoftSDV is extensibility. Bybuilding a core simulation kernel with a general set ofabstractions, we were able to unify several differentsimulation technologies, each with their own uniquecapabilities with respect to speed, accuracy, complete-ness, and flexibility.

SoftSDV has proven that substantial amounts of complexsoftware can be developed, presilicon, even for an entirelynew ISA such as IA-64. As a general, extensible simula-tion infrastructure, SoftSDV is now being used in severalother efforts throughout the company, including IA-32presilicon software development, performance simulationof future IA-based microarchitectures, and processordesign validation.

ACKNOWLEDGEMENTSSoftSDV is the product of dozens of contributors span-ning three of Intel’s development sites. The fast IA-64emulator and resource analyzer were developed in theMicroprocessor Products Lab (MPL-Haifa) under theleadership of Tsvika Israeli. Methods for linking IA-64microarchitecture models to SoftSDV were developed bythe Microprocessor Research Lab (MRL) in Santa Clara,while the platform and IO-device modules were developedin MRL-Oregon. The design and implementation of theSoftSDV kernel and its extensible API was a joint effort ofall three groups. The IA-64 microarchitecture simulationtoolset was developed in the IA-64 Product Division (IPD)by Ralph Kling, Rumi Zahir, and their teams.

Many thanks to all those who contributed: JonathanBeimal, Rahul Bhatt, Baruch Chaikin, Stephen Chou,Maxim Goldin, Tziporet Koren, Mike Kozuch, TedKubaska, Etai Lev-Ran, Larry McGlinchy, Amit Miller,Kalpana Ramakrishnan, Edward Rubinstein, NadavNesher, Paul Pierce, Rinat Rappoport, Shai Satt, JeyasinghStalinselvaraj, Gur Stavi, Jiming Sun, and Gadi Ziv.

As key users of SoftSDV, special thanks go to Daniel Aw,Alan Kay, Bernard Lint, Sunil Saxena, Stephen Sked-zielewski, Fred Yang, and Wilfred Yu for their alwayshelpful feedback, and especially for making OS-portingefforts to IA-64 an enormous success.

And finally, much credit goes to our managers, ShukyErlich, Jack Mills, Wen-Hann Wang and Richard Wirt,who somehow kept us all working together productively!

REFERENCES[1] C. Dulong, “The IA-64 Architecture at Work,” IEEE

Computer, Vol. 31, No. 7, pp. 24-32, July 1998.

Page 32: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

SoftSDV: A Presilicon Software Development Environment for the IA-64 Architecture 14

[2] Intel Corporation, IA-64 Application Developer’sArchitecture Guide, Order Number: 245188-001,http://developer.intel.com/design/IA64/, May 1999.

[3] R. Cmelik and D. Keppel, “Shade: A Fast Instruction-Set Simulator for Execution Profiling,” in Proceedingsof the ACM Conference on Measurement and Model-ing of Computer Systems (SIGMETRICS), pp. 128-137,Nashville, Tennessee, May, 1994.

[4] E. Witchel and M. Rosenblum, “Embra: Fast andFlexible Machine Simulation,” in Proceedings of ACMConference on Measurement and Modeling of Com-puter Systems (SIGMETRICS), pp. 68-79, Philadelphia,May, 1996.

[5] D. Burger and T. Austin, “The SimpleScalar Tool Set,”Version 2.0, University of Wisconsin-Madison Com-puter Sciences Tech Report #1342, June, 1997.

[6] A. Lebeck and D. Wood, “Active Memory: A NewAbstraction for Memory System Simulation,” ACMTransactions on Modeling and Computer Systems(TOMACS), Vol. 7, pp. 42-77, 1997.

[7] E. Schnarr and J. Larus, “Fast Out-of-Order ProcessorSimulation Using Memoization,” in Proceedings of the8th International Conference on Architectural Sup-port for Programming Languages and OperatingSystems (ASPLOS-VIII) , pp. 283-294, San Jose, CA,October, 1998.

[8] T. Conte, M. Hirsch, K. Menezes, “Reducing StateLoss for Effective Trace Sampling of Superscalar Proc-essors,” in Proceedings of the 1996 InternationalConference on Computer Design (ICCD) , Austin, TX,October 1996.

[9] M. Rosenblum, E. Bugnion, S. Devine, and S. Herrod,“Using the SimOS Machine Simulator to Study Com-plex Computer Systems,” ACM Transactions onModeling and Computer Systems (TOMACS), Vol. 7,pp. 78-103, 1997.

[10] P. Magnusson, F. Dahlgren, H. Grahn, M. Karlsson, F.Larsson, F. Lundholm, A. Moestedt, J. Nilsson, P.Stenström, and B. Werner, “SimICS/sun4m: A VirtualWorkstation” in Proceedings of the 1998 Usenix An-nual Technical Conference, New Orleans, Louisiana,June, 1998.

[11] J. Wolf, “Programming Methods for the Pentium® IIIProcessor’s Streaming SIMD Extensions Using theVTune™ Performance Enhancement Environment,”Intel Technology Journal, Q2 1999.

[12] K. Carver, C. Fleckenstein, J. LeVasseur, S. Zeisset,“Porting Operating System Kernels to the IA-64 Archi-

tecture for Presilicon Validation Purposes,” IntelTechnology Journal, Q4 1999.

Authors’ BiographiesRichard Uhlig is a senior staff researcher in Intel’sMicroprocessor Research Labs in Oregon. His researchinterests include exploring new ways to analyze andsupport the interfaces between platform hardwarearchitectures and system software. Richard earned hisPh.D. in computer science and engineering from theUniversity of Michigan in 1995. His e-mail [email protected].

Roman Fishtein is a senior software engineer in theMicroComputer Products Lab in Haifa. He has been withIntel since 1995 and has been involved in many aspects ofthe SoftSDV project, which he is currently leading. Romanobtained B.S. and M.S. degrees in electrical engineeringfrom the Tashkent Electrotechnical Institute of Communi-cation. His e-mail is [email protected].

Oren Gershon joined the Intel Development Center inHaifa, Israel in 1990 and is currently a staff softwareengineer with MicroComputer Products Lab in Santa Clara.Oren pioneered the design and development of severalkey SoftSDV technologies and has been involved with theproject since its inception in 1993. Oren earned the IntelAchievement Award for this work. During a recent stint inMRL Santa Clara, Oren has been researching advancedperformance analysis and visualization tools for future IA-64 implementations. Oren obtained a B.S. degree incomputer science from the Technion, Israel Institute ofTechnology in 1992. His e-mail is [email protected].

Israel Hirsh is a senior software engineer in the Micro-Computer Products Lab in Haifa, Israel. He has been withIntel since 1992 and has made major contributions toSoftSDV during the five years that he led the project.Israel obtained a B.S. degree in computer science from theTechnion, Israel Institute of Technology in 1978. His e-mail is [email protected].

Hong Wang is an engineer working in the IA-64 Architec-ture and Microarchitecture Research Group of Intel MRLin California. He has been active in building and enhanc-ing state-of-the-art simulation infrastructures for IA-64.Hong is an avid crank on entropy, FFT, and variationalprinciples of physics. His current interests are in discov-ering unorthodox ideas and applying them to next-generation IA-64 processor designs. Hong earned hisPh.D. degree in electrical engineering in 1996 from theUniversity of Rhode Island. His e-mail [email protected].

Page 33: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Assembly Language Programming Tools for the IA-64 Architecture 1

Assembly Language ProgrammingTools for the IA-64 Architecture

Ady Tal, Microprocessor Products Group, Intel CorporationVadim Bassin, Microprocessor Products Group, Intel CorporationShay Gal-On, Microprocessor Products Group, Intel Corporation

Elena Demikhovsky, Microprocessor Products Group, Intel Corporation

AbstractThe IA-64 architecture, an implementation of ExplicitlyParallel Instruction Computing (EPIC), enables thecompiler to exercise an unprecedented level of controlover the processor. IA-64 architecture features maximizecode parallelism, enhance control over microarchitecture,permit large and unique register sets, and more. Explicitcontrol over parallelism adds a new challenge to assemblywriting, since the rules that determine valid instructioncombinations are far from trivial, introducing newconcepts such as bundling and instruction groups.

This paper describes Intel’s IA-64 Assembler and IA-64assembly assistant tools, which can simplify IA-64assembly language programming. The descriptions of thetools are accompanied by examples that use advanced IA-64 features.

INTRODUCTIONThe IA-64 architecture overcomes the performancelimitations of traditional architectures and providesmaximum headroom for future development. Intel’sinnovative 64-bit architecture allows greater instruction-level parallelism through speculation, predication, largeregister files, a register stack, advanced brancharchitecture, and more. 64-bit memory addressabilitymeets the increasingly large memory footprintrequirements of data warehousing, e-Business, and otherhigh-performance server and workstation applications.Significant effort in the architectural definition maximizesIA-64 scalability, performance, and architectural longevity.

In the 64-bit architecture, the processor relies on theprogrammers or the compiler to set parallelism boundaries.Programmers can decide which instructions are executedin each cycle, taking data dependencies and availability ofmicroarchitecture resources into account. Assembly canbe the preferred programming language under the

following situations: when learning new computerarchitectures in depth; when programming at a low level,such as that required for BIOS, operating systems, anddevice drivers; and when writing performance-sensitivecritical code sections that power math libraries, multimediakernels, and database engines.

Intel developed the Assembler and the AssemblyAssistant in order to aid assembly programmers in rapidlywriting efficient IA-64 assembly code, using the assemblylanguage syntax jointly defined by Intel and Hewlett-Packard*.

The Intel® IA-64 Assembler is more than an assemblysource code-to-binary translator. It can take care of manyassembly language details such as templates andbundling; it can also determine parallelism boundaries orcheck for those given by assembly programmers. Theassembler can also allocate virtual registers and so enableassembly programmers to write code with symbolic names,which are replaced automatically with physical registers.

The Assembly Assistant is an integrated developmenttool. It provides a visual guide to some IA-64 architecturefeatures permitting assembly programmers to comprehendthe workings of the processor. The Assembly Assistanthas three main goals: to introduce the architecture to newassembly programmers; to make it easier to write assemblycode and use the Assembler; and to help assemblyprogrammers get maximum performance from their code.This last task is achieved through static analysis, a drag-and-drop interface for manual optimization, and throughautomatic optimization of code segments.

IA-64 ARCHITECTURE FEATURES FORASSEMBLY PROGRAMMINGThe IA-64 architecture incorporates many features thatenable assembly programmers to optimize their code forefficient, high-sustained performance. To allow greaterinstruction-level parallelism, the architecture is based on

Page 34: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

Assembly Language Programming Tools for the IA-64 Architecture 2

principles such as explicit parallelism, with many executionunits, and large sets of general and floating-pointregisters. Predicate registers control instruction execution,and enable a reduction in the number of branches. Dataand control speculation can be used to hide memorylatency. Rotating registers allow low-overhead softwarepipelining, and branch prediction reduces the cost ofbranches.

The compiler takes advantage of these features to gain asignificant performance speedup. Branch hints and cachehints enable compilers to communicate compile-timeinformation to the processor. New compiler techniquesare developed to use these features, and IA-64 compilerswill continue gaining speedups utilizing these features ininnovative ways.

This abundance of architecture features and resourcesmakes assembly writing a challenging task for assemblyprogrammers.

The IA-64 architecture requires that the instructions arepacked in valid bundles. Bundles are constructs that holdinstructions. Bundles come in several templates,restricting the valid combinations of instructions anddefining the placement of boundaries (stops) for maximumparallelism. Each instruction can be placed into a specificslot in a bundle, according to the template and theinstruction type. For example, the instruction allocr34=ar.pfs,2,1,0,0 appearing in the example below, canonly be placed in an M slot of a bundle. The templatedefined in the examp le below for the first bundle is .miimeaning that the first slot can only be taken by aninstruction that is valid for an M slot, and the followinginstructions must be valid for I slots. When no usefulinstruction can be placed in the bundle due to templaterestrictions, a special nop instruction, valid for the slot,must be used to fill the bundle (as observed in the case ofthe second slot in the second bundle; a nop.i was placedin an I slot).

max:{ .mii

alloc r34=ar.pfs,2,1,0,0 cmp.lt p5,p6=r32,r33 ;; (p6) add r8=r32,r0

} { .mib (p5) add r8=r33,r0 nop.i 0 br.ret.sptk b0 ;;

}

Example 1: Code reflecting language syntax

Assembly programmers are expected to define groups ofinstructions that can execute simultaneously by insertingstops. If a stop is missing, then there is a chance that not

all the instructions were meant to be executed in the samecycle. Such an instruction group may contain adependent pair of instructions. For example, it maycontain two instructions that write to the same register ora register write followed by a read of the same register.

The result of parallel execution of dependent instructions,even though not necessarily adjacent, is unpredictableand may vary in different IA-64 processor models. Thissituation is called a dependency violation. To avoid it,assembly programmers have to place the two instructionsin different groups by inserting a stop.

In Example 1 we can see a stop after the cmp instruction.This stop will ensure that the cmp will not be executed inparallel with the following add, and it enables the add touse the predicate p6 written by the cmp instructionwithout producing a dependency violation.

THE IA-64 ASSEMBLERThe IA-64 Assembler enables many capabilities beyondtraditional assemblers. In addition to assembling, itimplements full support of all architecture and assemblylanguage features: bundles, templates, instruction groups,directives, symbols’ aliases, and debug and unwindinformation.

Writing assembly code with bundles and templates is nottrivial. Assembly programmers must know the type ofexecution unit for each instruction, be it memory, integer,branch, or floating-point. Another important element ofthe assembly language is the stop that separates theinstruction stream into groups.

The IA-64 assembly language provides assembly writerswith maximum control through the use of two modes forwriting assembly code: explicit and automatic.

When writing in explicit mode, assembly programmersdefine bundle boundaries, specify the template for eachbundle, and insert stops between instructions wherenecessary. The Assembler only checks that the assemblycode is valid and has the right parallelism defined. Thismode is recommended for expert assembly programmers orfor writing performance-critical code.

The automatic mode significantly simplifies the task ofassembly writing while placing the responsibility forbundling the code correctly on the Assembler. TheAssembler analyzes the instruction sequence, buildsbundles, and adds stops.

template

predicate

stop

}bundle

Page 35: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

Assembly Language Programming Tools for the IA-64 Architecture 3

ld4 r4 = [r33]add r8 = 5, r8mov r2 = r56add r32 = 5, r4mov r3 = r33

Example 2: Original user code

{ .miild4 r4 = [r33]add r8 = 5, r8mov r2 = r56 ;;

}{ .mmi

nop.m 0add r32 = 5, r4mov r3 = r33 ;;

}

Example 3: Code created after assembly with automaticmode

In the example above we can see how the user can writesimple code, which the Assembler will then fit intobundles. The Assembler also adds nop instructions forvalid template combinations and stops as needed to avoidany possibility of dependency violations. (A stop bit wasadded at the end of the first bundle to avoid a dependencyviolation between the first instruction and the next to lastinstruction on r4.)

ParallelismOne of the tasks of the Assembler is to help assemblyprogrammers define the right parallelism boundaries. TheAssembler analyzes the instruction stream, taking intoconsideration the architectural impact of each instructionand any implicit or explicit operands involved in theexecution. However, it is hard to do the complete programanalysis needed in order to detect all these conditions,statically. Consider a common case in IA-64 architecturewhere two instructions writing to the same register may bepredicated by mutually exclusive predicates, as shown inthe Example 4.

cmp.ne p2,p3 = r5,r0 ;;…

(p2) add r6 = 8, r5(p3) add r6 = 12, r5

Example 4: Predicate relation

The Assembler can identify this case and ignore theapparent dependency violation between the two addinstructions on R6. In this case, the compare instruction,which defines the pair of predicates, precedes their usage.However, more complicated cases may exist. For example,consider a case in which there is a function call between

predicates set and usage. In this case, assemblyprogrammers may know that the called function doesn’talter the predicates’ values, but there is no way for theassembler to deduce this information, if the function is in adifferent file.

Another type of information known only at run-time is theprogram flow at conditional branches. The processorautomatically begins a new instruction group when thebranch is taken, and a dependency violation may occuronly on the fall-through execution path.

When writing in explicit mode, assembly programmers areresponsible for stops. The Assembler simply checks thecode and reports errors, even when it finds only potentialdependency violations. In this mode, to avoid falsemessages, assembly programmers can add annotationsdescribing predicate relations at that point of theprocedure.

.pred.rel “imply“, p1, p2(p1) mov r5 = r23(p2) br.cond.dptk Label1add r5 = 8, r15

Example 5: User annotation

The relation “p1 implies p2”, in Example 5 means that if p1is true then p2 is also true. Adding such a clue to theassembly code prevents a false dependency violationreport between the second and fourth lines.

Automatic mode simplifies the programming tasks whiledelegating the responsibility for valid instruction groupingto the Assembler. In automatic mode, the source codecontains no bundle boundaries. The Assembler ignoresstops written by the assembly programmer; it buildsbundles and adds stops according to the results of staticanalysis of instructions and program flow. In this mode,the code is guaranteed not to contain dependencyviolations.

The example below contains a dependency violationwhich is not immediately apparent. The first instructionwrites to CFM, while the second instruction reads fromCFM, resulting in a dependency violation. Usingautomatic mode, the dependency violation is automaticallyresolved.

br.ctop.dptk.many l8fpmin f33=f1,f2

Example 6: Code containing dependency violation

Page 36: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

Assembly Language Programming Tools for the IA-64 Architecture 4

{ .mibnop.m 0nop.i 0br.ctop.dptk.many l8 ;;

}{ .mfi

nop.m 0 fpmin f33=f1,f2

nop.i 0}

Example 7: Code after automatic mode assembly

In the example above, observe how the Assembler detectsa dependency violation on CFM between the instructions,and how it inserts a stop between them.

Virtual Register AllocationLarge register sets in the IA-64 architecture complementthe unique parallelism features. Maintaining assemblycode becomes harder when there is a need to track theassignment of many variables to registers. Modifying aprocedure code might lead to variable reallocation.

The virtual register allocation (VRAL) feature solves theseproblems. VRAL allows assembly programmers to usesymbolic names instead of registers, and it performs thetask of register allocation in procedures.

To employ VRAL, assembly programmers must use a setof VRAL directives in order to communicate some register-related information to the Assembler. Assemblyprogrammers assign groups of physical registers forvirtual allocation, and they define the usage policy; i.e.,whether they should be scratch registers or registers thatare preserved across calls. The Assembler assigns somedefault families that many assembly programmers are likelyto use, including integer, floating point, branch, andpredicate. Assembly programmers can also isolateregisters of the same type in subfamilies. For example, auser-defined family may include all local registers of aprocedure.

Each symbolic name used in a procedure, called a virtualregister, belongs to one of the register families. Theassembly language allows redefinition of virtual registers’names, which is convenient when used in preprocessormacros.

VRAL analyzes the control flow graph of the procedure,and it calculates the registers’ live ranges. An accuratecontrol flow graph is very significant for this analysis.The Assembler provides appropriate directives to specifythe targets of indirect branches and additional entrypoints. In order to find a replacement for each symbolicname, VRAL applies standard graph-coloring techniques.

The heuristic function used for allocation prioritiesconsiders both the results of the preceding analysis andthe architecture constraints of registers’ usage. Severalphysical registers may replace one symbolic name, andone physical register may be reallocated and utilized forseveral different symbolic names.

.proc foofoo:: alloc loc0=ar.pfs,2,8,0,0.vreg.safe_across_calls r15-r21.vreg.safe_across_calls [email protected] p6-p9.vreg.family LocalRegs,[email protected] LocalRegs,X,Y,Diff

mov loc1=b0add X=in0,r0add Y=in1,r0 ;;

.vreg.var @pred,GT,LEcmp.gt GT,LE=X,Y ;;(GT) sub Diff=X,Y(LE) sub Diff=Y,X ;;

.vreg.redef GT,LEmov r8=Diffmov ar.pfs=loc0mov b0=loc1br.ret.dptk b0

.endp foo

Example 8: Code with virtual register

Consider the code in the Example 8 above. Starting with.vreg.safe_across_calls and .vreg.allocatable directives,we define the registers that are available for allocation.We then use the .vreg.family directive to define a familyof virtual registers that will only be allocated from the localregisters. We then define the virtual registers themselvesand declare them to be part of the local registers’ familydefined earlier using the .vreg.var directive. The codeitself then uses virtual registers X and Y instead of directlynaming physical registers. The example also illustratesthat virtual registers can be defined in the middle of thecode, and then undefined with the .vreg.redef directive toallow reuse of symbolic names (used most frequently inmacros).

All symbolic names defined with the directive .vreg.var arereplaced with physical registers assigned for allocation bythe directives .vreg.allocatable and.vreg.safe_across_calls. For this simple example, in thecurrent version of the Assembler, the registers chosenwere X=R37, Y=R38, GT=P6, and LE=P7.

In order to effectively use VRAL, we plan to emit theallocation information to allow debugging using symbolicnames. This enables the debugger to show the value of

Page 37: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

Assembly Language Programming Tools for the IA-64 Architecture 5

the symbolic name, even if the value is represented bydifferent registers in different parts of the code. Also, byemitting the allocation information, code optimizationswithout the allocation constraints will be enabled.

THE IA-64 ASSEMBLY ASSISTANTIn order to further assist IA-64 assembly developers, wedesigned and implemented a unique developmentenvironment to have the following:

1. a tool to reduce the steep learning curve for IA-64assembly programming and to introduce IA-64architecture using assembly programming

2. a user friendly environment for IA-64 assemblydevelopment

3. an environment for analyzing and improving theperformance of assembly-code fragments

The Assembly Assistant delivers a comprehensivesolution for assembly code developers, assemblylanguage-directed editing, tools that aid the creation ofnew code, error reporting, static performance analysis, andmanual and automatic optimizations.

Other needs such as debugging or run-time performanceanalysis will be addressed when the Assembly Assistantis integrated with other tools that supply these features.

The next sections describe the Assembly Assistant’sediting, assembling, and analysis capabilities in detail andexamine the unique features that the Assembly Assistantprovides to IA-64 assembly programmers.

Editing and AssemblingThe Assembly Assistant provides syntax-sensitivecoloring that includes all components of assembly code:directives, instructions, comments, templates, andbundling. Every valid instruction format (includingcompleters) is colored to mark it visually as a validinstruction.

Figure 1: Source code window

The IA-64 instruction set is very rich, and the samemnemonic may be used in a variety of instructions when

combined with different types of operands. TheAssembly Assistant provides an Instruction Wizard tohelp assembly programmers select the appropriateinstruction with the right set of completers and operands.It allows assembly programmers to choose betweeninstructions in different variations, and it provides atemplate to select the operands and activate on-line helpabout the instruction. The example in Figure 2 illustrateshow the instruction wizard allows you to choose a specificld4 form (step 1) and then easily apply the correctcompleters (step 2). The Help includes some informationfrom ref [1], ref [5], and the IA-64 Assembly LanguageReference Guide.

Page 38: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

Assembly Language Programming Tools for the IA-64 Architecture 6

Figure 2: Instruction Wizard

Many assembly programmers need an easy way tointerface assembly with other high-level languages suchas C* and C++*. Procedures written in assembly mustadhere to the IA-64 Software Conventions in order toexecute correctly. The Assembly Assistant will generatethe procedure linkage code given a high-level language-like function prototype. The Procedure Wizard generatesa procedure template with information that assemblyprogrammers can use to access input parameters andresults (see Figure 3).

Figure 3: Procedure Wizard

The goal of the Auto Code Wizard is to provide an optionto retain, customize, and reuse favorite code templates.An example of such a template is the code for integermultiplication. (It is provided as an example in the tool.)The IA-64 architecture does not have a multiplicationinstruction for general registers so the instruction forfloating-point registers must be used. Assemblyprogrammers could write an Auto Code template thatmoves values from general to floating-point registers,multiplies them, and moves the result back to a generalregister.

In general, using the source code editor together withwizards and the context-sensitive help provides a rich setof customizable tools to help both beginners andexperienced IA-64 assembly programmers.

While browsing errors after compilation is a common taskin all development environments, the Assembler identifiesa special set of errors called dependency violations (seeabove). These errors can produce treacherous results,

and special care is required while treating them. Thedifficulty is that these errors involve two instructions thatmay be distant from one another. When the error in theerror view at the bottom of the screen is highlighted, itdisplays connected pointers pointing to the offending pairof instructions in the source code window (see Figure 1).

ANALYSIS WINDOWThe Assembly Assistant provides a static analysis as aguide to help assembly programmers improveperformance. In this section, we discuss the analysiswindow. This window helps assembly programmersunderstand, browse, analyze, and optimize their assemblycode.

The Assembly Assistant uses static performance analysison a function-by-function basis, without any dynamicinformation on the program behavior (such as registervalues and memory access addresses). Fast performancesimulation of instruction sequences is used in order toobtain the performance information.

The main performance information displayed in theanalysis view is cycle count and conflicts. The cyclecount is the estimated relative cycle number in which theinstruction enters the execution pipe in the performancesimulation. This number is relative to the beginning of thefunction or selected block. Usually the execution pathdoesn’t utilize the full capacity made possible by the IA-64architecture. Conflict indicators in the stall columns showthe reasons for extra cycles in the execution path andprocessor stalls.

Assembly programmers analyze the conflicts and modifytheir code accordingly by manually moving an instructionearlier or later in the code sequence, selecting a differentinstruction sequence, etc. Automatic optimizationattempts to find the best instruction scheduling.Optimizations are discussed in a later section.

As shown in Figure 4, the assembly source is presentedalong with line numbers, cycle counts, and conflicts, asdiscussed earlier. Conflicts are highlighted with differentcolors for each conflict, so assembly programmers caneasily identify which instructions are involved in eachconflict. In Figure 4, the cycle counts are based on ahypothetical machine model.

Another type of information is division of the instructionstream into several types of groups. The most interestingare bundles that are fetched together from memory andinstruction groups, which assembly programmers defineas candidates for parallel execution. Two additionalgroups for more advanced assembly programmers areissue groups (instructions that execute simultaneously)and basic blocks (for control flow analysis).

Page 39: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

Assembly Language Programming Tools for the IA-64 Architecture 7

Figure 4: Analysis window

Run-time information is not available during staticanalysis. The Assembly Assistant provides informationabout execution flow and predicate values. These valuescontrol the simulated execution path and may activate ordeactivate instructions. As illustsrated in Figure 4, eachpredicated instruction is preceded by a checkbox. Markingthe checkbox signifies that the qualifying predicate is true,and the instruction executes. This interface eases analysisand optimization of predicated blocks. The AssemblyAssistant provides the means to control predicate values.In the future, the Assembly Assistant might also calculatepredicate relations for use in the static analysis , andmarking a single predicate as true or false willautomatically determine the values for all of the predicatesthat are related to it .

The Assembly Assistant gives assembly programmersmore extensive control. Assembly programmers canspecify whether or not the branch is taken, and they canset the probability of taking the branch in the simulation.The Assembly Assistant uses this probability whensimulating loops: it provides assembly programmers withthe approximate time of loop execution together witha-priori knowledge of possible execution paths.

The analysis window provides more than just loopvisualization. Assembly programmers may select thenumber of iterations to simulate. Selecting a single

iteration provides performance information and shows anyconflicts between the main body of the loop and theprologue code. Selecting two iterations also displaysconflicts between the head and tail of the loop codesection. Selecting more than two iterations provides theapproximate execution time of the loop calculated bybranch probabilities, as described above.

The assembly module may contain more than onefunction. To help assembly programmers navigate in theanalysis window, the window is split into two panes, justlike the Microsoft Explorer∗ window. The left tree panecontains a list of all the functions in the module, while theright pane displays one function’s analyzed code.Clicking on a function name or icon in the tree panedisplays the analysis of the selected function. Assemblyprogrammers work with one function at a time, viewing inthe tree pane the list of labels in the active function. Thisallows easy navigation inside the function.

We have described above how assembly programmers cananalyze assembly code. But analysis is useless ifassembly programmers cannot apply their insights to

∗ Other brands and names are the property of theirrespective owners.

Page 40: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

Assembly Language Programming Tools for the IA-64 Architecture 8

improve the code. The Assembly Assistant allows themto do this.

Assembly programmers may want to move stallinginstructions and solve conflicts. The Assembly Assistantprovides a simple drag-and-drop interface so thatinstructions can be moved manually. It displays theinstruction’s move boundaries as defined by datadependencies. Assembly programmers can drop theinstructions into their new position. Assemblyprogrammers can also apply automatic optimizations thatreschedule the instruction stream to improve performance.

After completing code optimization in the analysiswindow, the Assembly Assistant generates new,improved code in a new source window.

OPTIMIZATION AIDES IN THE IA-64ASSEMBLY ASSISTANTWhen optimizing assembly code, a tool such as theAssembly Assistant can generally use conventionalcompiler techniques. However, a key challenge tooptimization is code analysis , since some of theinformation visible to the compiler does not exist or is hardto infer from such low-level representations. Examplesinclude branch targets for indirect branches, callingconventions, memory disambiguation, and aliasing.

This information is critical for code speedup andmaintenance of correct code. An assembly optimizer alsohas no choice but to deal with every feature of thearchitecture, whereas a compiler might choose not to usecertain features or instructions; for example, systeminstructions.

The first and simplest solution is to leave optimizations tothe assembly programmers but still help them withavailable analysis data and IA-64 processor-specificinformation. An advanced and friendly user interfaceenables assembly programmers to easily perform theoptimizations.

The next level of automation requires assemblyprogrammers to provide missing information. A user-friendly interface allows assembly programmers to interactwith the optimizer to define branch targets and more.Using this information, a control flow graph is created andanalyzed. Assembly programmers can also provideprogram behavior information such as branchprobabilities, which direct the optimizer to bias itsoptimizations accordingly.

Automatic optimization is also used, but as mentionedearlier, it is somewhat limited due to the conservativeapproach to assembly-level optimizations.

Manual OptimizationAnalysis provides many hints for manual optimization.The analysis of register live ranges helps assemblyprogrammers better use the registers. The analysis of dataflow provides the assembly programmers with suspecteddead code, and it detects the use of registers that were notinitialized in the analyzed code. Data flow analysis is alsohelpful when trying to attain optimal scheduling. Heightreduction (the process of reducing the controldependence, for example as in ref. [4]) and strengthreduction (ref. [3] p.435) are easier for assemblyprogrammers to handle when all the dependency chain isanalyzed automatically.

For software pipelined loops, automatic tracking of therotating registers used in the loop helps assemblyprogrammers to write modulo-scheduled loops. This canalso greatly simplify modification of the code.

It is difficult to keep track of other machine resources(such as control registers, functional units, and more). TheAssembly Assistant can automatically keep track ofmachine resources, and it can warn assembly programmerswhen machine resources are insufficient for the code. TheAssembly Assistant shows an assembly programmer thepenalty incurred by the code, and it suggests methods toovercome the limitations inherent in the microarchitecture.

To aid in speculation, when moving a load beyondambiguous memory references or control dependencies,the Assembly Assistant shows assembly programmers theprobable costs and benefits of the speculation. Loadinstructions that inhibit scheduling can also be identifiedand they can be suggested to assembly programmers aslikely candidates for speculation.

Automatic OptimizationWhile assembly programmers are certainly capable ofperforming most optimizations that can be doneautomatically, other optimizations are difficult. This is dueeither to complexity or tedium. For example, schedulingthe instructions for optimal performance is mostly aproblem of brute force. Experienced assemblyprogrammers who are also familiar with all themicroarchitecture details can tweak the scheduling to getthe best performance, but sometimes the only way to getthe best scheduling is to simply try out all of thecombinations. The testing would have to be repeatedafter every source code change. This is clearly a missionfor an automated tool.

In automatic optimization mode, the Assembly Assistantschedules the instruction stream in a top-to-bottom issue-group-scheduling approach. Instructions are scheduledaccording to internal heuristics, taking into account critical

Page 41: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

Assembly Language Programming Tools for the IA-64 Architecture 9

path, code size, instruction priorities, utilization of machineresources, and more. Many possible templates arechecked against the heuristics, and the best one is chosenfor the issue group. The code in the example below willexecute significantly slower than the same code afterautomatic optimization (~25%). Analyzing the example, wecan observe that the instructions at lines 8 and 9 of theoriginal code were moved up, and instructions on lines 3,4, and 7 were moved down. This schedule was chosenconsidering the latencies of various functional units andin order to prevent unnecessary stalls .

Original code:

1 { .mii2 ld4 r4 = [r33]3 add r8 = 5, r84 mov r2 = r56 ;;5 }6 { .mii7 add r32 = 5, r48 mov r3 = r33 ;;9 add r33 = 4, r3 ;;10 }11 { .mib12 cmp4.ltp6,p7=r2,r3313 nop.i 99914 (p7) br.cond.dpnt START15 }

Code after automatic optimization:1 { .mii2 ld4 r4 = [r33]3 mov r3 = r33 ;;4 add r33 = 4, r35 }6 { .mii7 mov r2 = r568 add r8 = 5, r8 ;;9 add r32 = 5, r410 }11 { .mib12 cmp4.ltp6,p7=r2,r3313 nop.i 99914 (p7) br.cond.dpnt START15 }

Example 9: A small code sample before and afterautomatic optimization

Optimal utilization of machine resources for parallelexecution is very important, and actual results show thateven code written by experienced assembly programmerscan gain a speed-up of 6 – 8% from the AssemblyAssistant’s automatic scheduling. In the case of codewritten by inexperienced assembly programmers, the gainis likely to be much higher.

Assembly programmers can choose from variousoptimization schemes, actually changing the heuristicsused. For example, scheduling for the smallest code sizemight incur significant penalties due to overloading ofmachine resources. By default, automatic optimizationtries to address the issues most challenging to a humanassembly programmer. It optimizes for better utilization ofmachine resources rather than concern itself with codesize. However, this might result in inflated code and affectthe instruction cache. Enabling various optimizationschemes offers assembly programmers greater controlover the automatic optimizations, and it allows expertassembly programmers to take advantage of automaticmodes without losing flexibility.

Even for code that is not performance-critical, but still hasto be written in assembly, automatic optimization can bevaluable. It can be used either to speed up performance orto pack the code more tightly.

FUTURE ENHANCEMENTS FOR THEASSEMBLY ASSISTANTThe Assembly Assistant is currently used by many IA-64assembly programmers both beginners and experts to tunetheir code. We received many requests for more featuresand enhancements. The requests include a library ofoptimized special-purpose code (for example, integerdivide and floating-point square root), more manual andautomatic optimizations, visualization of registers’ liveranges, etc. We are also looking into integrating theAssembly Assistant with other programmingenvironments such as Microsoft Visual Studio∗ and theIntel® VTune performance analyzer.

CONCLUSIONIt is strongly recommended that compilers be used in orderto generate highly optimized code for the IA-64architecture. The use of compilers also guaranteesscalability and portability for future IA-64implementations. However, we recognize the need ofsome developers to continue to use assembly code in theirapplications. We attempted to outline the difficultiesfaced by assembly programmers when writing for the IA-64 architecture, and we presented tools to alleviate orovercome these difficulties. The tools presentedcontribute to the following goals:

• Quickly familiarize assembly programmers with thenew IA-64 architecture.

∗ Other brands and names are the property of theirrespective owners.

Page 42: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

Assembly Language Programming Tools for the IA-64 Architecture 10

• Program in IA-64 assembly with relative ease.

• Provide a comprehensive development environmentfor assembly programming.

• Analyze and optimize assembly programs to utilizeIA-64 unique features for optimal performance.

REFERENCES[1] IA-64 Application Developer’s Architecture Guide,

Order number 245188.

[2] Analysis of Predicated Code, HPL-96-119.

[3] Steven S. Muchnick, Advanced compiler design andimplementation, Morgan Kaufmann, San Francisco,California, 1997.

[4] “Control CPR: A Branch Height Reduction Optimizationfor EPIC Architectures, ” in Proceedings of the ACMSIGPLAN 99 Conference on PLDI, Atlanta, Georgia1999, pp.155-168.

[5] IA-64 Assembler User’s Guide, Order number 712173.

AUTHORS’ BIOGRAPHIESAdy Tal received his M.Sc. degree from the Technion in1990. He has been working for Intel Israel since 1996, andhe leads the Optimizing Libraries development team. Hewas a member of the Assembler and Assembly Assistantdevelopment teams and has wide experience with allaspects of IA-64 architecture features and assemblyoptimization techniques. His e-mail is [email protected].

Vadim Bassin received an M.A. degree in computing fromthe Belorussian Radio-Engineering Institute in 1987.While he studied mostly hardware, he has worked only insoftware. He spent six years programming in real time andworking on image processing in the Belorussian ScienceAcademy and a small Israeli-American company beforeswitching to network management. He finally foundhimself working at Intel on GUI tools for programmers. Hise-mail is [email protected].

Shay Gal-On received his B.A. degree from the Technionin 1997. Since then he has lingered at Intel Israel, mainlyworking on assembly optimization techniques and bitmanipulations’ libraries. Professional interests run fromoptimizations to security. His e-mail [email protected].

Elena Demikhovsky graduated from the BelorussianRadio-Engineering Institute with an M.Sc. degree in 1991.Since 1994, she has worked at Intel Israel as a softwareengineer. Elena has wide experience in the development oftools, especially the Assembler, for both the IA-32 and IA-

64 architectures. Her e-mail [email protected].

Page 43: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Porting Operating System Kernels to the IA-64 Architecture for Presilicon Validation Purposes 1

Porting Operating System Kernels to the IA-64 Architecturefor Presilicon Validation Purposes

Kathy Carver, IA-64 Processor Division, Intel CorporationChuck Fleckenstein, IA-64 Processor Division, Intel CorporationJoshua LeVasseur, IA-64 Processor Division, Intel CorporationStephan Zeisset, IA-64 Processor Division, Intel Corporation

Index words: presilicon, validation, COSIM, postsilicon, Mach, Linux

ABSTRACTTo provide additional vehicles for presilicon validationand postsilicon debug of the Intel Itanium™ processor,we ported two operating system kernels to the IA-64architecture. The Mach3* microkernel was ported first,followed by the Linux∗ 2.2.0 kernel, and these havehelped track the overall health of the Itanium™processor’s RTL model for the last two years. Theseoperating system (OS) kernels also helped presiliconperformance analysis and compiler-generated codeanalysis.

The Mach3 kernel (the IA-64 port was called Munsterinternally) was ported because it contained featuressimilar to Microsoft Windows NT∗, such as tasks,threads, interprocess communication (IPC), andsymmetric multiprocessing (SMP). Mach3 allowed us toexercise parts of the Itanium processor’s model in asimilar way to Windows NT, but at a reduced scale andwithout device support.

Linux (the IA-64 port was called IPD-Linux) was portedbecause its source is readily available and 64-bit clean, itis highly configurable, and it would exercise the model ina different way than Mach3. We started with a released2.2.0 version of the source and ported the kernel using anon-GNU C Compiler (GCC). The difficulty of portingthe Linux kernel without GCC made the task morechallenging.

Besides porting the architecture-specific portions of thekernels, modifications were necessary to both kernels toremove certain dependencies on external devices and ∗ Other brands and names are the property of theirrespective owners.

BIOS initialization. Also, the OS initialization pathsexecuted prior to user-level programs had to be shortenedto accommodate the simulation speed of the RTLenvironment. The kernels had to be extremelyconfigurable in order to run in diverse simulationenvironments.

Both kernels were tested from processor reset to user-mode code execution to validate the significant parts ofthe RTL that an operating system would exercise duringthe boot process. Kernel initialization, virtual memorymanagement, context switching, trap handling, systemcall interfaces, and user-mode context paths were allexercised on the actual RTL model. This effortuncovered several errata in the RTL model and in the IA-64 tools (such as the compiler and linker). It alsoprovided us a model regression sanity check for each newRTL release. We believe this presilicon effort wasinstrumental in allowing Windows NT to boot just daysafter first silicon. In this paper, we discuss the porting ofkernels to the IA-64 architecture for presilicon operatingsystem validation.

INTRODUCTIONOne of the major goals for early silicon is to boot acommercial operating system (OS) shortly after thearrival of first silicon. In order to increase the probabilityof success we decided to use an operating system kernelto validate the processor in addition to usingconventional presilicon testing methods. Traditionalmicroprocessor validation includes feature validation,unit testing, and random instruction testing. Thepotential shortfall of these methods is that they oftendon’t exercise the processor in the same environment inwhich it is later expected to run. In other words, anoperating system programmer often thinks of a different,

Page 44: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

Porting Operating System Kernels to the IA-64 Architecture for Presilicon Validation Purposes 2

but legal way, of exercising processor functionality thatmight not be covered by conventional methods ofvalidation. Therefore, running an operating systemkernel to exercise key OS-related features presiliconturned out to be a worthwhile effort. The followingsections detail the issues we had to resolve during thiseffort.

RUNNING AN OPERATING SYSTEM INTHE PRESILICON ENVIRONMENTThe two main constraints the presilicon environmentimposes on an operating system are as follows:

1. The simulation speed of the RTL simulatoreffectively restricts test runs to a few million cyclesof the simulated processor clock, and it causesturnaround times in the order of multiple days.

2. The simulated environment lacks devices.

The constraint in simulation speed had two majorconsequences for porting. First we had to reduce thenumber of instructions executed by the kernel during itsinitialization sequence. Second, we had to test the kernelthoroughly on functional simulators before committing itto a run on the RTL model.

To cope with the slow simulation speed, we wrote a toolthat allowed us to run our kernel up to an arbitrary pointin the functional simulator and then to continuesimulation on the RTL model from that point on. Toaccomplish this, our tool read the saved architecturalstate from the functional simulator and used it togenerate a sequence of IA-64 instructions that restoredthe architecture to this state. Then it read the memoryimage saved from the functional simulator and used it togenerate a new binary, with the state restoration sequenceplaced at the processor reset vector. When we ran thisnew binary on the RTL model, the processor wentthrough the state restoration sequence and then continuedat the point where the state was saved on the functionalsimulator. Our two primary uses of this tool were (1) toskip the kernel initialization sequence and have the RTLsimulation start directly with the execution of user-modeprograms, and (2) to improve the latency for running thekernel initialization sequence on the RTL model bysubdividing it into multiple parts and running the partsin parallel. To minimize the danger of processor erratabeing obscured by cold caches, we allowed for heavyoverlap between the parts.

Reducing the Instruction CountOur initial profiles of the kernel startup sequence for bothMunster and IPD-Linux∗ showed that a large portion oftime was spent in the routines for zeroing and copyingmemory, and in the initialization of a few key datastructures, the most prominent being the structures usedfor virtual memory management. Our solution, therefore,included the following:

• Optimize the routines for zeroing and copyingmemory (bzero/memset, bcopy/memcopy).

• Reduce the amount of physical memory presented tothe kernel. This reduced the time spent initializingpage management information.

• Add delayed initialization for some kernel datastructures.

These changes, however, did not reduce the functionalityof the kernels.

One example of how we modified the Mach kernel toreduce the instruction count during kernel initializationwas through changes to the zone allocation code. Mostmemory allocation for kernel data structures is donethrough zones, which act as buckets for fixed-size blocksof memory (zone entries) whose typical size ranges froma few bytes to a few hundred bytes. When an entry wasallocated from a zone that had no entries in its free list,the free list was replenished by allocating one page ofmemory and splitting it up into zone entries, all linkedtogether in a free list. The number of instructionsrequired for doing this initialization for dozens of zoneswas quite high when keeping the speed of RTLsimulation in mind. Therefore we changed themechanism for replenishing a zone. Instead ofimmediately entering a whole page into the free list, a“free space” pointer was kept. The pointer was initiallyset to the newly allocated page, and it was used to carveout new zone entries one by one at the time they wereactually needed.

Testing in Different EnvironmentsFigure 1 shows our available simulation environments.Except for device support, matching environments wereavailable on the functional and the RTL simulator. Eventhough no device support was available on RTL, we stillneeded to test our kernels with devices on the functionallevel to prepare for postsilicon.

∗ Other brands and names are the property of theirrespective owners.

Page 45: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

Porting Operating System Kernels to the IA-64 Architecture for Presilicon Validation Purposes 3

Our kernels had to run in several different simulationenvironments. We ported the kernels so they could beconfigured with or without devices and run with orwithout external interrupts, etc. Our kernels wereflexible enough to run in simulation environments thatranged from just one processor with memory to a fullsimulation of a multiprocessor platform with devices.

1 Processor

Memory

n Processors

Memory

Chipset

n Processors

Memory

Chipset

Devices

Functional

& RTL

Functional

& RTL

Functionalonly

Figure 1: Available simulation environments

We used two functional simulators, Giza and SoftSDV[8], to test and debug the presilicon operating systemkernels before running in RTL. Both simulators wereutilized in our development process in order to debugcode quickly. Giza was also designed to be used as achecker against the RTL model, so we always ran ourcode through it before running in RTL. Since theSoftSDV simulator is already described in “SoftSDV: APresilicon Software Development Environment for theIA-64 Architecture” in this issue of the Intel TechnologyJournal, we only describe the Giza simulator.

Giza is built around an instruction accurate softwaresimulator for Itanium processor’s ISA (Sphinx). Itsupports critical implementation specific registers,SAPIC, a non-blocking memory hierarchy (TLB+caches)that handles both synchronous and asynchronous trafficbetween the CPU and the external sub-system, andmultiple CPU instances (multiprocessor).Implementation-specific registers are modeled to supportfirmware execution. SAPIC, non-blocking memoryhierarchy, and multiprocessor (MP) are modeled tosupport characteristic subsystem traffic for typical IA-64platforms. A functional accurate software model thatmimics the Itanium processor’s front-side-bus (FSB) isdesigned to schedule CPU events and dispatch theresulting transactions to and from memory and I/Osubsystems. Software models for the Itanium processor’schipset and Itanium processor’s standard devicesrepresent the latter.

By using functional simulators, we avoided wastingprecious RTL cycles that could be used by conventionaltests. We began with uniprocessor (UP) versions of thefunctional simulators and OS kernels. Once we passed

the UP functional simulator test, the code would run onthe RTL. These jobs often took over a million cycles tocomplete so the ramifications of simple code mistakeswere great and had to be eliminated before being run onthe RTL models. Once the kernels passed a UPfunctional and RTL simulator run, they were moved ontothe multiprocessor path. Each symmetricmultiprocessing (SMP) version of the kernel wasdebugged via a functional simulator. The MP RTLenvironment, known as COSIM, allowed modeling ofmultiple IA-64 RTL processor models, chipset models,PCI busses, and external interrupt controllers. Thisenvironment allowed us to exercise SMP kernels onmany of the platform components before silicon wasavailable, which taught us valuable lessons anduncovered errata that were not uncovered duringconventional methods of testing.

Since operating system code is not “self checking,” theMunster and IPD Linux kernels were run in RTL with anRTL checker running at the same time. The RTLchecker is a functional simulator that runs in conjunctionwith the RTL simulation and compares the architecturalstate after the retirement of each bundle. If a statemismatch occurs, then an error condition is flagged, andfurther analysis can be done to isolate the root of theproblem.

Porting ChallengesThere were many challenges in porting the kernels to runpresilicon in RTL. We encountered a number of toolproblems since we were on the leading edge as far asrunning code with the actual RTL model is concerned.The early tool sets often worked for running code in thefunctional simulator, but had problems with generatingcorrect code for running in the RTL simulator. We hadto write a utility called the AfterBurner to post-processcompiler-generated assembly code and to fix problemsthat were preventing the code from running in RTL.

During the project, the compiler-generated code quality(correctness and performance) improved, as did themodeling of the architecture by the functional simulators.However, for some sequences of legal C code, thecompiler produced semantically incorrect as well asarchitecturally incorrect code. In certain cases, due to thesequential nature of the functional simulator,architecturally incorrect code would appear to functioncorrectly. In other cases, architecturally incorrect hand-written code would appear to execute correctly within thefunctional simulator (e.g., missing serializationinstructions required by the architecture wentundetected).

Page 46: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

Porting Operating System Kernels to the IA-64 Architecture for Presilicon Validation Purposes 4

Benefits of Using Two Different KernelsThe benefit of porting multiple kernels to The Itaniumprocessor was the ability to share some of the low-levelstart-up, trap handling (TLB faults, etc.), bcopy, port IOusage, and other code between the two kernels. It took usroughly two weeks to obtain a linkable Linux IA-64kernel, and much of that time was spent onaccommodating a non-GNU [11] C compiler. Much ofthe low-level code was already done from the Mach* portand just had to be merged into the Linux source tree.Another benefit of a second port was that it allowed us toredesign some of the code to make it cleaner and moreefficient.

Porting two kernels allowed us to test some of the IA-64Instruction Set Architecture in a slightly different way.We achieved a broader validation of some features suchas Instruction Level Parallelism (ILP), speculation,predication, use of the large register files, the RegisterStack Engine (RSE), and advanced branch architecture.Our “common trap handler,” the common path for savingand restoring state when entering/exiting the kernel, forthe IPD Linux was very different from the Mach* versionin both the design of the operating system and in the areaof performance. As a result, the processor was exercisedin an alternate way.

Both kernels supported Seamless mode, which is theability to run IA-32 binaries on top of an IA-64 operatingsystem kernel. We ran IA-32 user-mode programs on thekernels in presilicon RTL as another validation test.

ISSUES SPECIFIC TO PORTINGMUNSTERMach3 [6] was the first kernel to be ported so thearchitecture-dependent code had to be written fromscratch. We received some example code from otherIntel groups, but some of it didn’t fit very well into theMach3 architecture. One of the biggest issues that weencountered was Mach3’s ability to come into kernelmode on one stack and leave on another [15]. Thisadded complexity to the trap handler due to the fact thatall of the required IA-64 state had to be saved on to theProcess Control Block (PCB) and restored into the newstack state. In Mach*, instead of a static assignmentbetween threads and kernel stacks, the assignment isdynamic, and a thread that blocks in kernel context whilewaiting for some event can hand off its stack to thethread that is next in line. This is beneficial in terms ofcache locality, but it complicates handling of the registerstack engine because the kernel backing store is part ofthe kernel stack. As such, it does not persist between thetime a thread enters the kernel and the time it returns.When entering the kernel from user mode, we first had to

flush the dirty RSE registers into a dedicated area in theprocess control block before switching to the kernelbacking store. Then, when we returned to user mode, wehad to load the flushed RSE registers from the processcontrol block into the physical register file beforeswitching to the user backing store.

There were also LP-64 issues in the Mach3 source codewhere assumptions were made that ints, longs, andpointers were all the same size. This caused pointers tobe truncated in some cases.

The Munster kernel port involved IA-64 start-up code,fault handling, TLB handling, context switching, systemcalls, interrupt handling, interprocess communication,LP 64-bit clean efforts, and user-mode libraries. We alsohad to port the Mach* build tools to UnixWare∗ beforethe Mach3 kernel could be built.

ISSUES SPECIFIC TO PORTINGIPD-LINUXLinux∗ was ported as another presilicon operating systemvalidation kernel. (The source for the Trillian∗ kernel,which was demonstrated during the 1999 IntelDeveloper’s Forum was not available when this portbegan.) The main issue with porting Linux to IA-64presilicon was the lack of a complete IA-64 GNU Ccompiler [13] at the time we began the port. We used theIntel Electron C compiler to compile the kernel. Thisrequired us to conditionally compile around the heavyusage of GNU C extensions [12] within the Linux kernel.Extensions such as inline C and inline assemblyfunctions are not supported by the Electron compiler sothis made the port more difficult than if we had a GNU Ccompiler available. The majority of our work in thisport was in the two architecture-dependent directoriesthat we added (include/asm-ia64 and arch/ia64). We alsoadded an inline directory under arch/ia64 as a substitutefor those routines that are normally inlined by the GNUC compiler. Since we didn’t have access to a GNU Ccompiler early in our development cycle, a basic user-mode shell, Josh, was written and used to launch Linuxtests for validation purposes. Without a full GNU Ccompiler it proved very difficult to port the GNU CLibrary (GLIB C), which forms the basis for the full setof user-level shells and commands. Attempts were madeto port GLIBC without the GNU C compiler, but theywere unsuccessful in presilicon.

∗ Other brands and names are the property of theirrespective owners.

Page 47: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

Porting Operating System Kernels to the IA-64 Architecture for Presilicon Validation Purposes 5

ISSUES SPECIFIC TO SUPPORTINGPRESILICON PERFORMANCEANALYSISThe Linux port was also chosen as a vehicle to facilitatearchitectural performance research. The study ofperformance phenomena related to the microarchitecture,architecture, operating system, software, and toolsrequired an open source workload. Besides offering acomplete source, Linux offers SMP support, 64-bit cleancode, runtime C library, and a kernel designed to workwith multiple architectures. All of these features wereintegral to quickly satisfy the group’s goals.

To automate data collection and analysis, the Linuxkernel was augmented with many hooks to communicatewith the simulation infrastructure. The hooks reportedinformation about the internal state of the kernel, whichwere recorded within an event trace. The simulator andpost processing tools correlate the information witharchitecture events, debug information, and compilerannotations. The kernel was also instrumented tosupport efficient branch and data trace collection whenrun on silicon.

To integrate the Linux kernel with the trace environment,and to support the silicon trace collection, a commonfeature set was added to the kernel. The kernel additionscollect information about context switches, processcreation and termination, and modifications to theaddress space (such as through mmap() or munmap()).This type of data collection has proven useful for otherdomains such as checkpoint/restore (the EPCKPTproject), and kernel profiling (Intel Vtune performanceanalyzer [14]).

PRESILICON RESULTSBy running the operating systems presilicon, we foundseveral unique RTL errata that would have affectedcommercial operating systems postsilicon. Errata werefound in operating system-specific code, compiler-generated code sequences, speculative execution,platform interrupt paths, and tools. Other testingmethods did not find these errata. Therefore, since thiswas a new architecture, it was worthwhile to incorporatean operating system kernel test presilicon to rule outmajor issues and to provide an indicator of the overallRTL model health.

Operating System-Specific ErrataThe first errata we uncovered was related to the returnfrom interrupt (rfi) instruction that is used by operatingsystems to return from an interruption/fault. The erroroccurred when the operating system start-up code used

this instruction in the process of switching from physicalmode to virtual mode, and the new instruction addresswas only valid in the new addressing mode. In checkingthe validity of the target address of the rfi instruction, theprocessor was using the previous (physical) addressingmode instead of the new (virtual) one, generating anexception. This prevented our kernels from booting andcould have affected other operating system kernels likeTrillian Linux and Windows NT∗.

Errata Uncovered by Compiler-Generated CodeSequencesPresilicon OS runs uncovered an error in the Itaniumprocessor’s RTL where certain combinations of floating-point instructions produced an incorrect result becausesequences of multiply-accumulate instructions withregister dependencies were not properly stalled. Thesignificance of this error is that this exact instructionsequence is used by the C compiler to implement theinteger modulo operation. Since C is the primarylanguage for software development, this error would havebeen encountered by most applications running on theprocessor.

Code Example:

int i, x, y ;

i=x%y ;

where the generated code would contain a sequence likethe following (note the pseudo registers for the example):

fma fz=fa,fb,fc;;fma fw=fz,fk,fj

The result of the first fma is not available for severalcycles, so the second fma instruction should have beenstalled until fz was available.

Speculative Execution ErrataThe Itanium processor does extensive branch predictionand speculatively executes instructions at the predictedbranch target long before it is known if the branch will betaken. We found a problem with a conditional call to asubroutine, where the subroutine was short enough toexecute a return instruction before it was known if theconditional call should have been executed. This causedthe processor to permanently commit some of the statechanges caused by the return instruction, even if theconditional call was incorrectly predicted and was notsupposed to be executed.

∗ Other brands and names are the property of theirrespective owners.

Page 48: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

Porting Operating System Kernels to the IA-64 Architecture for Presilicon Validation Purposes 6

Example:

// where p3 is false

(p3) br.call b0 = foo

foo:

alloc …

mov … ;;

br.ret b0

In this pseudo code example the predicate p3 was false,but the code at foo was speculatively executed, and itsresults were erroneously committed.

Platform Interrupt ErrataSeveral interrupt-related errata were uncovered bywriting small tests that exercised the interrupt pathsutilized by an operating system on a typical Itaniumplatform. This testing was accomplished in a simulationenvironment (COSIM) that combined multiple Itaniumprocessor models, the chipset model, and platformcomponent models such as the external interruptcontroller model. This allowed exercising the path froma simulated device to the processor.

Unique errata were uncovered by generating interrupts inthe modeled environment, causing the execution ofspecific interrupt flow paths. One of the interrupt errorswas uncovered by redirecting interrupts to a particularprocessor based upon the priority of the processor in amultiprocessor simulation.

Tools ErrataDuring the development and testing of Munster and IPDLinux, many bugs were found in software developmenttools such as compilers and linkers, and also in varioussimulators used to run the kernels. Munster and IPDLinux were run in every simulator that was available tous and were crucial in detecting multiprocessorfunctional simulator errors.

POST-SILICON RESULTSThe great advantage of using a kernel for postsilicondebug that has been validated in the presiliconenvironment is that it removes potential software bugsfrom the list of unknowns during initial bring-up.

The extensive presilicon testing allowed the bring-upteam to concentrate on mechanical, electrical, and siliconissues and provided them with a metric for assessingbring-up progress.

First Itanium processor’s silicon was very healthy, so ourkernels were able to run without modification as soon asinitial platform issues were resolved and a stableoperating range for the processor was found.

CONCLUSIONTesting an RTL model using an operating system kernelconsumes many RTL cycles. The tests typically run forover one million cycles and take up cycles that could beused by shorter focus tests. This is the reason that ourcode was debugged on a functional simulator beforelaunching the tests on the RTL model. We expected thekernels to boot if the model was healthy and if there wereno infrastructure problems with the testing environment.The advantages of using an operating system faroutweigh the disadvantages. We were able to find erratapresilicon that normally would not have been detecteduntil hardware was available. At that point, the problemscan be difficult to isolate and expensive to fix. Thekernels were also valuable during the first few days ofItanium processor’s postsilicon bring-up. Our kernelswere able to run without modification as soon as thesilicon and platform hardware were stable. This allowedus to use our kernels as an indicator of hardware healthduring the first few days.

ACKNOWLEDGMENTSThese people were always there to support this uniqueproject: Piyush Desai, Andy Pfiffer, Rahul Bhatt, StanSmith, Rumi Zahir, Drew Hess, Tony Porterfield,Moenes Iskarous, Dennis Marer, Ken Arora, and RandyAnderson.

REFERENCES [1] Carver, Fleckenstein, et al., “A System Centric

Reference Model and an OS Microkernel for Pre-Silicon CPU Platform Validation,” Intel DTTC 1998,(internal document).

[2] Alessandro Rubini, “Linux Device Drivers,” O’Reilly& Associates, February 1998, ISBN: 1-56592-292-1.

[3] Linux Kernel Source Archives (best portingreference), ftp://ftp.kernel.org/pub

[4] M. Beck, H. Bohme, et al., Linux Kernel Internals(Second Edition), Addison-Wesley, 1998, ISBN: 0-201-33143.

[5] IA-64 Application Developer’s Architecture Guide,Order Number: 245188-001, May 1999.http://developer.intel.com/design/IA64

[6] CMU CS Project Mach* Home Page,http://www.cs.cmu.edu/afs/cs.cmu.edu/project/mach/public/www/mach.html

[7] OSF Kernel and Server Programming Manuals,http://www.cs.cmu.edu/afs/cs/project/mach/public/www/doc/osf.html

Page 49: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

Porting Operating System Kernels to the IA-64 Architecture for Presilicon Validation Purposes 7

[8] R. Uhlig, R. Fishtein, O. Gershon, H. Wang,”SoftSDV: A Presilicon Software DevelopmentEnvironment for the IA-64 Architecture,” IntelTechnology Journal, Q4 1999.

[9] Gollakota, Naga, “COSIM,” Intel DTTC 1998.

[10] H.P. Messmer, The Indispensable PC HardwareBook, Addison-Wesley, 1997.

[11] GNU’s Not Unix !, http://www.gnu.ai.mit.edu

[12] “Using and Porting the GNU Compiler Collection(GCC), Extensions to the C Language Family”,

http://www.gnu.org/software/gcc/onlinedocs/gcc_4.html#SEC62

[13] “GCC Home Page”,

http://www.gnu.org/software/gcc/gcc.html

[14] “Vtune Performance Analyzer,”http://developer.intel.com/vtune/analyzer/index.htm

[15] Draves, R.P., Bershad, B., Rashid, R. F., and Dean,R.W., “Using Continuations to Implement ThreadManagement and Communication in OperatingSystems,” in Proceedings of the Thirteenth ACMSymposium on Operating Systems Principles, 1991,ftp://ftp.cs.cmu.edu//afs/cs/project/mach/public/doc/unpublished/internals_slides/Continuations/sosptr.ps

[16] Wheeler, Bob, “Porting and Modifying the Mach*3.0 Microkernel,” Third USENIX MACH*Symposium, 1993,ftp://ftp.cs.cmu.edu/afs/cs/project/mach/public/doc/published/porting.tutorial.slides.ps

[17] Rusling, David, “The Linux Kernel,”http://www.redhat.com/mirrors/LDP/LDP/tlk/tlk.html

AUTHORS’ BIOGRAPHIESKathy Carver received a B.S. degree in computer scienceand mathematics from Western Kentucky University in1981. She joined Intel Corporation in 1992 and workedon compiler validation and integration for Intel’sIPSC860, Paragon, and TFLOPS systems. She iscurrently part of the Itanium processor ArchitectureValidation group in IPD. Her e-mail is [email protected]

Chuck Fleckenstein is currently working for the IPDItanium processor validation group. He received an M.S.degree in computer science from Wright State Universityin 1988. His interests include distributed programminglanguages and operating systems. His e-mail [email protected].

Joshua LeVasseur works for the IPD IA-64 Architecturegroup. He currently focuses on system architectureperformance, IA-64 optimizations, andsimulator/analysis infrastructure. His e-mail [email protected]

Stephan Zeisset received an M.S. degree in computerscience from the Munich University of Technology in1994. Since then he has been working on the operatingsystem of the TFLOPS system and its predecessors, witha specialization in distributed memory management.Stephan is currently working on Itanium processorvalidation for IPD. His e-mail is [email protected]

Page 50: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

The Computation of Transcendental Functions on the IA-64 Architecture 1

The Computation of Transcendental Functions on the IA-64Architecture

John Harrison, Microprocessor Software Labs, Intel CorporationTed Kubaska, Microprocessor Software Labs, Intel CorporationShane Story, Microprocessor Software Labs, Intel Corporation

Ping Tak Peter Tang, Microprocessor Software Labs, Intel Corporation

Index words: floating point, mathematical software, transcendental functions

ABSTRACTThe fast and accurate evaluation of transcendentalfunctions (e.g. exp, log, sin, and atan) is vitally importantin many fields of scientific computing. Intel provides asoftware library of these functions that can be called fromboth the C∗ and FORTRAN* programming languages. Byexploiting some of the key features of the IA-64 floating-point architecture, we have been able to provide double-precision transcendental functions that are highly accurateyet can typically be evaluated in between 50 and 70 clockcycles. In this paper, we discuss some of the designprinciples and implementation details of these functions.

INTRODUCTIONTranscendental functions can be computed in software bya variety of algorithms. The algorithms that are mostsuitable for implementation on modern computerarchitectures usually comprise three steps: reduction,approximation, and reconstruction.

These steps are best illustrated by an example. Considerthe calculation of the exponential function exp(x). Onemay first attempt an evaluation using the familiarMaclaurin series expansion:

exp(x) = 1 + x + x2/2! + x3/3! + … + xk/k! + … .

When x is small, computing a few terms of this seriesgives a reasonably good approximation to exp(x) up to,for example, IEEE double precision (which isapproximately 17 significant decimal digits). However,when x is large, many more terms of the series are neededto satisfy the same accuracy requirement. Increasing thenumber of terms not only lengthens the calculation, but italso introduces more accumulated rounding errors thatmay degrade the accuracy of the answer. ∗All other brands and names are the property of theirrespective owners.

To solve this problem, we express x as

x = N ln(2) / 2K + r

for some integer K chosen beforehand (more about how tochoose later). If N ln(2)/2K is made as close to x aspossible, then |r| never exceeds ln(2)/2K+1. Themathematical identity

exp(x) = exp( N ln(2) / 2K + r ) = 2N/2K exp(r)

shows that the problem is transformed to that ofcalculating the exp function at an argument whosemagnitude is confined. The transformation to r from x iscalled the reduction step; the calculation of exp(r), usuallyperformed by computing an approximating polynomial, iscalled the approximation step; and the composition of thefinal result based on exp(r) and the constant related to Nand K is called the reconstruction step.

In a more traditional approach [1], K is chosen to be 1 andthus the approximation step requires a polynomial withaccuracy good to IEEE double precision for the range of|r | ≤ ln(2)/2. This choice of K leads to reconstruction viamultiplication of exp(r) by 2N, which is easilyimplementable, for example, by scaling the exponent fieldof a floating-point number. One drawback of thisapproach is that when |r | is near ln(2)/2, a large number ofterms of the Maclaurin series expansion is still needed.

More recently, a framework known as table-drivenalgorithms [8] suggested the use of K > 1. When forexample K=5, the argument r after the reduction stepwould satisfy |r| ≤ log(2)/64. As a result, a much shorterpolynomial can satisfy the same accuracy requirement.The tradeoff is a more complex reconstruction step,requiring the multiplication with a constant of the form

2N/32 = 2M 2d/32, d = 0, 1, …, 31,

Page 51: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

The Computation of Transcendental Functions on the IA-64 Architecture 2

where N = 32M + d . This constant can be obtained rathereasily, provided all the 32 possible values of the secondfactor are computed beforehand and stored in a table(hence the name table-driven). This framework workswell for modern machines not only because tables (evenlarge ones) can be accommodated, but also becauseparallelism, such as the presence of pipelined arithmeticunits, allow most of the extra work in the reconstructionstep to be carried out while the approximating polynomialis being evaluated. This extra work includes, forexample, the calculation of d, the indexing into and thefetching from the table of constants, and themultiplication to form 2N/32. Consequently, theperformance gain due to a shorter polynomial is fullyrealized.

In practice, however, we do not use the Maclaurin series.Rather, we use the lowest-degree polynomial p(r) whoseworst deviation |p(r) − exp(r)| within the reduced range inquestion is minimized and stays below an acceptablethreshold. This polynomial is called the minimaxpolynomial. Its coefficients can be determinednumerically, most commonly by the Remez algorithm [5].

Another fine point is that we may also want to retain someconvenient properties of the Maclaurin series, such as theleading coefficient being exactly 1. It is possible to findminimax polynomials even subject to such constraints;some examples using the commercial computer algebrasystem Maple are given in reference [3].

DESIGN PRINCIPLES ON THE IA-64ARCHITECTUREThere are tradeoffs to designing an algorithm followingthe table-driven approach:

• Different argument reduction methods lead totradeoffs between the complexity of the reductionand the reconstruction computation.

• Different table sizes lead to tradeoffs betweenmemory requirements (size and latencycharacteristics) and the complexity of polynomialcomputation.

Several key architectural features of IA-64 have a bearingon which choices are made:

• Short floating-point latency: On IA-64, the genericfloating-point operation is a “fused multiply add” thatcalculates A×B + C per instruction. Not only is thelatency of this floating-point operation much shorterthan memory references, but this floating-pointoperation consists of two basic arithmetic operations.

• Extended precision: Because our target is IEEEdouble-precision with 53 significant bits, the native

64-bit precision on IA-64 delivers 11 extra bits ofaccuracy on basic arithmetic operations.

• Parallelism: Each operation is fully pipelined, andmultiple floating-point units are present.

As stated, these architectural features affect our choices ofdesign tradeoffs. We enumerate several key points:

• Argument reduction usually involves a number ofserial computation steps that cannot take advantageof parallelism. In contrast, the approximation andreconstruction steps can naturally exploit parallelism.Consequently, the reduction step is often abottleneck. We should, therefore, favor a simplereduction method even at the price of a morecomplex reconstruction step.

• Argument reduction usually requires the use of someconstants. The short floating-point latency can makethe memory latency incurred in loading suchconstants a significant portion of the total latency.Consequently, any novel reduction techniques that doaway with memory latency are welcome.

• Long memory latency has two implications for tablesize. First, large tables that exceed even the lowest-level cache size should be avoided. Second, even if atable fits in cache, it still takes a number of repeatedcalls to a transcendental function at differentarguments to bring the whole table into cache. Thus,small tables are favored.

• Extended precision and parallelism together have animportant implication for the approximation step.Traditionally, the polynomial terms used in coreapproximations are evaluated in some well specifiedorder so as to minimize the undesirable effect ofrounding error accumulation. The availability ofextended precision implies that the order ofevaluation of a polynomial becomes unimportant.When a polynomial can be evaluated in an arbitraryorder, parallelism can be fully utilized. Theconsequence is that even long polynomials can beevaluated in short latency.

Roughly speaking, latency grows logarithmically in thedegree of polynomials. The permissive environment thatallows for functions that return accurate 53-bit resultsshould be contrasted with that which is required forfunctions that return accurate 64-bit results. Somefunctions returning accurate 64-bit results are provided ina special double-extended libm as well as in IA-32compatibility operations [6]. In both, considerable effortwas taken to minimize rounding error. Often,computations were carefully choreographed into adominant part that was calculated exactly and a smallerpart that was subject to rounding error. We frequentlystored precomputed values in two pieces to maintain

Page 52: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

The Computation of Transcendental Functions on the IA-64 Architecture 3

intermediate accuracy beyond the underlying precision of64 bits. All these costly implementation techniques areunnecessary in our present double-precision context.

We summarize the above as four simple principles:

1. Use a simple reduction scheme, even if such ascheme only works for a subset of the argumentdomain, provided this subset represents the mostcommon situations.

2. Consider novel reduction methods that avoid memorylatency.

3. Use tables of moderate size.

4. Do not fear long polynomials. Instead, work hard atusing parallelism to minimize latency.

In the next sections, we show these four principles inaction on Merced, the first implementation of the IA-64architecture.

SIMPLE AND FAST RANGE REDUCTIONA common reduction step involves the calculation of theform

r = x − N ρ.

This includes the forward trigonometric functions sin, cos,tan, and the exponential function exp, where ρ is of theform π/2K for the trigonometric functions and of the formln(2)/2K for the exponential function. We exploit the factthat the overwhelming majority of arguments will be in alimited range. For example, the evaluation oftrigonometric functions like sin for very large argumentsis known to be costly. This is because to perform a rangereduction accurately by subtracting a multiple of π/2K, weneed to implicitly have a huge number of bits ofπ available. But for inputs of less than 210 in magnitude,the reduction can be performed accurately and efficiently.The overwhelming majority of cases will fall within thislimited range. Other more time-consuming proceduresare well known and are required when arguments exceed210 in magnitude (see [3] and [6]).

The general difficulty of range reduction implementationis that ρ is not a machine number. If we compute:

r = x − N P

where the machine number P approximates π/2K, then if xis close to a root of the specific trigonometric function,the small error, ε = |P – π/2K|, scaled up by N, constitutesa large relative error in the final result. However, byusing number-theoretic arguments, one can see that whenreduction is really required for double-precision numbersin the specified range, the result of any of thetrigonometric functions, sin, cos, and tan, cannot besmaller in magnitude than about 2−60 (see [7]).

The worst relative error (which occurs when the result ofthe trigonometric function is its smallest, 2-60, and N isclose to 210+K) is about 270+K ε. If we store P as twodouble-extended precision numbers, P_1 + P_2, then wecan make ε < 2-130-K sufficient to make the relative error inthe final result negligible.

One technique to provide an accurate reduced argumenton IA-64 is to apply two successive fma operations

r0 = x − N P_1; r= r0 − N P_2.

The first operation introduces no rounding error becauseof the well known phenomenon of cancellation.

For sin and cos, we pick K to be 4, so the reconstructionhas the form

sin(x)= sin(Nπ/16) cos(r) + cos(Nπ/16)sin(r)

and

cos(x)= cos(Nπ/16) cos(r) − sin(Nπ/16)sin(r).

Periodicity implies that we need only tabulate sin(Nπ/16)and cos(Nπ/16) for N = 0, 1, …, 31.

The case for the exponential function is similar. Hereln(2)/2K (K is chosen to be 7 in this case) is approximatedby two machine numbers P_1 + P_2, and the argument isreduced in a similar fashion.

NOVEL REDUCTIONSome mathematical functions f have the property that

f(u v) = g(f(u),f(v))

where g is a simple function such as the sum or productoperator. For example, for the logarithm, we have (forpositive u and v)

ln(u v) = ln(u) + ln(v) (g is the sum operator)

while for the cube root, we have

(u v)1/3 = u1/3 v1/3 (g is the product operator).

In such situations, we can perform an argument reductionvery quickly using IA-64's basic floating-point reciprocalapproximation (frcpa) instruction, which is primarilyintended to support floating-point division. According toits definition, frcpa(a) is a floating-point with 11significant bits that approximates 1/a using a lookup onthe top 8 bits of the (normalized) input number a. This11-bit floating-point number approximates 1/a to about 8significant bits of accuracy. The exact values returned arespecified in the IA-64 architecture definition. Byenumeration of the approximate reciprocal values, onecan show that for all input values a,

frcpa(a) = (1/a) (1 − β), |β| ≤ 2−8.86.

We can write f(x) as

Page 53: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

The Computation of Transcendental Functions on the IA-64 Architecture 4

f(x) = f(x frcpa (x) / frcpa (x))

= g( f(x frcpa (x)), f(1/frcpa (x)) ).

The f(1/frcpa (x)) terms can be stored in precomputedtables, and they can be obtained by an index based on thetop 8 bits of x (which uniquely identifies thecorresponding frcpa (x)).

Because the f’s we are considering here have a naturalexpansion around 1,

f(x frcpa (x))

is most naturally approximated by a polynomial evaluatedat the argument r = x frcpa(x) − 1. Hence, a single fmaconstitutes our argument reduction computation, and thevalue frcpa(x) is obtained without any memory latency.

We apply this strategy to f(x) = ln(x).

ln(x) = ln(1/frcpa (x)) + ln(frcpa (x) x)

= ln(1/frcpa (x)) + ln(1 + r)

The first value on the right-hand side is obtained from atable, and the second value is computed by a minimaxpolynomial approximating ln(1+r) on |r| ≤ 2-8.8. Thequantity 2−8.8 is characteristic of the accuracy of the IA-64frcpa instruction.

The case for the cube root function cbrt is similar.

(x)1/3 = (1/frcpa (x))1/3 (frcpa (x) x)1/3

= (1/frcpa (x))1/3 (1 + r)1/3.

The first value on the right-hand side is obtained from atable, and the second value is computed by a minimaxpolynomial approximating (1+r)1/3 on |r| ≤ 2-8.8.

MODERATE TABLE SIZESWe tabulate here the number of double-extended tableentries used in each function. The trigonometric functionssin and cos share the same table, and the functions tan andatan do not use a table at all.

Function Number of Double-Extended Entries

cbrt 256 (3072 bytes)

exp 24 (288 bytes)

Ln 256 (3072 bytes)

sin, cos 64 (768 bytes)

tan None

atan None

Table 1: Table sizes used in the algorithms

Table 1 does not include the number of constants forargument reduction nor does it include the number ofcoefficients needed for evaluating the polynomial.

OPTIMAL EVALUATION OFPOLYNOMIALSThe traditional Horner's rule of evaluation of apolynomial is efficient on serial machines. Nevertheless,a general degree-n polynomial requires a latency of nfma’s. When more parallelism is available, it is possibleto be more efficient by splitting the polynomial into parts,evaluating the parts in parallel, and then combining them.We employ this technique to the polynomialapproximation steps for all the functions. The enhancedperformance is crucial to the cases of tan and atan wherethe polynomials involved are of degrees 15 and 22. Evenfor the other functions where the polynomials are varyingin degree from 4 to 8, our technique also contributes to anoticeable gain over the straightforward Horner’s method.We now describe this technique in more detail.

Merced has two floating-point execution units, so there iscertainly some parallelism to be exploited. Even moreimportant, both floating-point units are fully pipelined infive stages. Thus, two new operations can be issued everycycle, even though the results are then not available for afurther five cycles. This gives much of the same benefitas more parallel execution units. Therefore, as noted bythe author in reference [3], one can use more sophisticatedtechniques for polynomial evaluation intended for highlyparallel machines. For example, Estrin's method [2]breaks the evaluation down into a balanced binary tree.

We can easily place a lower bound on the latency withwhich a polynomial can be computed: if we start with xand the coefficients ci, then by induction, in n serial fmaoperations, we cannot create a polynomial that is a degreehigher than 2n, and we can only equal 2n if the term of thehighest degree is simply x2n

with unity as its coefficient.For example, in one operation we can reach c0 + c1 x orx + x2 but not x + c0x2. Our goal is to find an actualscheduling that comes as close as possible to this lowerbound.

Simple heuristics based on binary chopping normally givea good evaluation strategy, but it is not always easy tovisualize all the possibilities. When the polynomial canbe split asymmetrically, or where certain coefficients arespecial, such as 1 or 0, there are often ways of doingslightly better than one might expect in overall latency orat least in the number of instructions required to attainthat latency (and hence in throughput). Besides, doing thescheduling by hand is tedious. We search automaticallyfor the best scheduling using a program that exhaustivelyexamines all essentially different scheduling. One simply

Page 54: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

The Computation of Transcendental Functions on the IA-64 Architecture 5

enters a polynomial, and the program returns the bestlatency and throughput attainable, and it lists the mainways of scheduling the operations to attain this.

Even with various intelligent pruning approaches andheuristics, the search space is large. We restrict itsomewhat by considering only fma combinations of theform p1(x) + xkp2(x). That is, we do not considermultiplying two polynomials with nontrivial coefficients.Effectively, we allow only solutions that work forarbitrary coefficients, without considering specialfactorization properties. However, for polynomials whereall the coefficients are 1, these results may not be optimalbecause of the availability of nontrivial factorizations thatwe have ruled out. For example, we can calculate:

1 + x + x2 + x3 + x4 + x5 + x6

as

1 + (1 + (x + x2)) (x + (x2) (x2))

which can be scheduled in 15 cycles. However, if therestriction on fma operations is observed, then 16 cyclesis the best attainable.

The optimization program works in two stages. First, allpossible evaluation orders using these restricted fmaoperations are computed. These evaluation orders ignorescheduling, being just “abstract syntax” tree structuresindicating the dependencies of subexpressions, withinterior nodes representing fma operations of the formp1(x) + xk p2(x):

c 0 + c 1x + c 2x2 + c 3x

3

c0 + c 1x x2 c 2 + c 3x

c 0 x c1 0 x x c 2 x c 3

Figure 1: A dependency tree

However, because of the enormous explosion inpossibilities, we limit the search to the smallest possibletree depth. This tree depth corresponds to the minimumnumber of serial operations than can possibly be used toevaluate the expression using the order denoted by thatparticular tree. Consequently, if the tree depth is d thenwe cannot possibly do better than 5d cycles for thatparticular tree. Now, assuming that we can in fact do atleast as well as 5d + 4, we are justified in ignoring trees ofa depth greater than or equal to d + 1, which could notpossibly be scheduled in as few cycles. This turns out tobe the case for all our examples.

The next stage is to take each tree (in some of theexamples below there are as many as 10000 of them) andcalculate the optimal scheduling. The optimal schedulingis computed backwards by a fairly naive greedyalgorithm, but with a few simple refinements based onstratifying the nodes from the top as well as from thebottom.

The following table gives the evaluation strategy found bythe program for the polynomial:

x + c2x2 + c3x3 + … + c9x9

Table 2 shows that it can be scheduled in 20 cycles, andwe have attained the lower bound. However, if the firstterm were c1x we would need 21.

Cycle FMA Unit 1 FMA Unit 2

0 v1 = c2 + x c3 v2 = x x

3 v3 = c6 + x c7 v4 = c8 + x c9

4 v5 = c4 + x c5

5 v6 = x + v2 v1 v7 = v2 v2

9 v8 = v3 + v2 v4

10 v9 = v6 + v7 v5 v10 = v2 v7

15 v11 = v9 + v10 v8

Table 2: An optimal scheduling

OUTLINE OF ALGORITHMSWe outline each of the seven algorithms discussed here.We concentrate only on the numeric cases and ignoresituations such as when the input is out of the range of thefunctions’ domains or non-numeric (NaN for example).

Cbrt

1. Reduction: Given x, compute r = x frcpa(x) − 1.

2. Approximation: Compute a polynomial p(r) of theform p(r)=p1r + p2r2 + … + p6r6 that approximates(1+r)1/3−1.

3. Reconstruction: Compute the result T + Tp(r) wherethe T is (1/frcpa(x))1/3. This value T is obtained viaa tabulation of (1/frcpa(y))1/3, where y=1+k/256, kranges from 0 to 255 and a tabulation of 2−j/3, and jranges from 0 to 2.

Exp

1. Reduction: Given x, compute N, the closest integer tothe value x (128/ ln(2)). Then compute r = (x−NP1)−N P2. Here P1+P2 approximates ln(2)/128 (seeprevious discussions).

Page 55: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

The Computation of Transcendental Functions on the IA-64 Architecture 6

2. Approximation: Compute a polynomial p(r) of theform p(r) = r + p1 r

2 + … + p4 r5 that approximatesexp(r) − 1.

3. Reconstruction: Compute the result T + Tp(r) whereT is 2N/128 . This value T is obtained as follows. First,N is expressed as N = 128 M + 16 K + J, where I1ranges from 0 to 15, and I2 ranges from 0 to 7.Clearly 2N/128 = 2M 2K/8 2J/128. The first of the threefactors can be obtained by scaling the exponent; theremaining two factors are fetched from tables with 8entries and 16 entries, respectively.

Ln

1. Reduction: Given x, compute r = x frcpa(x) − 1.

2. Approximation: Compute a polynomial p(r) of theform p(r)= p1 r2 + … + p5 r6 that approximatesln(1+r) − r.

3. Reconstruction: Compute the result T + r + p(r)where the T is ln(1/frcpa(x)). This value T isobtained via a tabulation of ln(1/frcpa(y)), wherey=1+k/256, k ranges from 0 to 255, and a calculationof the form N ln(2).

Sin and Cos

We first consider the case of sin(x).

1. Reduction: Given x, compute N, the closest integer tothe value x (16/π). Then compute r = (x−N P1)−N P2.Here P1+P2 approximates π/16 (see previousdiscussions).

2. Approximation: Compute two polynomials: p(r) ofthe form r + p1 r

3 + … + p4 r9 that approximatessin(r) and q(r) of the form q1 r

2 + q2 r4 + … + q4 r8

that approximates cos(r) − 1.

3. Reconstruction: Return the result as Cp(r)+(S+Sq(r))where C is cos(N π/16) and S is sin(N π/16) obtainedfrom a table.

The case of cos(x) is almost identical. Add 8 to N justafter it is first obtained. This works because of theidentity cos(x) = sin(x+π/2).

Tan

1. Reduction: Given x, compute N, the closest integer tothe value x (2/π). Then compute r = (x−N P1)−N P2.Here P1+P2 approximates π/2 (see previousdiscussions).

2. Approximation: When N is even, compute apolynomial p(r) = r + r t (p0 + p1 t + … + p15 t

15) thatapproximates tan(r). When N is odd, compute apolynomial q(r) = (−r)-1+ r(q0 + q1 t + … + q10 t10)

that approximates −cot(r). The term t is r2. Weemphasize the fact that parallelism is fully utilized.

3. Reconstruction: If N is even, return p. If N is odd,return q.

Atan

1. Reduction: No reduction is needed.

2. Approximation: If |x| is less than 1, compute apolynomial p(x) = x + x3(p0 + p1 y + … + p22 y22) thatapproximates atan(x), y is x2. I f |x| > 1, computeseveral quantities, fully utilizing parallelism. First,compute q(x) = q0 + q1 y + … + q22 y

22, y=x2, thatapproximates x45 atan(1/x). Second, compute c45

where c = frcpa(x). Third, compute anotherpolynomial r(β) = 1+r1 β + … + r10 β10, where β is thequantity x frcpa(x) − 1 and r(β) approximates thevalue (1−β)−45.

3. Reconstruction: If |x | is less than 1, return p(x).Otherwise, return sign(x)π/2 − c45r(β)q(x). This isdue to the identity atan(x) = sign(x)π/2 − atan(1/x).

SPEED AND ACCURACYThese new double-precision elementary functions aredesigned to be both fast and accurate. We present thespeed of the functions in terms of latency for argumentsthat fall through the implementation in a path that isdeemed most likely. As far as accuracy is concerned, wereport the largest observed error after extensive testing interms of units of last place (ulps). This error measure isstandard in this field. Let f be the mathematical functionto be implemented and F be the actual implementation indouble precision. When 2L < |f(x)| ≤ 2L+1, the error in ulpsis defined as |f(x) − F(x)| / (2L−52). Note that the smallestworst-case error that one can possibly attain is 0.5 ulps.Table 3 tabulates the latency and maximum errorobserved.

Function Latency (cycles) Max. Error (ulps)

cbrt 60 0.51

exp 60 0.51

ln 52 0.53

sin 70 0.51

cos 70 0.51

tan 72 0.51

atan 66 0.51

Table 3: Speed and accuracy of functions

Page 56: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

The Computation of Transcendental Functions on the IA-64 Architecture 7

CONCLUSIONSWe have shown how certain key features of the IA-64architecture can be exploited to design transcendentalfunctions featuring an excellent combination of speed andaccuracy. All of these functions performed over twice asfast as the ones based on the simple conversion of alibrary tailored for double-extended precision. In oneinstance, the ln function described here contributed to atwo point increment of SpecFp benchmark run undersimulation.

The features of the IA-64 architecture that are exploitedinclude parallelism and the fused multiply add as well as less obvious features such as the reciprocal approximationinstruction. When abundant resources for parallelism areavailable, it is not always easy to visualize how to takefull advantage of them. We have searched for optimalinstruction schedules. Although our search method issufficient to handle the situations we have faced so far,more sophisticated techniques are needed to handle morecomplex situations. First, polynomials of a higher degreemay be needed in more advanced algorithms. Second,more general expressions that can be considered asmultivariate polynomials are also anticipated. Finally, ourcurrent method does not handle the full generality ofmicroarchitectural constraints, which also vary in futureimplementations on the IA-64 roadmap. We believe thisoptimal scheduling problem to be important not onlybecause it yields high-performance implementation, butalso because it may offer a quantitative analysis on thebalance of microarchitectural parameters. Currently weare considering an integer programming framework totackle this problem. We welcome other suggestions aswell.

REFERENCES [1] Cody Jr., William J. and Waite, William, Software

Manual for the Elementary Functions, Prentice Hall,1980.

[2] Knuth, D.E., The Art of Computer Programming vol.2: Seminumerical Algorithms, Addison-Welsey, 1969.

[3] Muller, J. M., Elementary functions: algorithms andimplementation, Birkhaüser, 1997.

[4] Payne, M., “An Argument Reduction Scheme on theDEC VAX,” Signum Newsletter, January 1983.

[5] Powell, M.J.D., Approximation Theory and Methods,Cambridge University Press, 1981.

[6] Story, S. and Tang, P.T.P., “New algorithms forimproved transcendental functions on IA-64,” inProceedings of 14th IEEE symposium on computerarithmetic, IEEE Computer Society Press, 1999.

[7] Smith, Roger A., “A Continued-Fraction Analysis ofTrigonometric Argument Reduction,” IEEETransactions on Computers, pp. 1348-1351, Vol. 44,No. 11, November 1995.

[8] Tang, P.T.P., “Table-driven implementation of theexponential function in IEEE floating-pointarithmetic,” ACM Transactions on MathematicalSoftware, vol. 15, pp. 144-157, 1989.

AUTHORS’ BIOGRAPHIESJohn Harrison has been with Intel for just over one year.He obtained his Ph.D. degree from Cambridge Universityin England and is a specialist in formal validation andtheorem proving. His e-mail is [email protected].

Ted Kubaska is a senior software engineer with IntelCorporation in Hillsboro, Oregon. He has a M.S. degreein physics from the University of Maine at Orono and aM.S. degree in computer science from the OregonGraduate Institute. He works in the MSL NumericsGroup where he implements and tests floating-pointalgorithms. His e-mail [email protected].

Shane Story has worked on numerical and floating-pointrelated issues since he began working for Intel eight yearsago. His e-mail is [email protected].

Ping Tak Peter Tang (his friends call him Peter) joinedIntel very recently as an applied mathematician workingin the Computational Software Lab of MSL. Peterreceived his Ph.D. degree in mathematics from theUniversity of California at Berkeley. His interest is infloating-point issues as well as fast and accuratenumerical computation methods. Peter has consulted forIntel in the past on such issues as the design of thetranscendental algorithms on the Pentium, and hecontributed a software solution to the Pentium divisionproblem. His e-mail is [email protected].

Page 57: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

IA-64 Floating-Point Operations and the IEEE Standard for Binary Floating-Point Arithmetic 1

IA-64 Floating-Point Operations and the IEEE Standard forBinary Floating-Point Arithmetic

Marius Cornea-Hasegan, Microprocessor Products Group, Intel CorporationBob Norin, Microprocessor Products Group, Intel Corporation

Index words: IA-64 architecture, floating-point, IEEE Standard 754-1985

ABSTRACTThis paper examines the implementation of floating-pointoperations in the IA-64 architecture from the perspectiveof the IEEE Standard for Binary Floating-Point Arithmetic[1]. The floating-point data formats, operations, andspecial values are compared with the mandatory orrecommended ones from the IEEE Standard, showing thepotential gains in performance that result from specificchoices.

Two subsections are dedicated to the floating-pointdivide, remainder, and square root operations, which areimplemented in software. It is shown how IEEEcompliance was achieved using new IA-64 features suchas fused multiply-add operations, predication, andmultiple status fields for IEEE status flags. Derivedinteger operations (the integer divide and remainder) arealso illustrated.

IA-64 floating-point exceptions and traps are described,including the Software Assistance faults and traps thatcan lead to further IEEE-defined exceptions. Thesoftware extensions to the hardware needed to complywith the IEEE Standard’s recommendations in handlingfloating-point exceptions are specified. The special caseof the Single Instruction Multiple Data (SIMD)instructions is described. Finally, a subsection isdedicated to speculation, a new feature in IA processors.

INTRODUCTIONThe IA-64 floating-point architecture was designed withthree objectives in mind. First, it was meant to allowhigh-performance computations. This was achievedthrough a number of architectural features. Pipelinedfloating-point units allow several operations to takeplace in parallel. Special instructions were added, suchas fused floating-point multiply-add, or SIMDinstructions, which allow the processing of two subsets

of floating-point operands in parallel. Predication allowsskipping operations without taking a branch.Speculation allows speculative execution chains whoseresults are committed only if needed. In addition, a largefloating-point register file (including a rotating subset)reduces the number of save/restore operations involvingmemory. The rotating subset of the floating-pointregister file enables software pipelining of loops, leadingto significant gains in performance.

Second, the architecture aims to provide high floating-point accuracy. For this, several floating-point datatypes were provided, and instructions new to the Intelarchitecture, such as the fused floating-point multiply-add, were introduced.

Third, compliance with the IEEE Standard for BinaryFloating-Point Arithmetic was sought. The environmentthat a numeric software programmer sees complies withthe IEEE Standard and most of its recommendations as acombination of hardware and software, as explainedfurther in this paper.

Floating-Point NumbersFloating-point numbers are represented as aconcatenation of a sign bit, an M-bit exponent field, andan N-bit significand field. In some floating-point formats,the most significant bit (integer bit) of the significand isnot represented. Its assumed value is 1, except fordenormal numbers, whose most significant bit of thesignificand is 0. Mathematically

f = σ ⋅ s ⋅ 2e

where σ = ±1, s ∈ [1,2), s = 1 + k / 2N-1 , k ∈ {0, 1, 2,…, 2N-

1-1}, e ∈ [emin, emax] ∩ Z (Z is the set of integers), emin = -2M-1 + 2, and emax = 2M-1 – 1.

The IA-64 architecture provides 128 82-bit floating-pointregisters that can hold floating-point values in variousformats, and which can be addressed in any order.

Page 58: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

IA-64 Floating-Point Operations and the IEEE Standard for Binary Floating-Point Arithmetic 2

Floating-point numbers can also be stored into or loadedfrom memory.

IA-64 FORMATS, CONTROL, ANDSTATUS

FormatsThree floating-point formats described in the IEEEStandard are implemented as required: single precision(M=8, N=24), double precision (M=11, N=53), anddouble-extended precision (M=15, N=64). These are theformats usually accessible to a high-level languagenumeric programmer. The architecture provides forseveral more formats, listed in Table 1, that can be usedby compilers or assembly code writers, some of whichemploy the 17-bit exponent range and 64-bit significandsallowed by the floating-point register format.

Format FormatParameters

Single precision M=8, N=24

Double precision M=11, N=53

Double-extended precision M=15, N=64

Pair of single precision floating-pointnumbers

M=8, N=24

IA-32 register stack single precision M=15, N=24

IA-32 register stack double precision M=15, N=53

IA-32 double-extended precision M=15, N=64

Full register file single precision M=17, N=24

Full register file double precision M=17, N=53

Full register file double-extendedprecision

M=17, N=64

Table 1: IA-64 floating-point formats

The floating-point format used in a given computation isdetermined by the floating-point instruction (someinstructions have a precision control completer pcspecifying a static precision) or by the precision controlfield (pc), and by the widest-range exponent (wre) bit inthe Floating-Point Status Register (FPSR). In memory,floating-point numbers can only be stored in singleprecision, double precision, double-extended precision,and register file format (‘spilled’ as a 128-bit entity,containing the value of the floating-point register in thelower 82 bits).

RoundingThe four IEEE rounding modes are supported: roundingto nearest, rounding to negative infinity, rounding topositive infinity, and rounding to zero. Someinstructions have the option of using a static roundingmode. For example, fcvt.fx.trunc performs conversion ofa floating-point number to integer using rounding tozero.

Some of the basic operations specified by the IEEEStandard (divide, remainder, and square root) as well asother derived operations are implemented usingsequences of add, subtract, multiply, or fused multiply-add and multiply-subtract operations.

In order to determine whether a given computation yieldsthe correctly rounded result in any rounding mode, asspecified by the standard, the error that occurs due torounding has to be evaluated. Two measures arecommonly used for this purpose. The first is the error ofan approximation with respect to the exact result,expressed in fractions of an ulp, or unit in the last place.Let FN be the set of floating-point numbers with N-bitsignificands and unlimited exponent range. For thefloating-point number f = σ ⋅ s ⋅ 2e ∈ FN, one ulp has themagnitude

1 ulp = 2e-N+1.

An alternative is to use the relative error. If the realnumber x is approximated by the floating-point number a,then the relative error ε is determined by

a = x ⋅ (1 + ε)

The Floating-Point Status RegisterSeveral characteristics of the floating-pointcomputations are determined by the contents of the 64-bit FPSR.

A set of six trap mask bits (bits 0 through 5) controlenabling or disabling the five IEEE traps (invalidoperation, divide-by-zero, overflow, underflow, andinexact result) and the IA-defined denormal trap [2]. Inaddition, four 13-bit subsets of control and status bitsare provided: status fields sf0, sf1, sf2, and sf3. Multiplestatus fields allow different computations to beperformed simultaneously with different precisionsand/or rounding modes. Status field 0 is the user statusfield, specifying rounding-to-nearest and 64-bit precisionby default. Status field 1 is reserved by softwareconventions for special operations, such as divide andsquare root. It uses rounding-to-nearest, the 64-bitprecision, and the widest-range exponent (17 bits).Status fields 2 and 3 can be used in speculative

Page 59: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

IA-64 Floating-Point Operations and the IEEE Standard for Binary Floating-Point Arithmetic 3

operations, or for implementing special numericalgorithms, e.g., the transcendental functions.

Each status field contains a 2-bit rounding mode controlfield (00 for rounding to nearest, 01 to negative infinity,10 to positive infinity, and 11 toward zero), a 2-bitprecision control field (00 for 24 bits, 10 for 53 bits, and11 for 64 bits), a widest-range exponent bit (use the 17-bitexponent if wre = 1), a flush-to-zero bit (causes flushingto zero of tiny results if ftz = 1), and a traps disabled bit(overrides the individual trap masks and disables alltraps if td = 1, except for status field 0, where this bit isreserved). Each status field also contains status flags forthe five IEEE exceptions and for the denormal exception.

The register file floating-point format uses a 17-bitexponent range, which has two more bits than thedouble-extended precision format, for at least threereasons. The first is related to the implementation insoftware of the divide and square root operations in theIA-64 architecture. Short sequences of assemblylanguage instructions carry out these computationsiteratively. If the exponent range of the intermediatecomputation steps is equal to that of the final result, thensome of the intermediate steps might overflow,underflow, or lose precision, preventing the final resultfrom being IEEE correct. Software Assistance (SWA)will be necessary in these cases to generate the correctresults, as explained in [4]. The two (or more) extra bitsin the exponent range (17 versus 15 or less) prevent theSWA requests from occurring. The second reason forhaving a 17-bit exponent range is that it allows thecommon computation of x2 + y2 to be performed withoutoverflow or underflow, even for the largest or smallestdouble-extended precision numbers. Third, the 17-bitexponent range is necessary in order to be able torepresent the product of all double-extended denormalnumbers.

Special ValuesThe various floating-point formats support the IEEEmandated representations for denormals, zero, infinities,quiet NaNs (QNaNs), and signaling NaNs (SNaNs). Inaddition, the formats that have an explicit integer bit inthe significand can also hold other types of values.These formats are double-extended, with 15-bitexponents biased by 16383 (0x3fff), and all the registerfile formats, with 17-bit exponents biased by 65535(0xffff). The exponents of these additional types ofvalues are specified below for the register file format:

unnormalized numbers: non-zero significandbeginning with 0 and exponent from 0 to 0x1fffe,or pseudo-zeroes with a significand of 0, andexponent from 0x1 to 0x1fffe

pseudo-NaNs: non-zero significand andexponent of 0x1ffff (unsupported by thearchitecture); the pseudo-QNaNs have thesecond most significant bit of the significandequal to 1; this bit is 0 for pseudo-SNaNs

pseudo-infinities: significand of zero andexponent of 0x1ffff (unsupported by thearchitecture)

Note that one of the pseudo-zero values, encoded on 82bits as 0x1fffe0000000000000000, is denoted as NaTVal(‘not a value’) and is generated by unsuccessfulspeculative load from memory operations (e.g. aspeculative load, in the presence of a deferred floating-point exception). It is then propagated through thespeculative chain to indicate in the end that no usefulresult is available.

Two special categories that overload other floating-pointnumbers in register file format are the SIMD floating-point pairs, and the canonical non-zero integers. Bothhave an exponent of 0x1003e (unbiased 63). The value ofthe canonical non-zero integers is equal to that of theunnormal or normal floating-point numbers that theyoverlap with. The exponent of 63 moves the binary pointbeyond the least significant bit, the resulting value beingthe integer stored in the significand. The SIMD floating-point numbers consist of two single-precision floating-point values encoded in the two halves of the 64-bitsignificand of a floating-point register, with the biasedexponent set to 0x1003e. For example, the 82-bit value of0x1003e 3f800000 3f800000 represents the pair (+1.0,+1.0). Note that all the arithmetic scalar floating-pointinstructions have SIMD counterparts that operate ontwo single-precision floating-point values in parallel.

IA-64 FLOATING-POINT OPERATIONSAll the floating-point operations mandated orrecommended by the IEEE Standard are or can beimplemented in IA-64 [2]. Note that most IA-64instructions [2] are predicated by a 1-bit predicate (qp)from the 64-bit predicate register (predicate p0 is fixed,containing always the logical value 1). For example, thefused multiply-add operation is

(qp) fma.pc.sf f1 = f3, f4, f2

The fma instruction is executed if qp = 1; otherwise, it isskipped. Two instruction completers select the precisioncontrol (pc) and the status field (sf) to be used. Whenthe qualifying predicate is not represented, it is either notnecessary, or it is assumed to be p0. When qp = 1, fmacalculates f3 ⋅ f4 + f2, where ‘pc’ can be ‘s’, ‘d’, or none.If the instruction completer ‘pc’ is ‘s’, fma.s.sf generatesa result with a 24-bit significand. Similarly, fma.d.sf

Page 60: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

IA-64 Floating-Point Operations and the IEEE Standard for Binary Floating-Point Arithmetic 4

generates a result with a 53-bit significand. Theexponent in the two cases is in the 8-bit or 11-bit rangerespectively if sf.wre = 0, and in the 17-bit range if sf.wre= 1. If ‘pc’ is none, the precision of the computationfma.sf f1 = f3, f4, f2 is specified by the pc field of thestatus field being used, sf.pc. The exponent size is 15bits if sf.wre = 0, and 17 bits if sf.wre = 1.

Addition and multiplication are implemented as pseudo-ops of the floating-point multiply-add operation. Thepseudo-op for addition is fadd.pc.sf f1 = f3, f2 obtainedby replacing f4 with register f1 that contains +1.0. Thepseudo-op for multiplication is fmpy.pc.sf f1 = f3, f4,obtained by replacing f2 with f0 that contains +0.0.

The reason for having a fused multiply-add operation isthat it allows computation of a ⋅ b + c with only onerounding error. Assuming rounding to nearest, fmacomputes

(a ⋅ b + c) rn = (a ⋅ b + c) ⋅ (1 + ε)

where |ε| < 2-N, and N is the number of bits in thesignificand. The relative error above (ε) is smaller ingeneral than that obtained with pure add and multiplyoperations:

((a ⋅ b) rn + c) rn = (a ⋅ b (1 + ε1) + c) ⋅ (1 + ε2)

where |ε1| < 2–N and |ε2| < 2–N.

The benefit that arises from this property is that itenables the implementation of a whole new category ofnumerical algorithms, relying on the possibility ofperforming this combined operation with only onerounding error (see the subsections on divide andsquare root below).

Subtraction (fsub.pc.sf f1 = f3, f2) is implemented as apseudo-op of the floating-point multiply-subtract,fms.pc.sf f1 = f3, f4, f2 (which calculates f3 ⋅ f4 - f2) wheref4 is replaced by f1. In addition to fma and fms, a similaroperation is available for the floating-point negativemultiply-add operation, fnma.pc.sf f1 = f3, f4, f2, whichcalculates -f3 ⋅ f4 + f2.

A deviation from one of the IEEE Standard’srecommendations is to allow higher precision operandsto lead to lower precision results. However, this is auseful feature when implementing the divide, remainder,and square root operations in software.

For parallel computations, counterparts of fma, fms, andfnma are provided. For example, fpma.pc.sf f1 = f3, f4, f2calculates f3 ⋅ f4 + f2. A pair of ones (1.0, 1.0) has to beloaded explicitly in a floating-point register to emulatethe SIMD floating-point add.

Divide, square root, and remainder operations are notavailable directly in hardware. Instead, they have to beimplemented in software as sequences of instructionscorresponding to iterative algorithms (described below).

Rounding of a floating-point number to a 64-bit signedinteger in floating-point format is achieved by thefcvt.fx.sf f1 = f2 instruction followed by fcvt.xf f2 = f1. For64-bit unsigned integers, the similar instructions arefcvt.fxu.sf f1 = f2 and fcvt.xuf.pc.sf f2 = f1. Twovariations of the instructions that convert floating-pointnumbers to integer use the rounding-to-zero moderegardless of the rounding control bits used in the FPSRstatus field (fcvt.fx.trunc.sf f1 = f2 and fcvt.fxu.trunc.sf f1= f2). They are useful in implementing integer divide andremainder operations using floating-point instructions.For example, the following instructions convert a singleprecision floating-point number from memory (whoseaddress is in the general register r30) to a 64-bit signedinteger in r8:

ldfs f6=[r30];; // load single precision fp number

fcvt.fx.trunc.s0 f7=f6;; // convert to integer

getf.sig r8=f7;;

(Note that stop bits (;;) delimit the instruction groups.)The biased exponent of the value in f7 is set byfcvt.fx.trunc.s0 to 0x1003e (unbiased 63) and thesignificand to the signed integer that is the result of theconversion. (If the conversion is invalid, the significandis set to the value of Integer Indefinite, which is –263.)Since rounding to zero is used by fcvt.fx.trunc,specifying the status field only tells which status flags toset if an invalid operation, denormal, or inexact resultexception occurs (Exceptions and Traps are covered laterin the paper.) For the conversion from a floating-pointnumber to a 64-bit unsigned integer, fcvt.fx.trunc abovehas to be replaced by fcvt.fxu.trunc.

The opposite conversion, from a 64-bit signed integer inr32 to a register-file format floating-point number in f7, isperformed by

setf.sig f6 = r32;; //sign=0 exp=0x1003e signif.=r32

fcvt.xf f7 = f6;; // sign=sign(r32); no fp exceptions

where the result is an integer-valued normal floating-point number. To convert further, for example to a singleprecision floating-point number, one more instruction isneeded

fma.s.s0 f8=f7,f1,f0;;

where the single precision format is specified statically,and status field s0 is assumed to have wre = 0.

For 64-bit unsigned integers, the similar conversion is

Page 61: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

IA-64 Floating-Point Operations and the IEEE Standard for Binary Floating-Point Arithmetic 5

setf.sig f6 = r32;; // sign=0 exp=0x1003e signif.=r32

fcvt.xuf.s0 f7 = f6

where fcvt.xuf.pc.sf f7 = f6 is actually a pseudo-op forfma.pc.sf f7 = f6, f1, f0, and a synonym of fnorm.pc.sf f7 =f6 (it is assumed that status field s0 has pc = 0x3). Theresult is thus a normalized integer-valued floating-pointnumber. This is important to know, since floating-pointoperations on unnormalized numbers lead to SoftwareAssistance faults (as explained further in the paper),thereby slowing down performance unnecessarily.

Conversions between the different floating-point formatsare achieved using floating-point load, store, or otheroperations. For example, the following sequenceconverts a single precision value from memory to doubleprecision format, also in memory (r29 contains theaddress of the single precision source, and r30 that ofthe double precision destination):

ldfs f6 = [r29];;

fma.d.s0 f7=f6,f1,f0;;

stfd [r30] = f7

This conversion could trigger the invalid exception (for asignaling NaN operand) or the denormal operandexception. These can happen on the fma instruction, butthe conversion will be correct numerically even withoutthis instruction, as all the single precision values can berepresented in the double precision format.

The opposite conversion is shown below (it is assumedthat status field s0 has wre = 0):

ldfd f6=[r29];;

fma.s.s0 f7=f6,f1,f0;;

stfs [r30]=f7;;

The role of the fma.s.s0 is to trigger possible invalid,denormal, underflow, overflow, or inexact exceptions onthis conversion.

Other conversions between floating-point and integerformats can be achieved with short sequences ofinstructions. For example, the following sequenceconverts a single precision floating-point value inmemory to a 32-bit signed integer (correct only if theresult fits on 32 bits):

ldfs f6 = [r30];; // load f6 with fp value from memory

fcvt.fx.trunc.s0 f7=f6;; // convert to signed integer

getf.sig r29 = f7;; // move the 64-bit integer to r29

st4 [r28] = r29;; // store as 32-bit integer in memory

The opposite conversion, from a 32-bit integer in memoryto a single precision floating-point number in memory, isperformed by

ld4 r29 = [r30];; // load r29 with 32-bit int from mem

sxt4 r28=r29;; // sign-extend

setf.sig f6 = r28;; // 32-bit integer in f6; exp=0x1003e

fcvt.xf f7=f6;; // convert to normal floating-point

fma.s.s0 f8 = f7,f1,f0;; // trigger I exceptions if any

stfs [r27] = f8;; // store single prec. value in memory.

Floating-point compare operations can be performeddirectly between numbers in floating-point register fileformat, using the fcmp instruction. For other memoryformats, a conversion to register format is required priorto applying the floating-point compare instruction. Fromthe 26 functionally distinct relations specified by theIEEE Standard, only the six mandatory ones areimplemented (four directly, and two as pseudo-ops):

fcmp.eq.sf p1, p2 = f2, f3 (test for ‘=’)

fcmp.lt.sf p1, p2 = f2, f3 (test for ‘<’)

fcmp.le.sf p1, p2 = f2, f3 (test for ‘<=’)

fcmp.gt.sf p1, p2 = f2, f3 (test for ‘>’)

fcmp.ge.sf p1, p2 = f2, f3 (test for ‘>=’)

fcmp.unord.sf p1, p2 = f2, f3 (test for ‘?’)

The result of a compare operation is written to two 1-bitpredicates in the 64-bit predicate register. Predicate p1shows the result of the comparison, while p2 is itsopposite. An exception is the case when at least oneinput value is NaTVal, when p1 = p2 = 0. A variant ofthe fcmp instruction is called ‘unconditional’ (withrespect to the qualifying predicate). The difference isthat if qp = 0, the unconditional compare

(qp) fcmp.eq.unc.sf p1, p2 = f2, f3

clears both output predicates, while

(qp) fcmp.eq.sf p1, p2 = f2, f3

leaves them unchanged.

Six more compare relations are implemented, as pseudo-ops of the above, to test for the opposite situations (neq,nlt, nle, ngt, nge, and ord). The remaining 14 comparisonrelations specified by the IEEE Standard can beperformed based on the above.

A special type of compare instruction isfclass.fcrel.fctype p1,p2=f2,fclass9, that allowsclassification of the contents of f2 according to the classspecifier fclass9 . The fcrel instruction completer can be

Page 62: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

IA-64 Floating-Point Operations and the IEEE Standard for Binary Floating-Point Arithmetic 6

‘m’ (if f2 has to agree with the pattern specified byfclass9), or ‘nm’ (f2 has to disagree). The fctypecompleter can be none or ‘unc’ (as for fcmp). fclass9 canspecify one of {NaTVal, QNaN, SNaN} OR none, one orboth of {positive, negative} AND none, one or severalof {zero, unnormal, normal, infinity} (nine bitscorrespond to the nine classes that can be selected, withthe restrictions specified on the possible combinations).

IA-64 FLOATING-POINT OPERATIONSDEFERRED TO SOFTWAREA number of floating-point operations defined by theIEEE Standard are deferred to software by the IA-64architecture in all its implementations:

• floating-point divide (integer divide, which is basedon the floating-point divide operation, is alsodeferred to software)

• floating-point square root

• floating-point remainder (integer remainder, basedon the floating-point divide operation, is alsodeferred to software)

• binary to decimal and decimal to binary conversions

• floating-point to integer-valued floating-pointconversion

• correct wrapping of the exponent for single, double,and double-extended precision results of floating-point operations that overflow or underflow, asdescribed by the IEEE Standard

In addition, the IA-64 architecture allows virtually anyfloating-point operation to be deferred to softwarethrough the mechanism of Software Assistance (SWA)requests, which are treated as floating-point exceptions.Software Assistance is discussed in detail in thesections describing the divide operation, the square rootoperation, and the exceptions and traps.

IA-64 FLOATING-POINT DIVIDE ANDREMAINDERThe floating-point divide algorithms for the IA-64architecture are based on the Newton-Raphson iterativemethod and on polynomial evaluation. If a/b needs to becomputed and the Newton-Raphson method is used, anumber of iterations first calculate an approximation of1/b, using the function

f(y) = b – 1/y

The iteration step is

en = (1 - b ⋅ yn)rn ≈ 0

yn+1 = (yn + en ⋅ yn )rn ≈ 1/b

where the subscript rn denotes the IEEE rounding to thenearest mode.

Once a sufficiently good approximation y of 1/b isdetermined, q = a ⋅ y approximates a/b. In some cases,this might need further refinement, which requires only afew more computational steps.

In order to show that the final result generated by thefloating-point divide algorithm represents the correctlyrounded value of the infinitely precise result a/b in anyrounding mode, it was proved (by methods described in[3] and [4]) that the exact value of a/b and the final resultq* of the algorithm before rounding belong to the sameinterval of width 1/2 ulp, adjacent to a floating-pointnumber. Then

(a/b)rnd = (q*)rnd

where rnd is any IEEE rounding mode.

The algorithms proposed for floating-point divide (aswell as for square root) are designed, as seen from theNewton-Raphson iteration step shown above, based onthe availability of the floating-point multiply-addoperation, fma, that performs both the multiply and addoperations with only one rounding error.

Two variants of floating-point divide algorithms areprovided for single precision, double precision, double-extended and full register file format precision, and SIMDsingle precision. One achieves minimum latency, andone maximizes throughput.

The minimum latency variant minimizes the executiontime to complete one operation. The maximumthroughput variant performs the operation using aminimum number of floating-point instructions. Thisvariant allows the best utilization of the parallelresources of the IA-64, yielding the minimum time peroperation when performing the operation on multiplesets of operands.

Double Precision Floating-Point DivideAlgorithmThe double precision floating-point divide algorithm thatminimizes latency was chosen to illustrate theimplementation of the mathematical algorithm in IA-64assembly language. The input values are the doubleprecision operands a and b, and the output is a/b.

All the computational steps are performed in full registerfile double-extended precision, except for steps (11) and(12), which are performed in full register file doubleprecision, and step (13), performed in double-precision.

Page 63: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

IA-64 Floating-Point Operations and the IEEE Standard for Binary Floating-Point Arithmetic 7

The approximate values are shown on the right-handside.

(1) y0 = 1/b ⋅ (1 + ε 0), |ε 0| ≤ 2 -m , m=8.886

(2) q0 = (a ⋅ y 0)rn = a/b ⋅ (1 + ε 0)

(3) e0 = (1 - b ⋅ y 0)rn ≈ - ε0

(4) y1 = (y0 + e0 ⋅ y 0)rn ≈ 1/b ⋅ (1 - ε02)

(5) q1 = (q0 + e0 ⋅ q0)rn ≈ a/b ⋅ (1 - ε02)

(6) e1 = (e02)rn ≈ ε0

2

(7) y2 = (y1 + e1 ⋅ y 1)rn ≈ 1/b ⋅ (1 - ε04)

(8) q2 = (q1 + e1 ⋅ q1)rn ≈ a/b ⋅ (1 - ε04)

(9) e2 = (e12)rn ≈ ε0

4

(10) y3 = (y2 + e2 ⋅ y 2)rn ≈ 1/b ⋅ (1 - ε08)

(11) q3 = (q2 + e2 ⋅ q2)rn ≈ a/b ⋅ (1 - ε08)

(12) r0 = (a - b ⋅ q 3)rn ≈ a ⋅ ε08

(13) q4 = (q3 + r0 ⋅ y3)rnd ≈ a/b ⋅ (1 - ε016)

The first step is a table lookup performed by frcpa, whichgives an initial approximation y0 of 1/b, with knownrelative error determined by m = 8.886. Steps (3) and (4),(6) and (7), and (9) and (10) represent three iterations thatgenerate increasingly better approximations of 1/b in y1 ,y2 , and y3. Note that step (2) above is exact: y0 has 11bits (read from a table), and a has 53 bits in thesignificand, and thus the result of the multiplication hasat most 64 bits that fit in the significand. Steps (5), (8),and (11) calculate three increasingly betterapproximations q1, q2 and q3 of a/b. Evaluating theirrelative errors and applying other theoretical properties[4], it was shown that q4 = (a/b)rnd in any IEEE roundingmode rnd, and that the status flag settings and exceptionbehavior are IEEE compliant. Assuming that the latencyof all floating-point operations is the same, the algorithmtakes seven fma latencies: steps (2) and (3) can beexecuted in parallel, as can steps (4), (5), (6); then (7), (8),(9) and also (10) and (11).

The implementation of this algorithm in assemblylanguage is shown next.

(1) frcpa.s0 f8,p6=f6,f7;; // y0=1/b in f8

(2) (p6) fma.s1 f9=f6,f8,f0 // q0=a*y0 in f9

(3) (p6) fnma.s1 f10=f7,f8,f1;; // e0=1-b*y0 in f10

(4) (p6) fma.s1 f8=f10,f8,f8 // y1=y0+e0*y0 in f8

(5) (p6) fma.s1 f9=f10,f9,f9 // q1=q0+e0*q0 in f9

(6) (p6) fma.s1 f11=f10,f10,f0;; // e1=e0*e0 in f11

(7) (p6) fma.s1 f8=f11,f8,f8 // y2=y1+e1*y1 in f8

(8) (p6) fma.s1 f9=f11,f9,f9 // q2=q1+e1*q1 in f9

(9) (p6) fma.s1 f10=f11,f11,f0;; // e2=e1*e1 in f10

(10) (p6) fma.s1 f8=f10,f8,f8 // y3=y2+e2*y2 in f8

(11) (p6) fma.d.s1 f9=f10,f9,f9;;//q3=q2+e2*q2in f9

(12) (p6) fnma.d.s1 f6=f7,f9,f6;; // r0=a-b*q3 in f6

(13) (p6) fma.d.s0 f8=f6,f8,f9;;// q4=q3+r0*y3 in f8

Note that the output predicate p6 of instruction (1)(frcpa) predicates all the subsequent instructions. Also,the output register of frcpa (f8) is the same as the outputregister of the last operation (in step (13)). If the frcpainstruction encounters an exceptional situation such asunmasked division by 0, and an exception handlerprovides the result of the divide, p6 is cleared and noother instruction from the sequence is executed. Theresult is still provided where it is expected. Anotherobservation is that the first and last instructions in thesequence use the user status field (sf0), which will reflectexceptions that might occur, while the intermediatecomputations use status field 1 (sf1, with wre = 1). Thisimplementation behaves like an atomic double precisiondivide, as prescribed by the IEEE Standard. It setscorrectly all the IEEE status flags (plus the denormalstatus flag), and it signals correctly all the possiblefloating-point exceptions if unmasked (invalid operation,denormal operand, divide by zero, overflow, underflow,or inexact result).

Floating-Point RemainderThe floating-point divide algorithms are the basis for theimplementation of floating-point remainder operations.Their correctness is a direct consequence of thecorrectness of the floating-point divide algorithms. Theremainder is calculated as r = a – n ⋅ b, where n is theinteger closest to the infinitely precise a/b. The problemis that n might require more bits to represent thanavailable in the significand for the format of a and b. Thesolution is to implement an iterative algorithm, asexplained in [5] for FPREM1 (all iterations but the last arecalled ‘incomplete’). The implementation (not shownhere) is quite straightforward. The rounding to zeromode for divide can be set in status field sf2 (otherwiseidentical to the user status field sf0). Status field sf0 willonly be used by the first frcpa (which may signal theinvalid, divide by zero, or denormal exceptions) and bythe last fnma (computing the remainder). The last fnmamay also signal the underflow exception.

Page 64: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

IA-64 Floating-Point Operations and the IEEE Standard for Binary Floating-Point Arithmetic 8

Software Assistance Conditions for ScalarFloating-Point DivideThe main issue identified in the process of proving theIEEE correctness of the divide algorithms [4] is that thereare cases of input operands for a/b that can causeoverflow, underflow, or loss of precision of anintermediate result. Such operands might prevent thesequence from generating correct results, and theyrequire alternate algorithms implemented in software inorder to avoid this. These special situations areidentified by the following conditions that define thenecessity for IA-64 Architecturally Mandated SoftwareAssistance for the scalar floating-point divideoperations:

(a) e b ≤ e min – 2 (y i might become huge)

(b) e b ≥ e max – 2 (y i might become tiny)

(c) e a – e b ≥ e max (qi might become huge)

(d) e a – e b ≤ e min + 1 (qi might become tiny)

(e) e a ≤ e min + N – 1 (ri might lose precision)

where ea is the (unbiased) exponent of a; eb is theexponent of b; emin is the minimum value of the exponentin the given format; emax is its maximum possible value;and N is the number of bits in the significand. When anyof these conditions is met, frcpa issues a SoftwareAssistance (SWA) request in the form of a floating-pointexception instead of providing a reciprocal approximationfor 1/b, and clears its output predicate. An SWAhandler provides the result of the floating-point divide,and the rest of the iterative sequence for calculating a/bis predicated off. The five conditions above can berepresented to show how the two-dimensional spacecontaining pairs (ea, eb) is partitioned into regions (Figure4 of [4]). Alternate software algorithms had to bedevised to compute the IEEE correct quotients for pairsof numbers whose exponents fall in regions satisfyingany of the five conditions above. Note though that dueto the extended internal exponent range (17 bits), thesingle precision, double precision, and double-extendedprecision calculations will never require architecturallymandated software assistance. This type of softwareassistance might be required only for floating-pointregister file format computations with floating-pointnumbers having 17-bit exponents.

When an architecturally mandated software assistancerequest occurs for the divide operation, the result isprovided by the IA-64 Floating-Point Emulation Library,which has the role of an SWA handler, as describedfurther.

The parallel reciprocal approximation instruction, fprcpa,does not signal any SWA requests. When any of thefive conditions shown above is met, fprcpa merely clearsits output predicate, in which case the result of theparallel divide operation has to be computed by alternatealgorithms (typically by unpacking the parallel operands,performing two single precision divide operations, andpacking the results into a SIMD result).

IA-64 FLOATING-POINT SQUARE ROOTThe IA-64 floating-point square root algorithms are alsobased on Newton-Raphson or similar iterativecomputations. If √a needs to be computed and theNewton-Raphson method is used, a number of Newton-Raphson iterations first calculate an approximation of1/√a, using the function

f(y) = 1/y2 - a

The general iteration step is

en = (1/2 – 1/2 ⋅ a ⋅ yn2)rn

yn+1 = (yn + en ⋅ yn )rn

where the subscript rn denotes the IEEE rounding to thenearest mode. The first computation above is rearrangedin the real algorithm in order to take advantage of the fmainstruction capability.

Once a sufficiently good approximation y of 1/√a isdetermined, S = a ⋅ y approximates √a. In certain cases,this too might need further refinement.

In order to show that the final result generated by afloating-point square root algorithm represents thecorrectly rounded value of the infinitely precise result √ain any rounding mode, it was proved (by methodsdescribed in [3] and [4]), that the exact value of √a andthe final result R* of the algorithm before roundingbelong to the same interval of width 1/2 ulp, adjacent to afloating-point number. Then, just as for the divideoperation

(√a)rnd = (R*)rnd

where rnd is any IEEE rounding mode.

Floating-point square root algorithms are provided forsingle precision, double precision, double-extended andfull register file format precision, and for SIMD singleprecision, in two variants. One achieves minimumlatency, and one maximizes throughput.

SIMD Floating-Point Square Root AlgorithmWe next present as an example the algorithm that allowscomputing the SIMD single precision square root, andwhich is optimized for throughput, having a minimum

Page 65: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

IA-64 Floating-Point Operations and the IEEE Standard for Binary Floating-Point Arithmetic 9

number of floating-point instructions. The inputoperand is a pair of single precision numbers (a1, a2). Theoutput is the pair (√a1, √a2). All the computational stepsare performed in single precision. The algorithm isshown below as a scalar computation. The approximatevalues shown on the right-hand side are computedassuming no rounding errors occur, and they neglectsome high order terms that are very small.

(1) y0 = 1/√a ⋅ (1 + ε 0), |ε 0| ≤ 2 -m , m=8.831

(2) h = (1/2 ⋅ y0)rn ≈ (1 / (2⋅√a)) ⋅ (1 + ε 0)

(3) t1 = (a ⋅ y0)rn ≈ √a ⋅ (1 + ε 0)

(4) t2 = (1/2 - t1 ⋅ h)rn ≈ -ε 0 – 1/2 ⋅ ε 02

(5) y1 = (y0 + t2 ⋅ y0)rn ≈ 1/√a ⋅ (1 – 3/2⋅ε 02)

(6) S = (a ⋅ y1)rn ≈ √a ⋅ (1 – 3/2 ⋅ ε 02)

(7) H = (1/2 ⋅ y1)rn ≈ (1 / (2⋅√a)) ⋅ (1 – 3/2⋅ε 02)

(8) d = (a - S ⋅ S)rn ≈ a ⋅ (3⋅ε 02 – 9/4⋅ε 0

4)

(9) t4 = (1/2 - S ⋅ H)rn ≈ 3/2 ⋅ ε 02 – 9/8⋅ε 0

4

(10) S1 = (S + d ⋅ H)rn ≈ √a ⋅ (1 – 27/8⋅ε 04)

(11) H1 = (H + t4 ⋅ H)rn ≈ (1 / (2⋅√a))⋅(1 – 27/8⋅ε04)

(12) d1 = (a - S1 ⋅ S1)rn ≈ a ⋅ (27/4⋅ε 04 – 729/64⋅ε0

8)

(13) R = (S1 + d1 ⋅ H1)rnd ≈ √a ⋅ (1 – 2187/128⋅ε 08)

The first step is a table lookup performed by fprsqrta,which gives an initial approximation of (1/√a1,1/√a2) withknown relative error determined by m = 8.831. Thefollowing steps implement a Newton-Raphson iterativealgorithm. Specifically, step (5) improves on theapproximation of (1/√a1,1/√a2). Steps (3), (6), (10) and(13) calculate increasingly better approximations of(√a1,√a2). The algorithm was proved correct as outlinedin [3] and [4]. The final result (R1,R2) equals((√a1)rnd,(√a2)rnd) for any IEEE rounding mode rnd, and thestatus flag settings and exception behavior are IEEEcompliant.

The assembly language implementation is as follows(only the floating-point operations are numbered):

movl r3 = 0x3f0000003f000000;; // +1/2,+1/2

setf.sig f7=r3 // +1/2,+1/2 in f7

(1) fprsqrta.s0 f8,p6=f6;; // y0=1/sqrt(a) in f8

(2) (p6) fpma.s1 f9=f7,f8,f0 // h=1/2*y0 in f9

(3) (p6) fpma.s1 f10=f6,f8,f0;; // t1=a*y0 in f10

(4) (p6) fpnma.s1 f9=f10,f9,f7;;// t2=1/2-t1*h in f9

(5) (p6) fpma.s1 f8=f9,f8,f8;; // y1=y0+t2*y0 in f8

(6) (p6) fpma.s1 f9=f6,f8,f0 // S=a*y1 in f9

(7) (p6) fpma.s1 f8=f7,f8,f0;; // H =1/2*y1 in f8

(8) (p6) fpnma.s1 f10=f9,f9,f6 // d=a-S*S in f10

(9) (p6) fpnma.s1 f7=f9,f8,f7;; // t4=1/2-S*H in f7

(10) (p6) fpma.s1 f10=f10,f8,f9// S1=S+d*H in f10

(11) (p6) fpma.s1 f7=f7,f8,f8;; // H1=H+t4*H in f7

(12) (p6) fpnma.s1 f9=f10,f10,f6;;// d1=a-S1^2 in f9

(13) (p6) fpma.s0 f8=f9,f7,f10;;//R=S1+d1*H1 in f8

Software Assistance Conditions for ScalarFloating-Point Square RootJust as for divide, cases of special input operands wereidentified in the process of proving the IEEE correctnessof the square root algorithms [4]. The difference withrespect to divide is that only loss of precision of anintermediate result can occur in an iterative algorithmcalculating the floating-point square root. Suchoperands might prevent the sequence from generatingcorrect results, and they require alternate algorithmsimplemented in software in order to avoid this. Thesespecial situations are identified by the followingcondition that defines the necessity for IA-64Architecturally Mandated Software Assistance for thescalar floating-point square root operation:

e a ≤ e min + N – 1 (d i might lose precision)

where ea is the (unbiased) exponent of a, emin is theminimum value of the exponent in the given format, andN is the number of bits in the significand. When thiscondition is met, frsqrta issues a Software Assistancerequest in the form of a floating-point exception, insteadof providing a reciprocal approximation for 1/√a, and itclears its output predicate. The result of the floating-point square root operation is provided by an SWAhandler, and the rest of the iterative sequence forcalculating √a is predicated off. Due to the extendedinternal exponent range (17 bits), the single precision,double precision, and double-extended precisioncalculations will never require architecturally mandatedsoftware assistance. This type of software assistancemight be required only for floating-point register fileformat computations with floating-point numbers having17-bit exponents.

When an architecturally mandated software assistancerequest occurs for the square root operation, the result isprovided by the IA-64 Floating-Point Emulation Library.

Just as for the parallel divide, the parallel reciprocalsquare root approximation instruction, fprsqrta, does notsignal any SWA requests. When the condition shown

Page 66: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

IA-64 Floating-Point Operations and the IEEE Standard for Binary Floating-Point Arithmetic 10

above is met, fprsqrta merely clears its output predicate,in which case the result of the parallel square rootoperation has to be computed by alternate algorithms(typically by unpacking the parallel operands, performingtwo single precision square root operations, and packingthe results into a SIMD result).

DERIVED OPERATIONS: INTEGERDIVIDE AND REMAINDERThe integer divide and remainder operations are basedon floating-point operations. They are not specified inthe IEEE Standard [1], but their implementation is soclose to that of the floating-point operations mandatedby the standard, that it is worthwhile mentioning themhere.

A 64-bit integer divide algorithm can be implementedbased on the double-extended precision floating-pointdivide. A 32-bit integer divide algorithm can use thedouble precision divide. The 16-bit and 8-bit integerdivide can use the single precision divide. But thedesired computation can be performed in each case byshorter instruction sequences. For example, 24 bits ofprecision are not needed to implement the 16-bit integerdivide.

As examples, the signed and then unsigned 16-bitinteger divide algorithms are presented. They are bothbased on the same core (all four steps below areperformed in full register file double-extended precision):

(1) y0 = 1/b ⋅ (1 + ε 0), |ε 0| ≤ 2 -m , m=8.886

(2) q0 = (a ⋅ y 0)rn = a/b ⋅ (1 + ε 0)

(3) e0 = (1 + 2-17 - b ⋅ y0)rn ≈ - ε0 (adding 2-17

ensures correctness of the final result)

(4) q1 = (q0 + e0 ⋅ q 0)rn ≈ a/b ⋅ (1 - ε02)

The assembly language implementation of the 16-bitsigned integer divide algorithm follows. It is assumedthat the 16-bit operands are received in r32 and r33, andthe result is returned in r8.

sxt2 r2=r32 // sign-extend dividend

sxt2 r3=r33;; // sign-extend divisor

setf.sig f8=r2 // integer dividend in f8

setf.sig f9=r3 // integer divisor in f9

movl r9=0x8000400000000000;;// 1 + 2-17 in r9

setf.sig f10=r9 // (1 + 2-17) ⋅ 263 in f10

fcvt.xf f6=f8 // normal fp dividend in f6

fcvt.xf f7=f9;; // normal fp divisor in f7

fmerge.se f10=f1,f10 // 1 + 2-17 in f10

(1) frcpa.s1 f8,p6=f6, f7;; // y0 in f8

(2) (p6) fma.s1 f9=f6, f8, f0 // q0 = a * y0 in f9

(3) (p6) fnma.s1 f10=f8,f7,f10;; //e0=(1+2-17)-b*y0

(4) (p6) fma.s1 f8=f9,f10,f9;;// q1=q0+e0*q0 in f8

fcvt.fx.trunc.s1 f8=f8;; // integer quotient in f8

getf.sig r8=f8;; // integer quotient in r8

The 16-bit unsigned integer divide is similar, but uses thezero-extend instead of the sign-extend instruction from 2bytes to 8 bytes (zxt2 instead of sxt2), conversion fromunsigned integer to floating-point for the operands(fcvt.xuf instead of fcvt.xf), and conversion from floating-point to unsigned integer for the result (fcvt.fxu.truncinstead of fcvt.fx.trunc).

The integer remainder algorithms are implemented asextensions of the corresponding integer dividealgorithms. The 16-bit signed integer remainderalgorithm is almost identical to the 16-bit signed integerdivide, with the last instruction replaced by the followingsequence that is needed to calculate r = a - (a/b) ⋅ b:

fcvt.xf f8=f8;; // convert to fp and normalize

fnma.s1 f8=f8, f7, f6;; // r = a - (a/b) ⋅ b in f8

fcvt.fx.trunc.s1 f8=f8;;// integer remainder in f8

getf.sig r8=f8;; // integer remainder in r8

EXCEPTIONS AND TRAPSIA-64 arithmetic floating-point instructions [2] maysignal all of the five IEEE-specified exceptions and alsothe Intel Architecture defined exception for denormaloperands. Invalid operation, denormal operand, anddivide-by-zero are pre-computation exceptions (floating-point faults). Overflow, underflow, and inexact result arepost-computation exceptions (floating-point traps).

In addition to these user visible exceptions, SoftwareAssistance (SWA) faults and traps can be signaled.They do not surface to the user level, and cannot bedisabled (masked). The SWA requests are handled by asystem SWA handler, the IA-64 Floating-PointEmulation Library.

The status flags in a given status field can be clearedusing the fclrf.sf instruction. Control bits may be setusing the fsetc.sf amask7, omask7 instruction, whichinitializes the seven control bits of the specified statusfield to the value obtained by logically AND-ing thesf0.controls (seven bits) and amask7, and logically OR-ing with omask7. Alternatively, a 64-bit unsigned

Page 67: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

IA-64 Floating-Point Operations and the IEEE Standard for Binary Floating-Point Arithmetic 11

integer value can be moved to or from the FPSR(application register 40): mov ar40 = r1, ormov r1 = ar40.

Exception handlers can be registered, disabled, saved, orrestored with software support (from the operatingsystem and/or compiler) as specified by the IEEEStandard.

IA-64 Software Assistance Faults and TrapsThe IA-64 architecture allows virtually any floating-pointoperation to be deferred to software through themechanism of Software Assistance requests, which aretreated as floating-point exceptions, always unmasked,and resolved without reaching a user handler. OnItanium™, the first implementation of the IA-64architecture, SWA requests may be signaled in threeforms:

• IA-64 architecturally mandated SWA faults. Theseoccur for certain combinations of operands of thefloating-point divide and square root operations,and only for frcpa and frsqrta (scalar reciprocalapproximation instructions).

• Itanium-specific SWA faults. They occur whenevera floating-point instruction has a denormal input. Allthe arithmetic floating-point instructions on Itaniumsignal this exception, except for fma.pc.sf f1 = f2, f1,f0 (fnorm.pc.sf f1=f2), fms.pc.sf f1 = f2, f1, f0, andfnma.pc.sf f1 = f2, f1, f0. They signal Itanium-specificSWA faults only when the input is a canonicaldouble-extended denormal value (i.e., when theinput has a biased exponent of 0x00000 and a mostsignificant bit of the non-zero significand equal to0).

• Itanium-specific SWA traps. They occur whenevera floating-point instruction has a tiny result (smallerin magnitude than the smallest normal floating-pointnumber that can be represented in the destinationformat). These exceptions only occur for fma, fms,fnma, fpma, fpms, and fpnma.

The IA-64 Floating-Point Emulation LibraryWhen an unmasked floating-point exception occurs, thehardware causes a branch to the interruption vector

(Floating-Point Fault or Trap Vector) and then to a low-level OS handler. From here, handling of the floating-point exception is propagated higher in the operatingsystem, and an exception handler is invoked that decideswhether to provide a result for the excepting instructionand allow execution of the application to continue.

SWA requests are treated like regular floating-pointexceptions, but they are always ‘unmasked’ and handledby an SWA handler represented by the IA-64 Floating-Point Emulation Library. The library is able to calculatethe result for any IA-64 arithmetic floating-pointinstruction. When an SWA fault or trap occurs, it isprocessed and the result is provided to the operatingsystem kernel. The execution continues in a transparentmanner for the user. In addition to satisfying the SWArequests, the SWA handler filters all other unmaskedfloating-point exceptions that occur, passing them to theoperating system kernel that will continue to search foran appropriate user-provided exception handler.

Figure 1 depicts the control flow that occurs when anapplication running on an IA-64 processor signals anunmasked floating-point exception. The IA-64 Floating-Point Emulation Library is shown as part of the operatingsystem kernel, but this is implementation dependent. Ifan unmasked floating-point exception other than anSWA fault or trap occurs, a user handler must havealready been registered in order to resolve it. The userhandler can be called directly by the operating system,receiving ‘raw’ information about the exception, orthrough an optional IEEE filter (as shown in Figure 1)that processes the information about the exception,thereby allowing a less complex handler to resolve thesituation.

An example of an SWA trap is illustrated in Figure 2. Thecomputation generates a result that is tiny and inexact(sufficient to trigger an underflow or an inexact trap ifany was unmasked). As traps are masked, an Itanium™-specific SWA trap occurs, propagated from theapplication code to the floating-point emulation libraryvia the OS kernel trap handler in steps 1 and 2. The resultgenerated by the emulation library is then passed back insteps 3 and 4.

Page 68: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

IA-64 Floating-Point Operations and the IEEE Standard for Binary Floating-Point Arithmetic 12

Figure 1: Flow of control for IA-64 floating-point exceptions

Figure 2: Flow of control for handling an SWA trapsignaled by an IA-64 floating-point instruction

Page 69: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

IA-64 Floating-Point Operations and the IEEE Standard for Binary Floating-Point Arithmetic 13

FLOATING-POINT EXCEPTIONHANDLINGThe floating-point exception priority is documented in[2], but for a given implementation of the architecture(Itanium™ in this case), a distinction can be maderegarding the source of an exception. This can besignaled by the hardware, or from software, by the IA-64Floating-Point Emulation Library.

For example, on Itanium, denormal faults are signaled bysoftware (the IA-64 Floating-Point Emulation Library)after they are reported initially by the hardware asItanium-specific SWA faults. SWA faults that are notconverted to denormal faults (because denormal faultsare masked) cause the result to be calculated bysoftware. Whether the result of a floating-pointinstruction is calculated in hardware or in software, it canfurther signal other floating-point exceptions (traps).

For example, architecturally mandated SWA faults mightlead to overflow, underflow, or inexact exceptionssignaled from the IA-64 Floating-Point Emulation Library.

Another example is that of the SWA traps, that arealways raised from hardware. They have to be resolvedin software, but this computation might further lead toinexact exceptions signaled from the IA-64 Floating-PointEmulation Library.

The information that is relevant to a floating-point userexception handler is passed to it through a register filesave area, the excepting instruction pointer and opcode,the Floating-Point Status Register, and a set ofspecialized registers.

The IA-64 IEEE Floating-Point FilterThe floating-point exception handling mechanism of anoperating system raises portability issues, as exceptionhandling is almost always implemented using proprietarydata structures and procedures. A solution that can beadopted is to implement an IEEE Floating-Point Filter thatpreprocesses the exception information provided by the

operating system kernel before passing it on to the userhandler (see Figure 1). The filter, which can be viewed aspart of the user handler, helps in the processing of all theIEEE floating-point exceptions (invalid operation, divide-by-zero, overflow, underflow, and inexact result) and alsoin the processing of the denormal exceptions that are IAspecific. The interface between the operating systemand the IEEE filter should be almost identical to that ofthe IA-64 Floating-Point Emulation Library, as they bothprocess exception information. The IEEE filter alsoaccomplishes the correct wrapping of the exponentswhen overflow or underflow traps are taken, as requiredby the IEEE Standard [1] (operation deferred to softwareby the IA-64 architecture).

An important advantage is that the IEEE Floating-PointFilter simplifies greatly the task of the user handler. Allthe complexities of reading operating system-specificinformation, decoding operation codes, and reading andwriting floating-point or predicate registers areabstracted away by the filter. Also, exceptionsgenerated by parallel (SIMD) instructions will appear tothe user handler as originating in scalar instructions. Thefollowing two examples illustrate some of these benefits.

The example in Figure 3 illustrates the case of a scalardivide operation that signals an SWA fault, and then anunderflow trap (underflow traps are assumed to beunmasked). The SWA fault is signaled by an frcpainstruction that jumpstarts the iterative computationcalculating the quotient. The sequence of stepsperformed in handling the exception is numbered from 1to 10 in the figure. As the result is provided by the userexception handler for underflow exceptions, the outputpredicate of frpca has to be clear when execution of theapplication program containing it is resumed (clearingthe output predicate is the task of the user handler or ofthe IEEE Floating-Point Exception Filter if present). Theclear output predicate disables the iterative computationfollowing frcpa, as the result is already in the correctfloating-point register (the iterative computation isassumed to be automatically inlined by the compiler).

Page 70: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

IA-64 Floating-Point Operations and the IEEE Standard for Binary Floating-Point Arithmetic 14

Figure 3: Flow of control for handling an SWA faultsignaled by a divide operation, followed by an underflowtrap

The example in Figure 4 illustrates the case of a parallelinstruction that signals an invalid fault in the high half,and an underflow trap in the low half, with no SWArequests involved. Both invalid and underflowexceptions are assumed to be unmasked (enabled). Asonly the fault is detected first, the IEEE filter tries to re-execute the low half of the instruction, generating a newexception (underflow trap). The sequence of stepsexecuted while handling these exceptions is numberedfrom 1 to 12 in the figure.

Page 71: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

IA-64 Floating-Point Operations and the IEEE Standard for Binary Floating-Point Arithmetic 15

Figure 4: Flow of control for handling an invalid fault inthe high half (V high) and an underflow trap in the low

half (U low) of a parallel IA-64 instruction

SPECULATION FOR FLOATING-POINTCOMPUTATIONSControl speculation refers to a performance optimizationtechnique where a sequence of instructions is executedbefore it is known that the dynamic control flow of theprogram will actually reach the point in the programwhere the sequence of instructions is needed. Controlspeculation in floating-point computations on IA-64processors is possible, as loads to general or floating-point registers have both non-speculative (e.g., ldf, ldfp),and speculative (e.g., ldf.s, ldfp.s) variants. Allinstructions that write their results to general or floating-point registers are speculative.

A speculative floating-point computation uses statusfields sf2 or sf3. The computation is considered to havefailed if it signals a floating-point exception that isunmasked in the user status field sf0, or if it sets a statusflag that is clear in sf0. This is checked for with thefloating-point check flags instruction, fchkf.sf target25:the status flags in sf are compared with the status flags insf0. If any flags in sf are set and the corresponding trapsare enabled, or if any flags are set in sf that are not set insf0, then a branch is taken to target25, which should bethe address of the recovery code for the failed

speculative floating-point computation. The compliancewith the IEEE Standard is thus preserved even forspeculative chains of computation.

The following example shows original code withoutcontrol speculation. It is assumed that the contents of f9are not used at the destination of the branch.

(p6) br.cond some_label ;;

fma.s0 f9=f8,f7,f6 // Do f9=f8*f7+f6

continue_label:

This code sequence can be rewritten using controlspeculation with sf2 to move the fma ahead of the branchas follows:

fma.s2 f9=f8,f7,f6 // Speculative f9=f8*f7+f6

// other instructions

(p6) br.cond some_label ;;

fchkf.s2 recovery_label // Check speculation

continue_label:

If sf0 and sf2 do not agree, then the recovery code mustbe executed to cause the actual exception with sf0.

recovery_label:

fma.s0 f9=f8,f7,f6 // Do real f9=f8*f7+f6

br continue_label

Page 72: Intel Technology Journal Q4, 1999...register file to eliminate redundant references to array elements and to expose more parallelism to the scheduler and code generator. Scalar replacement

Intel Technology Journal Q4, 1999

IA-64 Floating-Point Operations and the IEEE Standard for Binary Floating-Point Arithmetic 16

CONCLUSIONCompliance with the IEEE Standard for Binary Floating-Point Arithmetic [1] is important for any modernprocessor. In this paper, we have shown how variousfacets of the standard are implemented or reflected in theIA-64 architecture, which is fully compliant with the IEEEStandard. In addition, we have highlighted features ofthe floating-point architecture that allow high-accuracyand high-performance computations, while abiding bythe IEEE Standard.

ACKNOWLEDGMENTSThe authors thank Roger Golliver, Gautam Doshi, JohnHarrison, Shane Story, Ted Kubaska, and CristinaIordache from Intel Corporation, and Peter Marksteinfrom Hewlett-Packard* Company for their contributions,support, ideas, and/or feedback regarding various partsof this paper.

REFERENCES[1] ANSI/IEEE Std 754-1985, IEEE Standard for Binary

Floating-Point Arithmetic, IEEE, New York, 1985.

[2] IA-64 Application Developer’s Architecture Guide,Intel Corporation, 1999.

[3] Cornea-Hasegan, M., “Proving IEEE Correctness ofIterative Floating-Point Square Root, Divide, andRemainder Algorithms,” Intel Technology Journal,Q2, 1998 athttp://developer.intel.com/technology/itj/q21998.htm

[4] Cornea-Hasegan, M. and Golliver, R., “CorrectnessProofs Outline for Newton-Raphson Based Floating-Point Divide and Square Root Algorithms,”Proceedings of the 14th IEEE Symposium onComputer Arithmetic, 1999, IEEE Computer Society,Los Alamitos, CA, pp. 96-105.

[5] Intel Architecture Software Developer’s Manual,Intel Corporation, 1997.

AUTHORS’ BIOGRAPHIESMarius Cornea-Hasegan is a senior staff softwareengineer with Intel Corporation in Hillsboro, Oregon. Heholds an M.Sc. degree in electrical engineering from thePolytechnic Institute of Cluj, Romania, and a Ph.D.degree in computer science from Purdue University, inWest Lafayette, IN. His interests include floating-pointarchitecture and algorithms as parts of a computingsystem. His e-mail is [email protected].

Bob Norin joined Intel in 1994. Currently he is managerof the MSL Numerics Group in MPG. Bob received his

Ph.D. degree in electrical engineering from CornellUniversity. He has over 25 years experience developingsoftware for high-performance computers. His technicalinterests include optimizing the performance of floating-point applications and developing mathematical librarysoftware. His e-mail is [email protected].