This chapter contains the following sections:
Introduction to XA C Cross-Compiler
General Implementation
Compiler Phases
Frontend Optimizations
Backend Optimizations
Sample Session
Using EDE
Using the Control Program
Using the Makefile
This manual provides a functional description of the TASKING XA C Cross-Compiler. This manual uses cxa (the name of the binary) as a shorthand notation for "TASKING XA C Compiler".
The TASKING XA C compiler accepts source programs written in ANSI C and translates these into XA assembly source code files. The XA C cross-compiler generates code for the XA operating in 'native' mode. The 80C51 compatibility mode is not supported. The compiler accepts language extensions to improve code performance and to allow the use of typical XA architectural provisions efficiently at the C level. The compiler is ANSI C compatible and consists of three major parts; the preprocessor, the XA C frontend and the associated backend or code generator. These are all integrated into a single program to avoid the need of intermediate files, thus speeding up the compilation process. It also simplifies the implementation of joint frontend-backend optimization strategies and preprocessor pragmas. This effectively makes the compiler a one pass compiler, with minimum file I/O overhead.
The compiler processes one C function at a time, until the entire source module has been read. The function is parsed, checked on semantic correctness and then transformed into an intermediate code tree that is stored in memory. Code optimizations are performed during the construction of the intermediate code, and are also applied when the complete function has been processed. The latter are often referred to as global optimizations.
cxa generates assembly source code using the XA assembly language specification, you must assemble this code with the TASKING XA Cross-Assembler. This manual uses asxa as a shorthand notation for "TASKING XA Cross-Assembler".
You can link the generated object with other objects and libraries using the TASKING lkxa XA linker. In this manual we use lkxa as a shorthand notation for "TASKING lkxa XA linker". You can locate the linked object to a complete application using the TASKING lcxa XA locator. In this manual we use lcxa as a shorthand notation for "TASKING lcxa XA locator".
The program ccxa is a control program. The control program facilitates the invocation of various components of the XA toolchain. ccxa recognizes several filename extensions. C source files (.c) are passed to the compiler. Assembly sources (.asm) are preprocessed and passed to the assembler. Relocatable object files (.obj) and libraries (.a) are recognized as linker input files. Files with extension .out and .dsc are treated as locator input files. The control program supports options to stop at any stage in the compilation process and has options to produce and retain intermediate files.
You can debug the software written in C with the TASKING CrossView Pro high-level language debugger. This manual uses XVW as a shorthand notation for "TASKING CrossView Pro high-level language debugger". A list of supported platforms and emulators is available from TASKING.
TASKING XA C Cross-Compiler
TK012-002
TASKING XA Cross-Assembler
TK012-000 (included in TK012-002)
TASKING XA CrossView Pro debugger (TK012-043)
All XA derivatives. Special function registers can be accessed by means of a user-definable register file.
This section describes the different phases of the compiler and the target independent optimizations.
During the compilation of a C program, a number of phases can be identified. These phases are divided into two groups, referred to as frontend and backend.
The preprocessor phase:
The scanner phase:
The parser phase:
The frontend optimization phase:
The backend optimization phase:
The code generator phase:
The peephole optimizer:
All phases (of both frontend and backend) of the compiler are combined into one program. The compiler does not use intermediate files for communication between the different phases of compilation. The backend part is not called for each C statement, but starts after a complete C function has been processed by the frontend (in memory), thus allowing more optimization. The compiler only requires one pass over the input file, resulting in relatively fast compilation.
The command line option -O controls the amount of optimization applied on the C source. Within a source file, the pragma #pragma optimize sets the optimization level of the compiler. Using the pragma, certain optimizations can be switched on or off for a particular part of the program. Several optimizations cannot be controlled individually. e.g., constant folding will always be done.
The compiler performs the following optimizations on the intermediate code. They are independent of the target processor and the code generation strategy:
Expressions only involving constants are replaced by their result.
Expressions are rearranged to allow more constant folding. E.g. 1+ (x-3) is transformed into x + (1-3), which can be folded.
Multiplication by 0 or 1 and additions or subtractions of 0 are removed. Such useless expressions may be introduced by macros, or by the compiler itself (e.g., array subscription).
Expressions involving '&&', '||' and '!' are interpreted and translated into a series of conditional jumps.
With for and while loops, the expression is evaluated once at the 'top' and then at the 'bottom' of the loop. This optimization does not save code, but speeds up execution.
A number of optimizations of a switch statement are performed, such as the deletion of redundant case labels or even the deletion of the switch.
By reversing jump conditions and moving code, the number of jump instructions is minimized. This reduces both the code size and the execution time.
A conditional or unconditional jump to a label which is immediately followed by an unconditional jump may be replaced by a jump to the destination label of the second jump. This optimization does not save code, but speeds up execution.
An unconditional jump to a label directly following the jump is removed. A conditional jump to such a label is replaced by an evaluation of the jump condition. The evaluation is necessary because it may have side effects.
A conditional jump over an unconditional jump is transformed into one conditional jump with the jump condition reversed. This reduces both the code size and the execution time.
Identical code sequences in two different execution paths are merged when this is possible without adding extra instructions. This transformation decreases code size rather than execution time, but under certain circumstances it avoids the execution of one jump.
A reference to a variable with known contents is replaced by those contents.
The compiler has the ability to detect repeated uses of the same (sub-) expression. Such a "common" expression may be temporarily saved to avoid recomputation. This method is called common subexpression elimination, abbreviated CSE.
Unreachable code can be removed from the intermediate code without affecting the program. However, the compiler generates a warning message, because the unreachable code may be the result of a coding error.
Invariant expressions may be moved out of a loop and expressions involving an index variable may be reduced in strength.
Eliminate short loops by replacing them with a number of copies.
String literals and floating point constants are put in ROM memory. The compiler overlays identical strings (within the same module) and let them share the same space, thus saving ROM space. Likewise identical floating point constants are overlaid and allocated only once.
The following optimizations are target dependent and are therefore performed by the backend.
Variables, parameters, intermediate results and common subexpressions are represented in allocation units. Per function, the compiler builds a graph of allocation units which indicates which units are needed and when. This allows the register allocator to get the most efficient occupation of the available registers. The compiler uses the allocation graph to generate the assembly code.
The generated assembly code is improved by replacing instruction sequences by equivalent but faster and/or shorter sequences, or by deleting unnecessary instructions.
Leaf functions (function not calling other functions), are handled specially with respect to stack frame building.
Expressions from which the result is never used are eliminated.
Only resources required by the interrupt function are saved.
Replace a recursion statement to branch to the beginning of the statement.
Three ways of code generation for a switch statement are supported: a jump chain (linear switch), a jump table, or a binary search table.
If you want to build ax XA application you need to invoke the following programs directly, or via the control program:
You can directly load the output file of the locator with extension .abs into the CrossView Pro debugger.
The next figure explains the relationship between the different parts of the TASKING XA toolchain:
Figure 2-1: XA development flow
The program ccxa is a so-called control program, which facilitates the invocation of various components of the XA toolchain. C++ source programs are compiled by the C++ compiler, C source programs are compiled by the compiler, assembly source files are passed to the assembler. A C preprocessor program is available as an integrated part of the C compiler. The control program recognizes the file extensions .a and .obj as input files for the linker. The control program passes files with extensions .out and .dsc to the locator. All other files are considered to be object files and are passed to the linker. The control program has options to suppress the locating stage (-cl), the linker stage (-c) or the assembler stage (-cs).
Optionally the locator, lcxa produces output files in Motorola S-record format or Intel Hex format. The default output format is IEEE-695.
Normally, the control program removes intermediate compilation results, as soon as the next phase completes successfully. If you want to retain all intermediate files, the option -tmp prevents removal of these files.
For a description of all utilities available and the possible output formats of the locator, see the XA Cross-Assembler, Linker/Locator, Utilities User's Guide.
The name of the XA CrossView Pro Debugger is xfwxa For more information check the XA CrossView Pro Debugger User's Guide.
This section contains an overview of the environment variables used by the XA toolchain.
Environment Variable | Description |
ASXAINC | Specifies an alternative path for include files for the assembler. |
CXAINC | Specifies an alternative path for #include files for the C compiler cxa. |
CXALIB | Specifies a path to search for library files used by the linker lkxa. |
CCXABIN | When this variable is set, the control program, ccxa, prepends the directory specified by this variable to the names of the tools invoked. |
CCXAOPT | Specifies extra options and/or arguments to each invocation of ccxa. The control program processes the arguments from this variable before the command line arguments. |
LM_LICENSE_FILE | Identifies the location of the license data file. Only needed for hosts that need the FLEXlm license manager. |
PATH | Specifies the search path for your executables. |
TMPDIR | Specifies an alternative directory where programs can create temporary files. Used by cxa, ccxa, asxa, lkxa, lcxa, arxa. |
Table 2-1: Environment variables
The subdirectory dhry in the examples subdirectory contains a demo program for the XA toolchain.
In order to debug your programs, you will have to compile, assemble, link and locate them for debugging using the TASKING XA tools. You can do this with one call to the control program or you can use EDE, the Embedded Development Environment (which uses a project file and a makefile) or you can call the makefile from the command line.
EDE stands for "Embedded Development Environment" and is the Windows oriented Integrated Development Environment you can use with your TASKING toolchain to design and develop your application.
To use EDE on the dhry demo program in the subdirectory dhry in the examples subdirectory of the XA product tree follow the steps below.
A detailed description of the process using the sample program dhry.c is described below. This procedure is outlined as a guide for you to build your own executables for debugging.
The dialog boxes shown in this manual serve as an example. They may slightly differ from the ones in your product.
You can launch EDE by double-clicking on the EDE shortcut on your desktop.
The EDE screen provides you with a menu bar, a toolbar (command buttons) and one or more windows (for example, for source files), a status bar and numerous dialog boxes.
EDE supports all the TASKING toolchains. When you first start EDE, the toolchain of the product you purchased is selected and displayed in the title of the EDE desktop window.
If you selected the wrong toolchain or if you want to change toolchains do the following:
1. Access the EDE menu and select the Select Toolchain... menu item. This opens the Select Toolchain dialog.
2. Select the toolchain you want. You can do this by clicking on a toolchain in the Toolchains list box and press OK.
If no toolchains are present, use the Browse... or Scan Disk... button to search for a toolchain directory. Use the Browse... button if you know the installation directory of another TASKING product. Use the Scan Disk... button to search for all TASKING products present on a specific drive. Then return to step 2.
Follow these steps to open an existing project:
1. Access the Project menu and select Set Current....
2. Select the project file to open. For the demo program select the file dhry.pjt in the subdirectory dhry in the examples subdirectory of the XA product tree. If you have used the defaults, the file dhry.pjt is in the directory c:\cxa\examples\dhry.
The next two steps are not needed for the demo program because the files dhry_1.c, dhry_2.c and timers_b.c are already open. To load the file you want to look at:
1. In the Project menu click on Load files....
This opens the Choose Project Files to Edit dialog.
2. Choose the file(s) you want to open by clicking on it. You can select multiple files by pressing the <Ctrl> or <Shift> key while you click on a file. With the <Ctrl> key you can make single selections and with the <Shift> key you can select everything from the first selected file to the file you click on. Then press the OK button.
This launches the file(s) so you can edit it (them).
The next step is to compile the file(s) together with its dependent files so you can debug the application.
Steps 1 and 2 are optional. Follow these steps if you want to specify additional build options such as to stop the build process on errors and to select a command to be executed as foreground or background process.
1. Access the EDE menu and select the Build Options... menu item.
This opens the Build Options dialog.
If you set the Show command line options at the bottom of a tool tab check box EDE shows the command line equivalent of the selected tool option. You can also click on the arrow button (left of the OK button) in a tool options dialog.
2. Make your changes and press the OK button.
3. Select the EDE | Directories menu item and check the directory paths for programs, include files and libraries. You can add your own directories here, separated by semicolons.
4. Access the EDE menu and select the Scan All Dependencies menu item.
5. Click on the Execute 'Make' command button. The following button is the execute Make button which is located in the ribbon bar.
If there are any unsaved files, EDE will ask you in a separate dialog if you want to save them before starting the build.
Once the files have been processed you can inspect the generated messages in the Build tab:
TASKING program builder vx.y rz SN00000001-018 (c) year TASKING, Inc. Compiling dhry_2.c Assembling dhry_2.src Compiling timers_b.c Assembling timers_b.src Compiling dhry_1.c Assembling dhry_1.src Linking to dhry.out Creating IEEE-695 absolute file dhry.abs
Once the files have been compiled, assembled, linked, located and formatted they can be executed by CrossView Pro.
To start CrossView Pro:
1. Click on the Debug application button. The following button is the Debug application button which is located in the ribbon bar.
CrossView Pro is launched. CrossView Pro will automatically download the compiled file for debugging.
When you use CrossView Pro ROM for the first time, you must setup the communication parameters.
To set the communication parameters:
1. Select the File | Communication Setup... item from the menu. This option opens up the Emulator Communication Setup dialog box.
2. In this dialog you need to identify the COM port (probably COM1: or COM2:) and the baud rate (9600 for RISM).
3. Close the dialog box by clicking on the OK button.
You must tell CrossView Pro which program you want to debug. To do this:
1. Click on File in the menu bar and select the Load Symbolic Debug Info... item. This opens up the Load Symbolic Debug Info dialog box.
2. Click Load.
To view your source while debugging, the Source Window must be open. To open this window,
1. Click on View in the menu bar and select the Source->Source lines item.
Before starting execution you have to reset the target system to its initial state. The program counter, stack pointer and any other registers must be set to their initial value. The easiest way to do this is:
2. Click on Run in the menu bar and select the Program Reset item.
3. Again click on Run in the menu bar and now select the Animate item.
The program dhry.abs is now stepping through the high level language statements. Using the Accelerator bar or the menu bar you can set breakpoints, monitor data, display registers, simulate I/O and much more. See the CrossView Pro Debugger User's Guide for more information.
When you first use EDE you need to setup a project space and add a new project:
1. Access the Project menu and select Project Space | New....
2. Give your project space a name and then click OK.
3. Click on the Add new project to project space button.
4. Give your project a name and then click OK.
The Project Properties dialog box then appears for you to identify the files to be added.
5. Add all the files you want to be part of your project. Then press the OK button. To add files, use one of the 3 methods described below.
The new project is now open.
6. Click Project | Load Files to open files you want on your EDE desktop.
EDE automatically creates a makefile for the project. EDE updates the makefile every time you modify your project.
A detailed description of the process using the sample program dhry is described below. This procedure is outlined as a guide for you to build your own executables for debugging.
1. Make the subdirectory dhry of the examples directory the current working directory.
2. Be sure that the directory of the binaries is present in the PATH environment variable.
3. Compile, assemble, link and locate the modules using one call to the control program ccxa:
ccxa -g -M -Ms -O2 -DCTimer -o dhry.abs dhry_1.c dhry_2.c timers_b.c
The -g option specifies to generate symbolic debugging information. This option must always be specified when debugging with CrossView Pro.
The -O2 option selects an optimization level.
The -M option specifies to generate map files.
The -Ms option specifies to use the small memory model.
The -D option defines the symbol CTimer.
The -o option specifies the name of the output file.
The command in step 3 generates the object files dhry_1.obj, dhry_2.obj and timers_b.obj, the linker map files dhry_1.lnl, dhry_2.lnl and timers_b.lnl, the locator map file dhry_1.map, dhry_2.map and timers_b.map and the absolute output file dhry.abs. The file dhry.abs is in the IEEE Std. 695 format, and can directly be used by XVW. No separate formatter is needed.
Now you have created all the files necessary for debugging with XVW with one call to the control program.
If you want to see how the control program calls the compiler, assembler, linker and locator, you can use the -v0 option or -v option. The -v0 option only displays the invocations without executing them. The -v option also executes them.
ccxa -g -M -Ms -O2 -DCTimer -o dhry.abs dhry_1.c dhry_2.c timers_b.c -v0
The control program shows the following command invocations without executing them (UNIX output):
XA control program va.b rc SN00000000-033 (C) year TASKING, Inc. dhry_1.c: + cxa -e -g -O2 -DCTimer -Ms -o /tmp/cc369b.src dhry_1.c + asxa /tmp/cc369b.src -e -g -o dhry_1.obj dhry_2.c: + cxa -e -g -O2 -DCTimer -Ms -o /tmp/cc369c.src dhry_2.c + asxa /tmp/cc369c.src -e -g -o dhry_2.obj timers_b.c: + cxa -e -g -O2 -DCTimer -Ms -o /tmp/cc369d.src timers_b.c + asxa /tmp/cc369d.src -e -g -o timers_b.obj + lkxa -e -M dhry_1.obj dhry_2.obj timers_b.obj -lcs -lfps -dxa_s.dsc -odhry.out -Odhry + lcxa -e -M -odhry.abs -dxa_s.dsc dhry.out
The -e option removes output files after errors occur. The -O option of the linker specifies the basename of the map file. The -lcs and -lfps options of the linker specify to link the appropriate C library and floating point library. The -d option of the locator specifies the name of the locator description file.
As you can see, the tools use temporary files for intermediate results. If you want to keep the intermediate files you can use the -tmp option. The following command makes this clear.
ccxa -g -M -Ms -O2 -DCTimer -o dhry.abs dhry_1.c dhry_2.c timers_b.c -v0 -tmp
This command produces the following output:
XA control program va.b rc SN00000000-003 (C) year TASKING, Inc. dhry_1.c: + cxa -e -g -O2 -DCTimer -Ms -o dhry_1.src dhry_1.c + asxa dhry_1.src -e -g -o dhry_1.obj dhry_2.c: + cxa -e -g -O2 -DCTimer -Ms -o dhry_2.src dhry_2.c + asxa dhry_2.src -e -g -o dhry_2.obj timers_b.c: + cxa -e -g -O2 -DCTimer -Ms -o timers_b.src timers_b.c + asxa timers_b.src -e -g -o timers_b.obj + lkxa -e -M dhry_1.obj dhry_2.obj timers_b.obj -lcs -lfps -dxa_s.dsc -odhry.out -Odhry + lcxa -e -M -odhry.abs -dxa_s.dsc dhry.out
As you can see, if you use the -tmp option, the assembly source files and linker output file will be created in your current directory also.
Of course, you will get the same result if you invoke the tools separately using the same calling scheme as the control program.
As you can see, the control program automatically calls each tool with the correct options and controls. The control program is described in detail in Chapter Compiler Use.
The subdirectories in the examples directory each contain a makefile which can be processed by mkxa. Also each subdirectory contains a readme.txt file with a description of how to build the example.
To build the dhry demo example follow the steps below. This procedure is outlined as a guide for you to build your own executables for debugging.
1. Make the subdirectory dhry of the examples directory the current working directory.
This directory contains a makefile for building the dhry demo example. It uses the default mkxa rules.
2. Be sure that the directory of the binaries is present in the PATH environment variable.
3. Compile, assemble, link and locate the modules using one call to the program builder mkxa:
mkxa
This command will build the example using the file makefile.
To see which commands are invoked by mkxa without actually executing them, type:
mkxa -n
This command produces the following output:
XA program builder vx.y rz SN00000000-003 (C) year TASKING, Inc. ccxa -c -o dhry_1.obj -Ms -O2 -g -DCTimer -Wc-w91 -Wc-w66 -Wc-w303 dhry_1.c ccxa -c -o dhry_2.obj -Ms -O2 -g -DCTimer -Wc-w91 -Wc-w66 -Wc-w303 dhry_2.c ccxa -c -o timers_b.obj -Ms -O2 -g -DCTimer -Wc-w91 -Wc-w66 -Wc-w303 timers_b.c ccxa -o dhry.abs dhry_1.obj dhry_2.obj timers_b.obj -Ms -Wlk-w1
The -g option in the makefile is used to instruct the C compiler to generate symbolic debug information. This information makes debugging an application written in C much easier to debug.
The -M option in the makefile is used to create the linker list file (.lnl) and the locator map file (.map).
The -Ms option specifies to use the small memory model.
The -o option specifies the name of the output file.
To remove all generated files type:
mkxa clean