Compiler. Analog Devices SHARC
إعلان
إعلان
2 Compiler
The C/C++ compiler ( cc21k
) is part of Analog Devices development software for SHARC (ADSP-21xxx and
ADSP-SCxxx) processors.
This chapter contains:
•
provides an overview of C/C++ compiler for SHARC processors.
•
and output files, and command-line switches.
•
how to use existing word-addressed code in a new byte-addressed application.
•
Using Native Fixed-Point Types describes the compiler support for the native fixed-point type
fract
. The fract
data type is defined in Chapter 4 of the Extensions to support embedded processors ISO/IEC draft technical report TR 18037.
•
describes how to enable the compliance to the ISO/IEC 9899:1990 C standard, ISO/IEC 9899:1999 C standard, ISO/IEC 14882:2003 C++ standard, or ISO/IEC 14882:2011 C++ standard.
•
describes how the cc21k
compiler enables checking for MISRA-C Guidelines.
•
Run-Time Checking describes the additional run-time checks supported by the compiler.
•
C/C++ Compiler Language Extensions describes the
cc21k
compiler extensions to the ISO/ANSI standard for the C and C++ languages.
•
•
C/C++ Run-Time Model and Environment
contains reference information about implementation of C/C++ programs, data, and function calls in the ADSP-21xxx and ADSP-SCxxx processors.
•
Compiler C++ Template Support describes how templates are instantiated at compilation time.
•
File Attributes describes how file attributes help with the placement of run-time library functions.
•
Implementation Defined Behavior
describes how the compiler implements various language features where the standard allows flexibility.
CrossCore Embedded Studio 2.2.0
2–1
C/C++ Compiler Overview
C/C++ Compiler Overview
The C/C++ compiler ( cc21k
) is designed to aid your project development efforts by:
• Processing C and C++ source files, producing machine-level versions of the source code and object files.
• Providing relocatable code and debugging information within the object files.
• Providing relocatable data and program memory segments for placement by the linker in the processor memory.
Using C/C++, developers can significantly decrease time-to-market since it gives them the ability to work efficiently with complex signal processing data types. It also allows them to take advantage of specialized processor operations without having to understand the underlying processor architecture.
The C/C++ compiler compiles ISO/ANSI standard C and C++ code for SHARC processors. Additionally, Analog
Devices includes, within the compiler, a number of C language extensions designed to aid processor development.
The compiler runs from the CCES environment or from an operating system command line.
The C/C++ compiler processes your C and C++ language source files and produces SHARC assembler source files.
The SHARC assembler ( easm21k
) processes the assembler source files into Executable and Linkable Format (ELF) object files. The object can be linked (by linker
) to create an ADSP-21xxx or ADSP-SCxxx executable file, or included in an archive library (by elfar
). The compiler controls the assemble, link, and archive phases of the process depending on the input source files and used compiler options.
The cc21k
compiler processes your source files containing the C/C++ program. The compiler supports the following standards, each with Analog Devices extensions enabled:
• A freestanding implementation of the ISO/IEC 9899:1990 C standard (C89).
• A freestanding implementation of the ISO/IEC 9899:1999 C standard (C99).
• A freestanding implementation of the ISO/IEC 14882:2003 C++ standard (C++ 2003). The compiler supports the language features supported by a standard subset of the C++ Library. You can obtain C++ library (Dinkum
EC++ Library) reference text in the CCES online help.
• A freestanding implementation of the ISO/IEC 14882:2011 C++ standard (C++ 2011).
RTTI and exceptions for C++ are supported, but disabled by default. See the
information.
For information on the C or C++ language standards, see any of the many reference texts.
The cc21k
compiler supports a set of C/C++ language extensions. These extensions support hardware features of
You can set the compiler options for the project in the
Preferences dialog box of the CCES Integrated Development
Environment (IDE). These options control how the compiler processes the source files, letting you select features that include the language dialect, error reporting, and debugger output.
2–2
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Overview
Access the
Preferences pages from the Project > Properties menu. Within the Preferences pages, navigate to C/C++
Build > Settings. Alternatively, click the Settings icon in the Project Explorer view. For both routines, access the compiler options via
Settings > Tool Settings > CrossCore SHARC C/C++ Compiler.
For more information on the CCES environment, see the CCES online help.
Compiler Components
The compiler is not a single program, but a collection of programs, each with a different task:
Compiler Driver
The compiler driver, cc21k
, is the user interface to other programs, and is the program to invoke when you run the compiler on the command line. Its responsibility is to marshall and interpret the command-line arguments to determine what other components and code-generation tools to invoke, and in what order. The compiler driver hides the complexity and presents a consistent interface. For this reason, throughout the documentation, "the compiler", "compiler driver", and " cc21k
" are used interchangeably.
Compiler Proper
The compiler proper, found in
..\SHARC\etc\compiler
and/or
..\SHARC\etc\compiler_ba
, is the actual compiler. It compiles a single C/C++ source file into a single assembly output file. The compiler driver invokes the compiler proper for each C/C++ source file specified.
Assembler
The assembler, easm21k
, assembles a single assembly source file into a single object file. The compiler driver invokes the assembler to translate both user-supplied assembly files and compiler-generated assembly files.
Linker
The linker, linker
, combines object files into executable files, and searches library files to resolve references to undefined symbols. The linker relies on an
.ldf
file to specify how the resulting collection of symbols are mapped into memory. The compiler driver invokes the linker when the specified output file is an executable file.
Prelinker
The prelinker is found in
..\SHARC\etc\prelinker
. Its purpose is to examine the set of objects and libraries before linking. The prelinker also instructs the compiler driver to recompile files or add other libraries or switches, as needed. The compiler driver invokes the prelinker just before invoking the linker. Language features supported by the prelinker include:
• C++ template instantiation
• Interprocedural analysis
CrossCore Embedded Studio 2.2.0
2–3
Compiler Command-Line Interface
• Instrumented profiling
IPA Solver
The IPA Solver, found in
..\SHARC\etc\ipa
, propagates information between compiled modules, as part of Interprocedural Analysis. If propagated information can improve optimization, the IPA Solver directs the compiler driver to recompile a source file. The prelinker invokes the IPA Solver when any of the input files are compiled with IPA optimization enabled.
PGO Merger
The PGO merger, found in
..\SHARC\etc\pgo
, combines multiple profiles gathered through profiled executions of an application. The PGO merger also produces a single profile for the compiler to use. The compiler driver invokes the PGO merger whenever more than one PGO profile is specified.
Librarian
The librarian, elfar
, provides facilities for creating, modifying, and inspecting library files. The compiler driver invokes the librarian when the output file is a library file.
Memory Initializer
The memory initializer, meminit
, creates an initialization stream within the executable file. The compiler
driver directs the linker to invoke the memory initializer after linking, when the
The assembler, linker, and librarian are documented in the Assembler and Preprocessor Manual and Linker and Utilit-
ies Manual. Invoke the other components only through the compiler driver, never directly.
Compiler Command-Line Interface
This section describes how the cc21k
compiler is invoked from the command line and the various types of files used by and generated from the compiler. It also describes the switches used to tailor the compiler operation.
This section contains:
•
•
Compiler Command-Line Switches
•
Environment Variables Used by the Compiler
•
•
•
Controlling Silicon Revision and Anomaly Workarounds Within the Compiler
2–4
CrossCore Embedded Studio 2.2.0
Compiler Command-Line Interface
By default, the compiler runs with Analog Devices extension keywords for C code enabled. It means that the compiler processes source files written in ISO/IEC 9899:1999 standard C language, supplemented with Analog Devices extensions. The
source files the compiler operates upon. By default, the compiler processes the input file through the listed stages to produce a
.dxe
file. See file names in the
Input and Output File Extensions table (in
The
C/C++ Mode Selection Switches table (in
Compiler Command-Line Switches ) lists the switches that select the
language dialect. Although many switches are generic between C and C++, some of them are valid in C++ mode only. A summary of the generic C/C++ compiler switches appears in the C/C++ Compiler Common Switches table
(in Compiler Command-Line Switches
). A summary of the C++-specific compiler switches appears in the
C++
Mode Compiler Switches table (in Compiler Command-Line Switches
). Each switch description follows the summaries.
NOTE: When developing a project, sometimes it is useful to modify the default option settings of the compiler.
The way the compiler options are set depends on the environment used to run the processor development software. For more information, see
Environment Variables Used by the Compiler
.
Running the Compiler
Use the following syntax for the cc21k
command line: cc21k [-switch [-switch …] sourcefile [sourcefile]]
The
cc21k Command-Line Syntax table describes the syntax elements.
Table 2-1: cc21k Command-Line Syntax
Command Element Description
cc21k
-switch sourcefile
Name of the compiler program for SHARC processors.
Switch (or switches) to process. The compiler has many switches. These switches select the operations and modes for the compiler and other tools. Command-line switches are case-sensitive.
For example,
-O
is not the same as
-o
.
Name of the file to be preprocessed, compiled, assembled, and/or linked. The
sourcefile
element can include the drive, directory, file name, and file extension. The compiler supports both Win32 and POSIX-style paths by using forward or back slashes as the directory delimiter.
It also supports UNC path names, starting with two slashes and a network name.
NOTE: When file names or switches for the compiler include spaces or special characters, ensure that the names are properly quoted, usually using double-quote characters. The properly quoted file names ensure that the operating system does not interpret the names before passing them to the compiler.
The cc21k
compiler uses the file extension to determine what the file contains and what operations to perform upon it. The Input and Output File Extensions table lists the allowed extensions.
CrossCore Embedded Studio 2.2.0
2–5
Compiler Command-Line Interface
Table 2-2: Input and Output File Extensions
File Extension File Extension Description
.c .C
C source file
.cpp .cxx .cc .c++
.h
.hpp .hh .hxx .h++
.hpl
.ii .ti
.et
.ipa
.pgo .pgi .pgt
C++ source file
Header file (referenced by an
#include
statement)
C++ header file (referenced by a
#include
statement)
Heap debugging output file-used by the Reporter Tool to produce a report on heap usage and related errors
Template instantiation files-used internally by the compiler when instantiating templates
Exported template files-used internally by the compiler when instantiating exported templates
Interprocedural analysis files-used internally by the compiler when performing interprocedural analysis.
Execution profile generated by a simulation run or instrumented executable
.i
.s .asm
.is
.sbn
.ldf
.misra
.doj .o
.dlb .a
.dxe
.xml
.sym
Preprocessed source file-created when preprocess only is specified
Assembly language source files
Preprocessed assembly language source-retained when
Binary data included by an assembly language source file
Linker description file
Text file used by prelinker for MISRA-C Guidelines checking
Object file to be linked
Library of object files to be linked as needed
Executable file produced by compiler
Processor memory map file output
Processor symbol map file output
The normal function of cc21k
is to invoke the compiler, assembler, and linker as required to produce an executable object file. The precise operation is determined by the extensions of the input file names and by various switches.
In normal operation, the compiler uses the files listed in the File Extensions Specifying Compiler Action table to perform a specified action.
Table 2-3: File Extensions Specifying Compiler Action
Extension Action
.c .C .cpp .cxx .cc .c++
.asm .dsp .s
.doj
Source file is compiled, assembled, and linked.
Assembly language source file is assembled and linked.
Object file (from previous assembly) is linked.
2–6
CrossCore Embedded Studio 2.2.0
Compiler Command-Line Interface
Table 2-3: File Extensions Specifying Compiler Action (Continued)
Extension Action
.pgo .pgi
Profile-guided optimization information file is used during compilation.
If multiple files are specified, each is processed to produce an object file and then all the object files are presented to the linker.
You can stop this sequence at various points using appropriate compiler switches (
-E,-P,-M,-H,-S,
and
-c
), or by selecting options within the IDE.
Many of the compiler's switches take a file name as an optional parameter. If you do not use the optional output name switch, cc21k
names the output for you. The Input and Output File Extensions table lists the type of files, names, and extensions cc21k
appends to output files.
File extensions vary by command-line switch and file type. These extensions are influenced by the program that is processing the file. The programs search directories that you specify and path information that you include in the file name. The Input and Output File Extensions table indicates the extensions that the preprocessor, compiler, assembler, and linker support. The compiler supports relative and absolute directory names to define file extension paths. For information on additional search directories, see the command-line switch that controls the specific type of extensions.
When providing an input or output file name as an optional parameter, follow these guidelines.
• Use a file name (include the file extension) with an unambiguous relative path or an absolute path. A file name with an absolute path includes the directory, file name, and file extension. The compiler uses the file extension convention listed in the Input and Output File Extensions table to determine the input file type.
• Verify that the compiler is using the correct file. If you do not provide the complete file path as part of the parameter or add additional search directories, cc21k
looks for input in the current directory.
NOTE: Use the verbose output switches for the preprocessor, compiler, assembler, and linker to cause each of these tools to display command-line information as they process each file.
The compiler refers to a number of environment variables during its operation, and these environment variables can
Processor Features
Some processor features are only supported by a subset of the ADSP-21xxx and ADSP-SCxxx processors. These features are VISA (Variable Instruction Set Architecture) execution, byte-addressing, and native double-precision floating-point arithmetic.
The Processors Supporting VISA Execution table shows the processors that support VISA execution. The compiler targets the VISA instruction set when using the
-short-word-code
CrossCore Embedded Studio 2.2.0
2–7
Compiler Command-Line Interface
Table 2-4: Processors Supporting VISA Execution
All ADSP-214xx Processors All ADSP-215xx Processors
ADSP-21467 ADSP-21584
ADSP-21587 ADSP-21469
ADSP-21477
ADSP-21478
ADSP-21479
ADSP-21483
ADSP-21486
ADSP-21487
ADSP-21488
ADSP-21489
All ADSP-SC5xx Processors
ADSP-SC582
ADSP-SC584
ADSP-SC587
ADSP-SC589
The processors that support byte-addressing are given in the Processors Supporting Byte-Addressing table. For more
information on byte-addressing, see Using Byte-Addressing
.
Table 2-5: Processors Supporting Byte-Addressing
All ADSP-215xx Processors
ADSP-21584
ADSP-21587
All ADSP-SC5xx Processors
ADSP-SC582
ADSP-SC584
ADSP-SC587
ADSP-SC589
The processors that support native double-precision floating-point arithmetic are given in the
Processors Supporting
Native Double-Precision Floating-Point Arithmetic table. For more information on the performance implications of
native vs. emulated arithmetic, see Avoiding Emulated Arithmetic in the
Achieving Optimal Performance From C/C
chapter.
Table 2-6: Processors Supporting Native Double-Precision Floating-Point Arithmetic
All ADSP-215xx Processors All ADSP-SC5xx Processors
ADSP-21584
ADSP-21587
ADSP-SC582
ADSP-SC584
ADSP-SC587
ADSP-SC589
2–8
CrossCore Embedded Studio 2.2.0
Compiler Command-Line Interface
Compiler Command-Line Switches
This section describes the command-line switches (options) used when compiling. It contains a set of tables providing a brief description of each switch. The tables are organized by type of switch. Following the tables are sections providing fuller descriptions of each switch.
These tables summarize generic and specific switches.
• C/C++ Mode Selection Switches
• C/C++ Compiler Common Switches
• C Mode (MISRA) Compiler Switches
• C++ Mode Compiler Switches
A brief description of each switch follows the tables.
Table 2-7: C/C++ Mode Selection Switches
Switch Name
Description
Supports programs that conform to the ISO/IEC 9899:1990 standard.
Supports programs that conform to the ISO/IEC 9899:1999 standard. It is the default mode.
Supports ISO/IEC 14882:2003 C++ standard with Analog Devices extensions.
Supports ISO/IEC 14882:2011 C++ standard with Analog Devices extensions.
Enables GNU C++ features and extensions.
Table 2-8: C/C++ Compiler Common Switches
Switch Name
Description
Specifies file to be compiled.
Reads command-line input from the file.
Asserts the specified name as a predicate.
Uses absolute paths for the make dependencies emitted when using the
-M
,
-MM
, or
-MD
switches.
Links against debug-specific variants of system libraries, where available.
Allows alternative keywords and sequences in sources.
Treats inline
keyword as a requirement rather than a suggestion.
Annotates compiler-produced assembly files.
Provides more annotation information for the prolog, kernel, and epilog of a loop.
Informs the compiler that asm
statements are not a barrier to SIMD inside loops with the
SIMD_for
pragma.
CrossCore Embedded Studio 2.2.0
2–9
Compiler Command-Line Interface
Table 2-8: C/C++ Compiler Common Switches (Continued)
Switch Name Description
-extra-keywords
Based on the files the compiler processes, directs the compiler to emit automatic attributes. Enabled by default.
Directs the librarian to build a library file.
Retains preprocessor comments in the output file; must run with the
-E
or
-P switch.
Compiles and/or assembles only, but does not link.
Selects 8-bit or 32-bit format for char
, and 16-bit or 32-bit format for short respectively. The
-char-size-8
switch is only supported when compiling for processors that support byte-addressing, shown in the
Processors Supporting Byte-
Addressing table in the Processor Features
.
The
-char-size-32
switch is the default mode for processors that do not support byte-addressing.The
-char-size-8
switch is the default for processors that do support byte-addressing.
Directs the compiler to treat dm
and pm
qualified pointers as assignment-compatible.
Reads more options from the specified XML file.
Specifies that data accessed via a pointer to const
data can be modified elsewhere.
Directs the compiler to mark string literals as const
qualified.
Defines a macro.
Adds target to any emitted dependency information.
Selects 32-bit or 64-bit IEEE format for double
. The
-double-size-32 switch is the default mode.
Indicates that the resulting object can be linked with objects built with any double
size.
Displays, but does not perform, main driver actions (verbose dry run).
Displays, but does not perform, top-level driver actions (terse dry run).
Preprocesses, but does not compile, the source file.
Preprocesses and sends all output to a file.
Preprocesses and compiles the source file.
Enables exception handling.
Ensures that the enum
type is int
. By default, enum
can have a type larger than int
.
Recognizes Analog Devices extensions to ANSI/ISO standards for C and C++ (default mode).
Directs the compiler not to:
2–10
CrossCore Embedded Studio 2.2.0
Compiler Command-Line Interface
Table 2-8: C/C++ Compiler Common Switches (Continued)
Switch Name
-flags-{asm|compiler|ipa|lib|link|mem| prelink} switch[, switch2[, ...]]
Description
• generate double-word memory accesses
• substitute an instruction for another that could result in the truncation of a
40-bit floating point value.
Adds the specified attribute name/value pair to the file or files being compiled.
Passes command-line switches through the compiler to other build tools.
-I directory[{,|;} directory...
-L directory[{;|,} directory …
Uses a support library function to convert a float
to an integer type.
Treats array references of the form array[i%n]
as circular buffer operations.
Treats floating-point multiply and addition as an associative.
Displays the version number of the driver and any processes invoked by the driver.
Sets the default mode of
FX_CONTRACT
to
ON
.
Sets the default mode of
FX_ROUNDING_MODE
to
BIASED
.
Sets the default mode of
FX_ROUNDING_MODE
to
TRUNCATION
.
Sets the default mode of
FX_ROUNDING_MODE
to
UNBIASED
.
Generates DWARF-2 debug information.
Generates lightweight DWARF-2 debug information.
Produces dependency information in the style expected by the GNU make
program.
Outputs a list of included header files, but does not compile.
Outputs a list of included header files and compiles.
Outputs a list of command-line switches.
Appends directory to the standard search path.
Establishes the point in the include
directory list at which the search for header files enclosed in angle brackets begins.
Outputs only header details or makefile dependencies for include
files specified in double quotes.
Includes named file before preprocessing each source file.
Enables interprocedural analysis.
Appends directory to the standard library search path.
Searches library for functions when linking.
Directs the compiler to attempt to generate SIMD code in linear code.
Lists all compiler-supported errata workarounds.
Directs the compiler to attempt to generate SIMD code in loops.
CrossCore Embedded Studio 2.2.0
2–11
Compiler Command-Line Interface
Table 2-8: C/C++ Compiler Common Switches (Continued)
Switch Name Description
Generates make rules only, but does not compile.
Generates make
rules, compiles, and prints to a file.
Generates make rules and compiles.
Writes dependency information to
filename
. This switch is used with the
-ED or
-MD
switch.
Makes dependencies, where the target is renamed as
filename
.
Directs the linker to generate a memory map of all symbols.
Enables memory initialization.
Enables string literals over multiple lines (default).
Ignores inline
keyword on function definitions.
Does not attempt to maintain alignment of the program stack.
Does not allow alternative keywords and sequences in sources.
Disables the annotation of assembly files.
Disables the production of extra loop annotation information by the compiler
(default mode).
Instructs the compiler not to assume that volatile loads and stores are to IOP addresses.
Directs the compiler not to emit automatic attributes based on the files it compiles.
Disables the automatic generation of circular buffer code by the compiler.
Directs the compiler not to make string literals const
qualified.
Directs the compiler not to generate code containing delayed branches jumps.
Disables preprocessor definitions: macros, include directories, library directories, or keyword extensions.
Disables exception handling.
Disables Analog Devices keyword extensions that could be valid C/C++ identifiers.
Does not treat floating-point multiply and addition as associative.
Sets the default mode of
FX_CONTRACT
to
OFF
.
Directs the compiler not to attempt to generate SIMD instructions in linear code.
Directs the compiler not to attempt to generate SIMD instructions in loop code without the
SIMD_for
or vector_for
pragma.
Prevents the compiler from inserting a call to exit()
at the end of main()
.
Disables memory initialization.
2–12
CrossCore Embedded Studio 2.2.0
Compiler Command-Line Interface
Table 2-8: C/C++ Compiler Common Switches (Continued)
Switch Name Description
-no-workaround workaround_id[, work-
Disables multiple line string literal support.
Prevents the compiler from issuing a diagnostic during excessively long compilations.
Disables run-time checking.
Disables checking array boundaries at run time.
Disables checking for division by zero at run time.
Disables checking of heap operations zero at run time.
Disables checking for NULL pointer dereferences at run time.
Disables checking for negative/too-large shifts at run-time.
Disables checking for stack overflow at run time.
Disables checking for unassigned variables at run time.
Saturating addition is not associative.
Causes the compiler not to introduce saturation semantics when optimizing expressions.
Directs the compiler not to replace an arithmetic shift by one with an add instruction.
Disables automatic SIMD mode.
Disables any predefined assertions and system-specific macro definitions.
Disables preprocessor definitions and Analog Devices keyword extensions that do not have leading underscores (__).
Searches for preprocessor include header files only in the current directory and in directories specified with the
-I
switch.
Searches for only those library files specified with the
-l
switch.
Specifies that all compiled code can be non-thread-safe.
Disables specific hardware anomaly workarounds within the compiler.
Directs the compiler to generate instructions of normal-word size (48-bits).
Has the same effect as compiling with the
-normal-word-code
switch.
Enables code optimizations.
Enables automatic function inlining.
Optimizes for code size.
Controls speed versus size optimizations.
Specifies the output file name.
CrossCore Embedded Studio 2.2.0
2–13
Compiler Command-Line Interface
Table 2-8: C/C++ Compiler Common Switches (Continued)
Switch Name Description
-overlay-clobbers clobbered-regs
-path-{ asm | compiler | ipa | lib | link | prelink } pathname
-progress-rep-timeout-secs secs
-R directory[{:|,} directory …]
-reserve register[, register …]
-restrict-hardware-loops maximum
Disables the propagation of register information between functions and forces the compiler to assume that all functions clobber all scratch registers.
Specifies the registers for an overlay manager to clobber.
Preprocesses, but does not compile, the source file. Omits line numbers in the preprocessor output.
Similar to
-P
, but does not halt compilation after preprocessing.
Generates profiling instrumentation.
Uses the specified pathname as the location of the specified compilation tool (assembler, compiler, IPA solver, library builder, linker, or prelinker, respectively).
Uses the specified directory as the location of all compilation tools.
Specifies the location of non-temporary files.
Specifies the location of temporary files.
Used with profile-guided optimization.
Adds instrumentation for the gathering of a profile as the first stage of performing profile-guided optimization.
Outputs a raw preprocessed listing to the specified file.
Directs the compiler to produce code suitable for the specified processor.
Directs the compiler to generate profiling code targeted for execution on hardware. Requires the use of a supported profiling switch.
Issues a diagnostic message each time the compiler starts compiling a new function. Equivalent to
-Wwarn=cc1472
.
Issues a diagnostic message each time the compiler starts a new generic optimization pass on the current function. Equivalent to
-Wwarn=cc1473
.
Issues a diagnostic message if the compiler exceeds a time limit during compilation.
Specifies how many seconds must elapse during a compilation before the compiler issues a diagnostic on the length of compilation.
Appends directory to the standard search path for source files.
Removes all directories from the standard search path for source files.
Reserves certain registers from compiler use.
Note: Reserving registers can have a detrimental effect on optimization capabilities of the compiler.
Restricts the number of levels of loop nesting used by the compiler.
Enables run-time checking.
Enables run-time checking of array boundaries.
2–14
CrossCore Embedded Studio 2.2.0
Compiler Command-Line Interface
Table 2-8: C/C++ Compiler Common Switches (Continued)
Switch Name Description
-sectionid=section_name[, id=sec-
-W{annotation|error|remark|suppress| warn} number[, number ...]
Enables run-time checking for division by zero.
Enables run-time checking of heap operations.
Enables run-time checking for NULL pointer dereferences.
Enables run-time checking for negative/too-large shifts.
Enables run-time checking for stack overflow.
Enables run-time checking for unassigned variables.
Stops compilation before running the assembler.
Removes debug info from the output executable file.
Saturating addition is associative.
Saves intermediate files.
Orders the compiler to place data/program of type
id
into the
section_name
section.
Directs the compiler to generate instructions of short word size (16/32/48-bits).
Displays the driver command-line information.
Specifies a silicon revision of the specified processor. The default setting is the latest silicon revision at the time of release.
Makes the default type for int
bit-fields signed.
Specifies that struct
copies can use memcpy
semantics, rather than the usual memmove
behavior.
Directs the compiler to generate instructions of short word size (16/32/48-bits).
Checks the source code for compiler syntax errors, but does not write any output.
Defines the system definition macros.
Specifies the Linker Description File.
Specifies that support for multithreaded applications is enabled.
Displays the elapsed time as part of the output information on each part of the compilation process.
Undefines macro.
Makes the default type for plain int
bit-fields unsigned.
Displays both the version and command-line information.
Displays command-line information.
Displays version information.
Overrides the default severity of the specified error message.
CrossCore Embedded Studio 2.2.0
2–15
Compiler Command-Line Interface
Table 2-8: C/C++ Compiler Common Switches (Continued)
Switch Name Description
-workaround workaround_id[, work-
Indicates that the compiler can issue code-generation annotations. Annotations are messages milder than warnings and can help to optimize your code.
Stops compiling after reaching the specified number of errors.
Directs the compiler to treat all warnings as errors.
Indicates that the compiler can issue remarks. Remarks are diagnostic messages milder than warnings.
Issues only the briefest form of compiler warning, errors, and remarks.
Does not display compiler warning messages.
Issues warnings if any libraries specified by component XML files could not be located.
Produces a warning when a function is called without a prototype.
Enables code-generator workaround for specific hardware errata.
Outputs cross-reference information to the specified file.
Table 2-9: C Mode (MISRA) Compiler Switches
Switch Name Description
Enables checking for MISRA-C: 2004 guidelines, allows some relaxation of interpretation.
Specifies directory for generation of
.misra
files.
If this option is not specified, a local directory called
MISRARepository
is created.
Enables checking for MISRA-C: 2004 guidelines, allows some relaxation of interpretation. Does not generate
.misra
files to check for link-time rule violations.
Enables checking for MISRA-C: 2004 guidelines, allows some relaxation of interpretation. Does not generate extra code to perform run-time checking in support of a number of Rules.
Enables checking for MISRA-C: 2004 guidelines
Enables checking for MISRA-C: 2004 guidelines. Advisory rules are not reported.
Enables checking for MISRA-C: 2004 guidelines. Suppresses reporting of MISRA-C rule 20.4,
20.7, 20.8, 20.9, 20.10, 20.11, and 20.12.
Overrides the default severity of the specified messages relating to the specified MISRA-C rules.
Overrides the default severity of the specified messages relating to the specified MISRA-C rules.
2–16
CrossCore Embedded Studio 2.2.0
Compiler Command-Line Switches
Table 2-10: C++ Mode Compiler Switches
Switch Name Description
Supports some language features (anachronisms) prohibited by the C++ standard but still in common use.
Adds run-time checking to the generated code, highlighting potential uninitialized external objects.
Allows non-standard behavior concerning friend declarations. When friend names are not injected, function names are visible only when using argument-dependent lookup.
Ensures reinclusion of implicitly included files when generating dependency information.
Allows implicit inclusion of source files as a method of finding definitions of template entities to be instantiated. It is not compatible with exported templates.
Disallows the use of anachronisms prohibited by the C++ standard.
Allows standard behavior. Friend function names are visible only when using argument-dependent lookup. Friend class names are never visible.
Prevents implicit inclusion of source files as a method of finding definitions of template entities to be instantiated.
Disables run-time type information.
Disables the lookup of names used in templates.
Enables run-time type information.
Enables the lookup of names used in templates.
C/C++ Mode Selection Switch Descriptions
The following command-line switches provide C/C++ mode selection.
-c89
The
-c89
switch directs the compiler to support programs that conform to the ISO/IEC 9899:1990 standard. For greater conformance to the standard, use the following switches:
-alttok
,
-const-read-write
, and
-noextra-keywords
. See the
C/C++ Compiler Common Switches table in Compiler Command-Line Switches .
-c99
The
-c99
switch directs the compiler to support programs that conform to a freestanding implementation of the
ISO/IEC 9899:1999 standard. For greater conformance to the standard, use the following switches:
-alttok
,
const-read-write
, and
-no-extra-keywords
. See the
C/C++ Compiler Common Switches table in
NOTE: The compiler does not support the
_Complex
and
_Imaginary
keywords. Complex arithmetic in C mode is enabled by including the Analog Devices-specific header file
<complex.h>
.
CrossCore Embedded Studio 2.2.0
2–17
C/C++ Mode Selection Switch Descriptions
-c++
The
-c++
(C++ mode) switch directs the compiler to compile the source file(s) written in ISO/IEC 14882:2003 standard C++ with Analog Devices language extensions. When using this switch, source files with an extension of
.c
are compiled and linked in C++ mode. The compiler implicitly adds this switch when compiling files with a
.cpp
extension.
All C++ standard features are accepted in the default mode, except exception handling and run-time type identification. The exception handling and run-time type identification impose a run-time overhead that is not desirable for all embedded programs. Support for these features can be enabled with the
-eh
and
-rtti
switches. See the
C++
Mode Compiler Switches table in
Compiler Command-Line Switches
.
-c++11
The
-c++11
(C++11 mode) switch directs the compiler to compile the source files written in ISO/IEC 14882:2011 standard C++ with Analog Devices language extensions. When using this switch, source files with an extension of
.c
are compiled and linked in C++ mode.
This version of the compiler accepts many of the ISO/IEC 14882:2011 standard features, but the underlying library support conforms to the ISO/IEC 14882:2003 standard. Enable exception handling and run-time type identification explicitly, because these features impose a run-time overhead that is not desirable for all embedded programs.
Support for these features can be enabled with the
-eh
and
-rtti
switches. See the
C++ Mode Compiler Switches
table in Compiler Command-Line Switches
.
-g++
The
-g++
(G++ mode) switch directs the compiler to emulate many extensions available in GNU C++. By default, the C++ dialect is based on the ISO/IEC 14882:2003 standard. To base the dialect on the ISOIEC 1482:2011 standard instead, enable the additional
-c++11
switch.
You must enable exception handling and run-time type identification explicitly if you require these features, because they impose a run-time overhead that is not desirable for all embedded programs. Use the
-eh
and
-rtti
switches to do so. See the C++ Mode Compiler Switches table in
Compiler Command-Line Switches
.
C/C++ Compiler Common Switch Descriptions
The following command-line switches apply in both C and C++ modes.
sourcefile
The
sourcefile
parameter (or parameters) specifies the name of the file (or files) to be preprocessed, compiled, assembled, and/or linked. A file name can include the drive, directory, file name, and file extension. The cc21k compiler uses the file extension to determine the operations to perform. The Input and Output File Extensions and
File Extensions Specifying Compiler Action tables (in
) list the permitted extensions and matching compiler operations.
2–18
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Common Switch Descriptions
-@ filename
The
-@ filename
(command file) switch directs the compiler to read command-line input from
filename
. The specified file must contain driver options but can also contain source file names and environment variables. It can be used to store frequently used options and read from a file list.
-A name[tokens]
The
-A
(assert) switch directs the compiler to assert
name
as a predicate with the specified
tokens
. The
-A
switch has the same effect as the
#assert
preprocessor directive. The following assertions are predefined:
Table 2-11: Predefined Assertions
Assertion system machine cpu compiler
Value
embedded adsp21xxx adsp21xxx cc21k
The
-A name(value)
switch is equivalent to including
#assert name(value) in your source file, and both can be tested in a preprocessor condition in the following manner:
#if #name(value)
// do something
#else
// do something else
#endif
For example, the default assertions can be tested as:
#if #machine(adsp21xxx)
// do something
#endif
NOTE: The parentheses in the assertion need quotes when using the
-A
switch, to prevent misinterpretation. No quotes are needed for a
#assert
directive in a source file.
-absolute-path-dependencies
The
-absolute-path-dependencies
switch can be used with one of the
-M
,
-MM
, or
-MD
switches. By default, these switches emit make dependencies using relative paths. The
-absolute-path-dependencies
switch can be used to emit the dependencies using absolute, rather than relative paths. The compiler driver invokes all underlying tools with the switch as required, so that all tools emit dependencies using absolute paths. The underlying tools are the compiler, assembler, preprocessor, linker, and archiver.
CrossCore Embedded Studio 2.2.0
2–19
C/C++ Compiler Common Switch Descriptions
-add-debug-libpaths
The
-add-debug-libpaths
switch prepends the
Debug
subdirectory to the search paths passed to the linker.
The
Debug
subdirectory, found in each of the silicon-revision-specific library directories, contains variants of certain libraries. For example, system services, which provide extra diagnostic output to help with debugging problems arising from their use.
NOTE: Invoke this switch from the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Link-
er > Processor > Use Debug System libraries.
-alttok
In C89 and C99 modes, the
-alttok
(alternative tokens) switch directs the compiler to allow digraph sequences in source files. This switch is enabled by default in C89 and C99 modes.
In C++ mode, this switch is disabled by default. When enabled in C++ mode, the switch also enables the recognition of alternative operator keywords in C++ source files. The Alternative Operator Keywords table lists the keywords.
Table 2-12: Alternative Operator Keywords
Keyword
and and_eq bitand bitor compl or or_eq not not_eq xor xor_eq
Equivalent
||
|=
!
!=
^
^=
&
|
&&
&=
~
.
NOTE: The
-alttok
switch has no effect on the use of the alternative tokens listed in the
Alternative Operator
Keywords table in C89 or C99 mode. Instead, in C89 or C99 mode, include header file
<iso646.h>
to use alternative tokens.
2–20
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Common Switch Descriptions
-always-inline
The
-always-inline
switch instructs the compiler always to attempt inlining any call to a function that is defined with the inline
qualifier. It is equivalent to applying #pragma always_inline to all functions in the module
that have the inline
qualifier.
-annotate
The
-annotate
(enable assembly annotations) switch directs the compiler to annotate assembly files generated by the compiler. The default behavior is that whenever optimizations are enabled, all assembly files generated by the compiler are annotated. The files are annotated with information on the performance of the generated assembly.
For more information, see
Assembly Optimizer Annotations
in the
Achieving Optimal Performance From C/C++
NOTE: Invoke this switch from the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Com-
piler > General > Generate annotations.
-annotate-loop-instr
The
-annotate-loop-instr
switch directs the compiler to provide more annotation information for the prolog,
kernel, and epilog of a loop. See Assembly Optimizer Annotations
in the Achieving Optimal Performance From
-asms-safe-in-simd-for-loops
The
-asms-safe-in-simd-for-loops
switch directs the compiler not to consider asm
statements within loops a barrier to SIMD code generation if the loop is decorated with a
SIMD_for
pragma.
.
-auto-attrs
The
-auto-attrs
(automatic attributes) switch directs the compiler to emit automatic attributes based on the files
attributes, including automatic attributes that the compiler emits. See also the
switches.
NOTE: Invoke this switch from the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Com-
piler > General > Auto-generated attributes.
CrossCore Embedded Studio 2.2.0
2–21
C/C++ Compiler Common Switch Descriptions
-build-lib
The
-build-lib
(build library) switch directs the compiler to use elfar
(the librarian) to produce a library
(
.dlb
) file as the output instead of using the linker to produce an executable (
.dxe
) file. The
-o
option must be used to specify the name of the resulting library.
-C
The
-C
(comments) switch directs the C/C++ preprocessor to retain comments in its output file. The
-C
switch must be used with the
-E
or
-P
switch.
-c
The
-c
(compile only) switch directs the compiler to compile and/or assemble the source files, but stop before linking. The output is an object (
.doj
) file for each source file.
-char-size[-8|-32]
The
-char-size-8
( char
is 8 bits, short
is 16 bits) and
-char-size-32
(both char
and short
are 32 bits) switches select the storage format that the compiler uses for types char
and short
. The switch also affects the endianness of 64-bit integer and floating-point types, as well as a number of other changes to the run-time model. The
-char-size-8
switch only is supported for processors that support byte-addressing, and for those processors, it is the default setting. The
Processors Supporting Byte-Addressing table in Processor Features
identifies these processors.
For processors without byte-addressing support, the default (and only) mode is
-char-size-32
. For more infor-
mation, see Using Byte-Addressing .
The
-char-size-8
switch defines the
__BYTE_ADDRESSING__
macro, while the
-char-size-32
switch undefines the
__BYTE_ADDRESSING__
macro.
NOTE: Invoke this switch in the IDE by setting
Project > Properties > C/C++ Build > Settings > Tool Settings >
Compiler > Processor > Char size to the required value.
-compatible-pm-dm
The compatible-pm-dm
switch directs the compiler to treat dm
and pm
qualified pointers as assignment-compatible.
-component file.xml
The
-component
(read component file) switch instructs the compiler to read the specified
file.xml
. The switch also instructs the compiler to retrieve additional switches while building applications using the component. The
IDE uses this switch to build projects that employ add-in products in addition to CCES.
2–22
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Common Switch Descriptions
-const-read-write
The
-const-read-write
switch specifies to the compiler that constants can be accessed as read-write data (as in
ANSI C). The compiler's default behavior assumes that data referenced through const
pointers never changes.
The
-const-read-write
switch changes the compiler behavior to match the ANSI C assumption, namely that the data may be changed via other nonconst
pointers.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > Language Settings > Pointers to const may point to non-const data.
-const-strings
The
-const-strings
( const
-qualify strings) switch directs the compiler to mark string literals as const
-qualified. This switch is the default behavior.
.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > Language Settings > Literal strings are const.
-D
macro
[=
definition
]
The
-D
(define macro) switch directs the compiler to define a macro. If you do not include the optional definition string, the compiler defines the macro as the string '
1
'. The compiler processes all
-D
switches on the command line before any
-U
(undefine macro) switches.
NOTE: Add instances of this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings
> Compiler > Preprocessor > Preprocessor definitions.
-dependency-add-target target
The
-dependency-add-target
switch adds target as another target that relies on the dependencies in the build.
Use this switch with switches for emitting dependency information, for example,
For example, if you are building apple.doj
from apple.c
, the compiler dependency output indicates that apple.doj
depends on apple.c
. Using
-dependency-add-target pear.doj
causes the compiler to emit extra dependency information to indicate that pear.doj
also depends on apple.c
.
-double-size[-32|-64]
The
-double-size-32
( double
is 32 bits) and
-double-size-64
(double is 64 bits) switches select the storage format that the compiler uses for type double
. The default mode is
-double-size-32
. For more information, see
.
CrossCore Embedded Studio 2.2.0
2–23
C/C++ Compiler Common Switch Descriptions
The
-double-size-32
switch defines the
__DOUBLES_ARE_FLOATS__
macro, while the
-double-size-64 switch undefines the
__DOUBLES_ARE_FLOATS__
macro.
NOTE: Invoke this switch in the IDE by setting
Project > Properties > C/C++ Build > Settings > Tool Settings >
Compiler > Processor > Double size to the required value.
-double-size-any
The
-double-size-any
switch specifies that the input source files make no use of double
type data. It also specifies that the resulting object files to be marked in a way that enables them to be linked against objects built with doubles
, either 32- or 64-bit in size.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > Processor > Allow mixing of sizes.
-dry
The
-dry
(verbose dry run) switch directs the compiler to display main cc21k
actions, but not to perform them.
-dryrun
The
-dryrun
(terse dry run) switch directs the compiler to display top-level cc21k
actions, but not to perform them.
-E
The
-E
(stop after preprocessing) switch directs the compiler to stop after the C/C++ preprocessor runs (without compiling). The output (preprocessed source code) prints to the standard output stream unless the output file is specified with the
-o
switch.
-ED
The
-ED
(run after preprocessing to file) switch directs the compiler to write the output of the C/C++ preprocessor to a file named original_filename.i
. After preprocessing, compilation proceeds normally.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > General > Generate preprocessed file.
-EE
The
-EE
(run after preprocessing) switch is similar to the
-E
switch, but it does not halt compilation after preprocessing.
2–24
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Common Switch Descriptions
-eh
The
-eh
(enable exception handling) switch directs the compiler to allow C++ code containing catch statements.
The switch also directs the compiler to throw expressions and other features associated with ANSI/ISO standard C+
+ exceptions. When this switch is used, the compiler defines the
__EXCEPTIONS
macro as 1.
If used when compiling C programs, without the
(C++ mode) switch, the
-eh
switch directs the compiler to generate exceptions tables but does not change the language accepted. In this case, the
__EXCEPTIONS
macro is not defined.
The
-eh
switch also causes the compiler to define the
__ADI_LIBEH__
macro during the linking stage. The macro causes appropriate sections to be activated in the
.ldf
file, and causes the program to be linked with a library built with exceptions enabled.
Object files created with exceptions enabled can be linked with objects created without exceptions. However, exceptions can only be thrown from and caught (and cleanup code executed) in modules compiled with
-eh
. If an attempt is made to throw an exception through the execution of a function not compiled with
-eh
, then abort
or the function registered with set_terminate
is called. See also #pragma generate_exceptions_tables and
.
In non-threaded applications, the buffer used for the passing of exception data is not returned to the heap on application exit. The buffer is not returned to avoid unnecessary code and has no impact on behavior.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > Language Settings > C++ exceptions and RTTI.
-enum-is-int
The
-enum-is-int
switch ensures that the type of an enum
is int
. By default, the compiler defines enumeration types with integral types larger than int
, if int
is insufficient to represent all the values in the enumeration. This switch prevents the compiler from selecting a type wider than int
. See Enumeration Type Implementation Details
for more information.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > Language Settings > Enumerated types are always int.
2.2.3.2.31 -extra-keywords
-extra-precision
The
-extra-precision
switch directs the compiler to avoid instructions and code sequences that can work incorrectly if 40-bit memory accesses are enabled. When the switch is used, the compiler does not generate the following:
• Long word (
LW
) memory accesses
• SIMD code (unless one of the loop optimization pragmas,
SIMD_for
or vector_for
, is used)
CrossCore Embedded Studio 2.2.0
2–25
C/C++ Compiler Common Switch Descriptions
• Instructions that can result in 40-bit values being truncated. For example, to aid instruction grouping, the compiler can replace a register transfer instruction with an integer
PASS
instruction. Although this gives more opportunities for issuing parallel instructions, it can result in the truncation of a 40-bit extended precision floating point value to a 32-bit floating point value.
-file-attr name[=value]
The
-file-attr
(file attribute) switch directs the compiler to add the specified attribute name/value pair to all of the files it compiles. To add multiple attributes, use the switch multiple times. If
=value
is omitted, the default
compiler emits.
and
.
NOTE: Add instances of this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings
> Compiler > General > Additional attributes.
-flags-{asm|compiler|ipa|lib|link|mem|prelink} switch[,
switch2[, ...]]
The
-flags
(command-line input) switch directs the compiler to pass command-line switches to other build tools.
The tools are listed in the Switches Passed to Other Build Tools table.
Table 2-13: Switches Passed to Other Build Tools
Switch
-asm
-compiler
-ipa
-lib
-link
-mem
-prelink
Tool
Assembler
Compiler executable
IPA solver
Library builder ( elfar.exe)
Linker
Memory initializer
Prelinker
-float-to-int
The
-float-to-int
switch instructs the compiler to use a support library function to convert a float
to an integer. The library support routine performs extra checking to avoid a floating-point underflow occurring.
-force-circbuf
The
-force-circbuf
(circular buffer) switch instructs the compiler to use circular buffer facilities. The circular buffer facilities are used even if the compiler cannot verify that the circular index or pointer is always within the range of the buffer. Without this switch, the compiler default behavior, regarding circular buffers, is conservative. In
2–26
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Common Switch Descriptions
default mode, circular buffers are not used unless verified that the circular index or pointer is always within the cir-
cular buffer range. See Circular Buffer Built-In Functions .
NOTE: Invoke this switch in the IDE by setting
Project > Properties > C/C++ Build > Settings > Tool Settings >
Compiler > Language Settings > Circular buffer generation to Even when pointer may be outside buffer
range (-force-circbuf).
-fp-associative
The
-fp-associative
switch directs the compiler to treat floating-point multiplication and addition as associative operations. This switch is on by default.
.
-full-version
The
-full-version
(display versions) switch directs the compiler to display version information for build tools used in a compilation.
-fx-contract
The
-fx-contract
switch sets the default state of
FX_CONTRACT
to
ON
, which is the default setting. This switch controls the performance and accuracy of arithmetic on the native fixed-point type, fract
for more information.
-fx-rounding-mode-biased
The
-fx-rounding-mode-biased
switch sets the default state of
FX_ROUNDING_MODE
to
BIASED
. This switch controls the rounding behavior of arithmetic on the native fixed-point type, fract
. See
for more information.
-fx-rounding-mode-truncation
The
-fx-rounding-mode-truncation
switch sets the default state of
FX_ROUNDING_MODE
to
TRUNCATION
, which is the default setting. This switch controls the rounding behavior of arithmetic on the native fixed-point type, fract
. See
for more information.
-fx-rounding-mode-unbiased
The
-fx-rounding-mode-unbiased
switch sets the default state of
FX_ROUNDING_MODE
to
UNBIASED
. This switch controls the rounding behavior of arithmetic on the native fixed-point type, fract
. See
Setting the Rounding Mode for more information.
CrossCore Embedded Studio 2.2.0
2–27
C/C++ Compiler Common Switch Descriptions
-g
The
-g
(generate debug information) switch directs the compiler to output symbols and other information used by the debugger.
When the
-g
switch is used with the enable optimization (
-O
) switch, the compiler performs standard optimizations. The compiler also outputs symbols and other information to provide limited source-level debugging through the CCES IDE (debugger). This combination of switches provides line debugging and global variable debugging.
NOTE: When the
-g
and
-O
switches are specified, no debug information is available for local variables. Also, the standard optimizations can rearrange program code such that inaccurate line number information is produced. For full debugging capabilities, use the
-g
switch without the
-O
switch.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > General > Generate debug information.
-glite
The
-glite
(lightweight debugging) switch can be used on its own, or with the
-g
compiler switch. The switch instructs the compiler to remove any unnecessary debug information from the compiled code.
When used on its own, the switch also enables the
-g
switch.
NOTE: Use this switch to reduce the size of object and executable files. The switch has no effect on the size of the code loaded onto the target.
-gnu-style-dependencies
The
-gnu-style-dependencies
switch changes the format in which dependency information, such as that produced by the
-M
switch, is produced, so that it matches the format used by the GNU make
program. The
Effect of -
gnu-style-dependencies Switch table shows the switch effect.
Table 2-14: Effect of -gnu-style-dependencies Switch
Without
-gnu-styledependencies
Quoting
Whitespace
Directory separators
Path form
Yes (
"foo"
)
Quoted (
"x y"
)
Backslash (
\
)
Canonical (
"c:\foo\bar"
)
With
-gnu-style-dependencies
No ( foo
)
Escaped with backslash ( x\ y
)
Forward slash (
/
)
Relative (
../bar
)
The IDE applies this switch automatically.
2–28
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Common Switch Descriptions
-H
The
-H
(list headers) switch directs the compiler to output only a list of the files included by the preprocessor via the
#include
directive, without compiling.
-HH
The
-HH
(list headers and compile) switch directs the compiler to output a list of the files included by the preprocessor via the
#include
directive. The list is output to the standard output stream. After preprocessing, compilation proceeds normally.
-h[elp]
The
-help
(command-line help) switch directs the compiler to output a list of command-line switches with a brief syntax description.
-I directory[{,|;} directory...
]
The
-I
(include search directory) switch directs the C/C++ compiler preprocessor to append the directory (directories) to the search path for include
files. This option can be specified more than once; all specified directories are added to the search path.
NOTE: Add instances of this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings
> Compiler > Preprocessor > Additional include directories.
Include files, whose names are not absolute path names and that are enclosed in "..." when included, are searched for in the following directories in this order:
1. The directory containing the current input file (the primary source file or the file containing the
#include
)
2. Any directories specified with the
-I
switch in the order they are listed on the command line
3. Any directories on the standard list,
<CCES_install_dir>\...\include
NOTE: If a file is included using the <...> form, this file is only searched for by using directories defined in items
2 and 3 above.
-I-
The
-I-
(start include directory list) switch establishes the point at which the search for header files enclosed in angle brackets begins. The point is in the include
directory list Normally, for header files enclosed in double quotes, the compiler searches in the directory containing the current input file. Then the compiler reverts to looking in the directories specified with the
-I-
switch, then in the standard include directory.
The initial search is within the directory containing the current input file. It is possible to replace the initial search by placing
-I-
at the command-line point where the search for all types of header file begins. All include
directories on the command line specified before
-I-
are used only in the search for header files that are enclosed in double quotes.
CrossCore Embedded Studio 2.2.0
2–29
C/C++ Compiler Common Switch Descriptions
NOTE: The
-I-
switch removes the directory containing the current input file from the include directory list.
-i
The
-i
(less includes) switch can be used with the
-H
,
-HH
,
-M
, and
-MM
switches. The switch directs the compiler to output only header details (
-H
,
-HH
) or makefile dependencies (
-M
,
-MM
) for include
files in double quotes.
-include filename
The
-include
(include file) switch directs the preprocessor to process the specified file before processing the regular input file. Any
-D
and
-U
switches on the command line are processed before an
-include
file. Only one
include
can be given.
-ipa
The
-ipa
(interprocedural analysis) switch turns on Interprocedural Analysis (IPA) in the compiler. This option enables optimization across the entire program, including between source files that were compiled separately. If used, the
-ipa
-ipa
switch.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > General > Interprocedural optimization.
-L directory[{;|,} directory …
]
The
-L
(library search directory) switch directs the compiler to append the directory to the search path for library files.
NOTE: Add instances of this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings
> Linker > General > Search directories.
-l
library
The
-l
(link library) switch directs the compiler to search the library for functions and global variables when linking. The library name is the portion of the file name between the lib
prefix and
.dlb
extension.
For example, the
-lc
compiler switch directs the linker to search in the library named c
. This library resides in a file named libc.dlb
.
When using this switch, list all object files on the command line before listing libraries using the
-l
switch. When a reference to a symbol is made, the symbol definition will be taken from the left-most object or library on the command line that contains the global definition of that symbol. If two objects on the command line contain definitions of the symbol x
, x
will be taken from the left-most object on the command line that contains a global definition of x
.
2–30
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Common Switch Descriptions
If one of the definitions for x
comes from user objects, and the other from a user library, and the library definition should be overridden by the user object definition, it is important that the user object comes before the library on the command line.
Libraries included in the default
.ldf
file are searched last for symbol definitions.
NOTE: Add instances of this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings
> Linker > General > Additional libraries and object files.
-linear-simd
The
-linear-simd
(generate linear SIMD code) switch directs the compiler to attempt to produce SIMD instructions in linear code. For more information, see
-list-workarounds
The
-list-workarounds
(list supported errata workarounds) switch displays a list of all errata workarounds which the compiler supports. See
Controlling Silicon Revision and Anomaly Workarounds Within the Compiler
for details of valid workarounds and the interaction of the
-si-revision
,
-workaround
, and
-no-workaround switches.
-loop-simd
The
-loop-simd
(use SIMD in loops) switch directs the compiler to generate SIMD code when possible.
ATTEN-
TION:
On certain SHARC processors (e.g. ADSP-21367/8/9), SIMD accesses of external memory are not supported. Do not use this switch if the data that is accessed in the loop might be mapped to external memory. For more information, refer to your processor's hardware reference manual.
-M
The
-M
(generate make rules only) switch directs the compiler not to compile the source file, but to output a rule suitable for the make utility, describing the dependencies of the main program file. The format of the make rule output by the preprocessor is:
object-file: include-file …
-MD
The
-MD
(generate make rules and compile) switch directs the preprocessor to print to a file called original_filename.d
a rule describing the dependencies of the main program file. After preprocessing, compilation proceeds normally.
CrossCore Embedded Studio 2.2.0
2–31
C/C++ Compiler Common Switch Descriptions
-MM
The
-MM
(generate make rules and compile) switch directs the preprocessor to print to standard out a rule describing the dependencies of the main program file. After preprocessing, compilation proceeds normally.
-Mo filename
The
-Mo filename
(preprocessor output file) switch directs the compiler to use
filename
for the output of
-MD or
-ED
switches.
-Mt name
The
-Mt name
(output make rule for the named source) switch modifies the target of generated dependencies, renaming the target to name. It only has an effect when used in conjunction with the
-M
or
-MM
switch.
-map filename
The
-map filename
(generate a memory map) switch directs the linker to output a memory map of all symbols.
The map file name corresponds to the
filename
argument. For example, if the argument is test
, the map file name is test.xml
. The
.xml
extension is added where necessary.
-mem
The
-mem
(enable memory initialization) switch directs the compiler to run the mem21k
initializer (utility). The memory initializer can be controlled through the mem
switch, or disabled using the
For more information, see:
• Processor Startup in the System Run-Time Documentation.
• Memory Initializer in the Linker and Utilities Manual.
-multiline
The
-multiline
switch directs the compiler to allow string literals to span multiple lines without the need for a
"\" at the end of each line. This is the default mode for C source files. The switch has no effect when compiling C++ source files which do not support multiline string literals.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > Language Settings > Allow multi-line character strings.
-never-inline
The
-never-inline
switch instructs the compiler to ignore the inline
qualifier on function definitions, so that no calls to such functions will be inlined.
.
2–32
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Common Switch Descriptions
NOTE: Invoke this switch in the IDE by setting
Project > Properties > C/C++ Build > Settings > Tool Settings >
Compiler > General > Inlining to Never.
-no-aligned-stack
The
-no-aligned-stack
(disable stack alignment) switch directs the compiler to not to attempt to maintain alignment of the program stack on a double-word boundary. This can result in slightly more efficient code and use
of stack space if there is no requirement to keep the stack aligned. See Stack Alignment
for more information.
-no-alttok
The
-no-alttok
(disable alternative tokens) switch directs the compiler not to accept digraph sequences in the source files. This switch is enabled by default in C++ mode, and disabled by default in C89 and C99 modes. In C++ mode, the switch also controls the acceptance of alternative operator keywords.
.
-no-annotate
The
-no-annotate
(disable assembly annotations) switch directs the compiler not to annotate assembly files generated by the compiler. The default behavior is that whenever optimizations are enabled all assembly files generated
Achieving Optimal Performance From C/C++ Source Code
chapter for more details on this feature.
NOTE: Invoke this switch in the IDE by clearing
Project > Properties > C/C++ Build > Settings > Tool Settings >
Compiler > General > Generate annotations.
-no-annotate-loop-instr
The
-no-annotate-loop-instr
switch disables the production of additional loop annotation information by the compiler. This is the default mode.
-no-assume-vols-are-iops
The
-no-assume-vols-are-iops
switch specifies that the compiler should not assume that volatile accesses to memory are accessing memory-mapped IOP registers. By default, the compiler will apply workarounds for IOPrelated anomalies to volatile memory accesses, unless it can determine that the access is not to an IOP register. When the
-no-assume-vols-are-iops
switch is used, memory-mapped IOP registers should be accessed using the iop_read
and iop_write
built-in functions (see
Miscellaneous Built-In Functions
).
CrossCore Embedded Studio 2.2.0
2–33
C/C++ Compiler Common Switch Descriptions
-no-auto-attrs
The
-no-auto-attrs
(no automatic attributes) switch directs the compiler not to emit automatic attributes based
for more information about attributes, and what automatic attributes the compiler emits.
and
NOTE: Invoke this switch in the IDE by clearing
Project > Properties > C/C++ Build > Settings > Tool Settings >
Compiler > General > Auto-generated attributes.
-no-circbuf
The
-no-circbuf
(no circular buffer) switch disables the automatic generation of circular buffer code by the compiler. Uses of the circindex()
and circptr()
functions (that is, explicit circular buffer operations) are not affected.
NOTE: Invoke this switch in the IDE by setting
Project > Properties > C/C++ Build > Settings > Tool Settings >
Compiler > Language Settings > Circular Buffer Generation to Never.
-no-const-strings
The
-no-const-strings
switch directs the compiler not to make string literals const
qualified.
.
NOTE: Invoke this switch in the IDE by clearing
Project > Properties > C/C++ Build > Settings > Tool Settings >
Compiler > Language Settings > Literal strings are const.
-no-db
The
-no-db
(no delayed branches) switch specifies that the compiler shall not generate jumps that use delayed branches.
NOTE: Disabling of interrupts within the epilogue code of a re-entrant interrupt function still uses a delayed branch jump to minimize interrupt latency.
-no-defs
The
-no-defs
(disable defaults) switch directs the preprocessor not to define any default preprocessor macros, include directories, library directories or libraries. It also disables the Analog Devices cc21k
C/C++ keyword extensions.
-no-eh
The
-no-eh
(disable exception handling) switch directs the compiler to disallow ANSI/ISO C++ exception handling. This is the default mode.
2–34
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Common Switch Descriptions
.
-no-extra-keywords
The
-no-extra-keywords
(disable short-form keywords) switch directs the compiler not to recognize the Analog
Devices keyword extensions that might conflict with valid C/C++ identifiers, for example, keywords such as pm
and dm
. Alternate keywords, which are prefixed with two leading underscores, such as
__pm
and
__dm
, continue to work.
See also
-extra-keywords
.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > Language Settings > Disable Analog Devices extension keywords.
-no-fp-associative
The
-no-fp-associative
switch directs the compiler not to treat floating-point multiplication and addition as associative operations.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > Language Settings > Do not treat floating-point operations as associative.
-no-fx-contract
The
-no-fx-contract
switch sets the default state of
FX_CONTRACT
to
OFF
. This switch controls the performance and accuracy of arithmetic on the native fixed-point type, fract
for more information.
-no-linear-simd
The
-no-linear-simd
(do not generate linear SIMD code) switch directs the compiler not to attempt to produce
SIMD instructions in linear code. For more information, see
-no-loop-simd
The
-no-loop-simd
(do not generate loop SIMD code) switch directs the compiler not to attempt to produce
SIMD instructions in loops except those marked with the
SIMD_for
or vector_for
pragmas. For more information, see
.
.
CrossCore Embedded Studio 2.2.0
2–35
C/C++ Compiler Common Switch Descriptions
-no-main-calls-exit
The
-no-main-calls-exit
switch specifies that the compiler should not insert a call to exit()
at the end of main()
. Instead, main()
will end with the standard function return sequence.
-no-mem
The
-no-mem
(disable memory initialization) switch directs the compiler not to run the mem21k
initializer.
.
-no-multiline
The
-no-multiline
switch directs the compiler to disallow string literals which span multiple lines without a "\" at the end of each line.
.
NOTE: Invoke this switch by clearing
Project > Properties > C/C++ Build > Settings > Tool Settings > Compiler >
Language Settings > Allow multi-line character strings.
-no-progress-rep-timeout
The
-no-progress-rep-timeout
(disable progress message for long compilations) switch disables the diagnostic message issued by the compiler to indicate that it is still working, when a function's compilation is taking an excessively long time. The message is disabled by default.
-progress-rep-timeout-secs secs
-no-rtcheck
The
-no-rtcheck
(disable run-time checking) switch directs the compiler to disable generation of additional code to check at runtime for common programming errors. This switch is the default, and is equivalent to specifying all of the following switches:
•
•
•
•
•
•
•
2–36
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Common Switch Descriptions
-no-rtcheck-arr-bnd
The
-no-rtcheck-arr-bnd
(do not check array bounds at runtime) switch directs the compiler not to generate additional code to verify that array accesses are within the bounds of the array.
NOTE: Invoke this behavior in the IDE via the run-time checking options under
Project > Properties > C/C++
Build > Settings > Tool Settings > Compiler > Processor.
.
-no-rtcheck-div-zero
The
-no-rtcheck-div-zero
(do not check for division by zero at runtime) switch directs the compiler not to generate additional code to verify that divisors are non-zero before performing division operations.
NOTE: Invoke this behavior in the IDE via the run-time checking options under
Project > Properties > C/C++
Build > Settings > Tool Settings > Compiler > Processor.
.
-no-rtcheck-heap
The
-no-rtcheck-heap
(do not heap operations at runtime) switch directs the compiler not to link against the debugging version of the heap libraries.
NOTE: Invoke this behavior in the IDE via the run-time checking options under
Project > Properties > C/C++
Build > Settings > Tool Settings > Compiler > Processor.
.
-no-rtcheck-null-ptr
The
-no-rtcheck-null-ptr
(do not check for NULL pointers at runtime) switch directs the compiler not to generate additional code to verify that pointers are not NULL, before dereferencing them.
NOTE: Invoke this behavior in the IDE via the run-time checking options under
Project > Properties > C/C++
Build > Settings > Tool Settings > Compiler > Processor.
.
-no-rtcheck-shift-check
The
-no-rtcheck-shift-check
(do not check shift values at runtime) switch directs the compiler not to generate additional code to verify that, when shifting a value X by some amount Y, Y is a positive amount, and less than the number of bits used to represent X's type.
CrossCore Embedded Studio 2.2.0
2–37
C/C++ Compiler Common Switch Descriptions
NOTE: Invoke this behavior in the IDE via the run-time checking options under
Project > Properties > C/C++
Build > Settings > Tool Settings > Compiler > Processor.
.
-no-rtcheck-stack
The
-no-rtcheck-stack
(do not check for stack overflow at runtime) switch directs the compiler not to link in the modified startup and interrupt vector code to enable CB7I to trap stack overflow occurrences.
NOTE: Invoke this behavior in the IDE via the run-time checking options under
Project > Properties > C/C++
Build > Settings > Tool Settings > Compiler > Processor.
-no-rtcheck-unassigned
The
-no-rtcheck-unassigned
(do not check variables are assigned at runtime) switch directs the compiler not to generate additional code to verify that variables have been assigned a value before they are used.
NOTE: Invoke this behavior in the IDE via the run-time checking options under
Project > Properties > C/C++
Build > Settings > Tool Settings > Compiler > Processor.
.
-no-sat-associative
The
-no-sat-associative
(saturating addition is not associative) switch instructs the compiler not to consider saturating addition operations as associative:
(a+b)+c
may not be rewritten as a+(b+c)
, when the addition operator saturates. The default is that saturating addition is not associative.
-no-saturation
The
-no-saturation
switch directs the compiler not to introduce faster operations in cases where the faster operation would saturate (if the expression overflowed) when the original operation would have wrapped the result. The code produced may be less efficient than when the switch is not used. Saturation is enabled by default when optimizing, and may be disabled by this switch. Saturation is disabled when not optimizing (this switch is the default when not optimizing).
-no-shift-to-add
The
-no-shift-to-add
switch prevents the compiler from replacing a shift-by-one instruction with an addition.
While this can produce faster code, it can also lead to arithmetic overflow.
2–38
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Common Switch Descriptions
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > Processor > Disable shift-to-add conversion.
-no-simd
The
-no-simd
(disable SIMD mode) switch directs the compiler to disable automatic SIMD code generation.
Note that SIMD code is still generated for a loop if it is preceded with the
SIMD_for
pragma. The pragma is treated as an explicit user request to generate SIMD code and is always obeyed, if possible.
.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > Processor > Disable automatic SIMD code generation.
-no-std-ass
The
-no-std-ass
(disable standard assertions) switch prevents the compiler from defining the standard assertions.
for the list of standard assertions.
-no-std-def
The
-no-std-def
(disable standard macro definitions) switch prevents the compiler from defining default preprocessor macro definitions.
NOTE: This switch also disables the Analog Devices keyword extensions that have no leading underscores, such as pm
and dm
.
-no-std-inc
The
-no-std-inc
(disable standard include search) switch directs the C/C++ preprocessor to search for header files in the current directory and directories specified with the
-I
switch.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > Preprocessor > Ignore standard include paths.
-no-std-lib
The
-no-std-lib
(disable standard library search) switch directs the compiler to search for libraries in only the current project directory and directories specified with the
-L
switch.
-no-threads
The
-no-threads
(disable thread-safe build) switch directs the compiler to link against the non-thread-safe variants of the C/C++ run-time library. This is the default.
CrossCore Embedded Studio 2.2.0
2–39
C/C++ Compiler Common Switch Descriptions
-no-workaround workaround_id[, workaround_id …]
The
-no-workaround
(disable avoidance of specific errata) switch disables compiler code generator workarounds
details of valid workarounds and the interactions of the
-si-revision
,
-workaround
, and
-no-workaround switches.
-normal-word-code
The
-normal-word-code
switch has the same effect as compiling with the
-nwc
switch. It directs the compiler to generate instructions of normal word size (48-bits). This switch applies only when compiling code targeted for processors that support VISA execution, shown in the
Processors Supporting VISA Execution table (see Processor Features
).
NOTE: Invoke this switch in the IDE by setting
Project > Properties > C/C++ Build > Settings > Tool Settings >
Compiler > Processor > Variable Instruction Set Encoding (VISA) to Generate Normal Word code.
-nwc
The
-nwc
switch has the same effect as compiling with the
-normal-word-code
switch. It directs the compiler to generate instructions of normal word size (48-bits). This switch applies only when compiling code targeted for processors that support VISA execution, shown in the
Processors Supporting VISA Execution table (see Processor Features
).
NOTE: Invoke this switch in the IDE by setting
Project > Properties > C/C++ Build > Settings > Tool Settings >
Compiler > Processor > Variable Instruction Set Encoding (VISA) to Generate Normal Word code.
-O[0|1]
The
-O
(enable optimizations) switch directs the compiler to produce code that is optimized for performance. Optimizations are not enabled by default for the cc21k
compiler. (Note that the switch settings begin with the uppercase letter "O" and end with a digit - a zero or a one.) The switch setting
-O
or
-O1
turns optimization on, while setting
-O0
turns off all optimizations.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > General > Enable optimization.
-Oa
The
-Oa
(automatic function inlining) switch enables the inline expansion of C/C++ functions, which are not necessarily declared inline in the source code. The amount of auto-inlining the compiler performs is controlled using
(optimize for speed versus size) switch. Therefore, use of
-Ov100
indicates that as many functions as possible are auto-inlined, whereas
-Ov0
prevents any function from being auto-inlined. Specifying
the use of
2–40
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Common Switch Descriptions
NOTE: Invoke this switch in the IDE by setting
Project > Properties > C/C++ Build > Settings > Tool Settings >
Compiler > General > Inlining to Automatic.
-Os
The
-Os
(optimize for size) switch directs the compiler to produce code that is optimized for size. This is achieved by performing all optimizations except those that increase code size. The optimizations not performed include loop unrolling, some delay slot filling, and jump avoidance.
-Ov num
The
-Ov num
(optimize for speed versus size) switch informs the compiler of the relative importance of speed versus size, when considering whether such trade-offs are worthwhile. The
num
variable should be an integer between 0
(purely size) and 100 (purely speed).
For any given optimization, the compiler modifies the code being generated. Some optimizations produce code that will execute in fewer cycles, but which will require more code space. In such cases, there is a trade-off between speed and space.
The
num
variable indicates a sliding scale between 0 and 100 which is the probability that a linear piece of generated code-a "basic block"-will be optimized for speed or for space. At
-Ov0
all blocks are optimized for space (equivalent to
-Os
) and at
-Ov100
all blocks are optimized for speed (equivalent to
-O
). At any point in between, the decision is based upon
num
and how many times the block is expected to be executed-the "execution count" of the block.
The -Ov Switch Optimization Curve figure demonstrates this relationship.
Infinity
Optimize for speed
Limit line
Optimize for space
0
0 100
-Ovnum
Figure 2-1: -Ov Switch Optimization Curve
For any given optimization where speed and size conflict, the potential benefit is dependent on the execution count: an optimization that increases performance at the expense of code size is considerably more beneficial if applied to the core loop of a critical algorithm than if applied to one-time initialization code or to rarely-used error-handling functions. If code appears to be executed only once, it will be optimized for space. As its execution count increases,
CrossCore Embedded Studio 2.2.0
2–41
C/C++ Compiler Common Switch Descriptions
so too does the likelihood that the compiler will consider the code increase worthwhile for the corresponding benefit in performance.
As the -Ov Switch Optimization Curve figure shows, the
-Ov
switch affects the point at which a given execution count is considered sufficient to switch optimization from "for space" to "for speed". Where
num
is a low value, the compiler is biased towards space, so a block's execution count has to be relatively high for the compiler to apply code-increasing transformations. Where
num
has a high value, the compiler is biased towards speed, so the same transformation will be considered valid for a much lower execution count.
The
-Ov
switch is most effective when used in conjunction with profile- guided optimization, where accurate execution counts are available. Without profile-guided optimization, the compiler makes estimates of the relative execution counts using heuristics.
NOTE: Invoke this switch in the IDE by entering an appropriate value into the
Project > Properties > C/C++
Build > Settings > Tool Settings > Compiler > General > Optimize for code size/speed field.
For more information, see
Using Profile-Guided Optimization
.
-o filename
The
-o
(output file) switch directs the compiler to use
filename
for the name of the final output file.
-overlay
The
-overlay
(program may use overlays) switch will disable the propagation of register information between functions and force the compiler to assume that all functions clobber all scratch registers. Note that this switch will affect all functions in the source file, and may result in a performance degradation. For information on disabling the
propagation of register information only for specific functions, see #pragma overlay
.
-overlay-clobbers clobbered-regs
The
-overlay-clobbers
(registers clobbered by overlay manager) switch identifies the set of registers clobbered by an overlay manager, if one is used. The compiler will assume that any call to an overlay-managed function will clobber the values in clobbered-regs
, in addition to those clobbered by the function in question. A function is considered to be an overlay-managed function if the
switch is specified, or if the function is marked with
.
The
clobbered-regs
argument is a single string, formatted as per the argument to
#pragma regs_clobbered
, except that individual components of the list may also be separated by commas.
NOTE: Whitespace and semicolons are valid separators for the components of the list, but must be properly quoted when being passed to the compiler.
Examples: cc21k -O t.c -overlay -overlay-clobbers r3,m4,r5 cc21k -O t.c -overlay -overlay-clobbers Dscratch cc21k -O t.c -overlay -overlay-clobbers "r3 m4;r5"
2–42
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Common Switch Descriptions
-P
The
-P
(omit line numbers) switch directs the compiler to stop after the C/C++ preprocessor runs (without compiling) and to omit the
#line
preprocessor command with line number information from the preprocessor output.
The
-C
switch can be used in conjunction with
-P
to retain comments.
-PP
The
-PP
(omit line numbers and compile) switch is similar to the
-P
switch; however, it does not halt compilation after preprocessing.
-p
The
-p
(generate instrumented profiling) switch directs the compiler to generate the additional instructions needed to profile the program by recording the number of cycles spent in each function.
Applications built with the
-p
switch write the data to a
.prf
file. For more information on profiling, see Profiling
NOTE: Invoke this switch in the IDE via
Project >Properties > C/C++ Build > Settings > Tool Settings > Compiler
> Profiling > Enable compiler instrumented profiling.
-path-{ asm | compiler | ipa | lib | link | prelink } pathname
The
-path-{asm|compiler|ipa|lib|link|prelink}
pathname
(tool location) switch directs the compiler to use the specified component in place of the default-installed version of the compilation tool. The component comprises a relative or absolute path to its location. Respectively, the tools are the assembler, compiler, IPA solver, library builder, linker or prelinker. Use this switch when overriding the normal version of one or more of the tools.
The
-path-{...}
switch also overrides the directory specified by the
-path-install
switch.
-path-install directory
The
-path-install
(installation location) switch directs the compiler to use the specified directory as the location for all compilation tools instead of the default path. This is useful when working with multiple versions of the tool set.
NOTE: You can selectively override this switch with the
-path-{asm|compiler|ipa|lib|link|prelink} switch.
-path-output directory
The
-path-output
(non-temporary files location) switch directs the compiler to place final output files in the specified directory.
CrossCore Embedded Studio 2.2.0
2–43
C/C++ Compiler Common Switch Descriptions
-path-temp
directory
The
-path-temp
(temporary files location) switch directs the compiler to place temporary files in the specified directory.
-pgo-session session-id
The
-pgo-session
(specify PGO session identifier) switch is used with profile-guided optimization. It has the following effects:
switch, the compiler associates all counters for this module with the session identifier session-id
.
• When used with a previously-gathered profile (a
.pgo
file), the compiler ignores the profile contents, unless they have the same session-id
identifier.
This is most useful when the same source file is being built in more than one way (for example, different macro definitions, or for multiple processors) in the same application; each variant of the build can have a different session-id
associated with it, which means that the compiler will be able to identify which parts of the gathered profile should be used when optimizing for the final build.
If each source file is only built in a single manner within the system (the usual case), then the
-pgo-session switch is not needed.
NOTE: Invoke this switch in the IDE by entering a suitable name into the
Project > Properties > C/C++ Build >
Settings > Tool Settings > Compiler > Profile-guided Optimization > PGO Session name field.
For more information, see for more information see, Using Profile-Guided Optimization
.
-pguide
The
-pguide
switch causes the compiler to add instrumentation for the gathering of a profile (a
.pgo
file) as the first stage of performing profile-guided optimization.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > Profile-guided Optimization > Prepare application to create new profile.
For more information, see
Using Profile-Guided Optimization
.
-pplist filename
The
-pplist
(preprocessor listing) directs the preprocessor to output a listing to the named file. When more than one source file is preprocessed, the listing file contains information about the last file processed. The generated file contains raw source lines, information on transitions into and out of include files, and diagnostics generated by the compiler.
Each listing line begins with a key character that identifies its type, as shown in the
Key Characters table.
2–44
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Common Switch Descriptions
E
C
R
W
S
L
N
X
Table 2-15: Key Characters
Character Meaning
Normal line of source
Expanded line of source
Line of source skipped by
#if
or
#ifdef
Change in source position
Diagnostic message (remark)
Diagnostic message (warning)
Diagnostic message (error)
Diagnostic message (catastrophic error)
-proc processor
The
-proc
(target processor) switch specifies the compiler-produced code suitable for the specified processor. Refer to CCES online help for the list of supported SHARC processors. For example,
cc21k -proc ADSP-21161 -o bin\p1.doj p1.asm
NOTE: If no target is specified with the
-proc
switch, the system uses the ADSP-21160 processor settings as a default.
When compiling with the
-proc
switch, a hierarchy of processor macros are defined as 1 to identify the processor being used and the families it belongs to. So in the above example,
__ADSP21161__
,
__ADSP21161_FAMILY__
,
__ADSP2116x__
,
__ADSP211xx__
,
__SIMDSHARC__
, and
__ADSP21000__
are all 1.
__ADSPSHARC__
is defined to
0x110
. For more information, see Predefined Preprocessor Macros
.
for more information on silicon revision of the specified processor.
-prof-hw
The
-prof-hw
switch instructs the compiler to generate profiling code that shall be run on hardware (rather than on the simulator). The switch requires a supported profiling switch to also be specified on the command line. Sup-
ported profiling methods are profile-guided optimization (
).
NOTE: Instrumented profiling (
) does not differentiate between execution on hardware or simulator and can
be executed on both targets. It does not require the
switch.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > Profile-guided Optimization > Gather profile using hardware.
-progress-rep-func
The
-progress-rep-func
switch provides feedback on the compiler's progress that may be useful when compiling and optimizing very large source files. It issues a "warning" message each time the compiler starts compiling a
CrossCore Embedded Studio 2.2.0
2–45
C/C++ Compiler Common Switch Descriptions
new function. The "warning" message is a remark that is disabled by default, and this switch enables the remark as a warning. The switch is equivalent to
-Wwarn=cc1472
.
-progress-rep-opt
The
-progress-rep-opt
switch provides feedback on the compiler's progress that may be useful when compiling and optimizing a very large, complex function. It issues a "warning" message each time the compiler starts a new optimization pass on the current function. The "warning" message is a remark that is disabled by default, and this switch enables the remark as a warning. The switch is equivalent to
-Wwarn=cc1473
.
-progress-rep-timeout
The
-progress-rep-timeout
switch issues a diagnostic message if the compiler exceeds a time limit during compilation. This indicates the compiler is still operating, just taking a long time.
-progress-rep-timeout-secs secs
The
-progress-rep-timeout-secs
switch specifies how many seconds must elapse during a compilation before the compiler issues a diagnostic message about the length of time the compilation has used so far.
-R directory[{:|,} directory …]
The
-R directory
(add source directory) switch directs the compiler to add the specified directory to the list of directories searched for source files. On Windows platforms, multiple source directories are given as a colon, comma, or semicolon separated list.
The compiler searches for the source files in the order specified on the command line. The compiler searches the specified directories before reverting to the current project directory. The
-R directory
switch is position-dependent on the command line. That is, it affects only source files that follow the option.
NOTE: Source files whose file names begin with
/
,
./
, or
../
(or Windows equivalent) and contain drive specifiers (on Windows platforms) are not affected by this option.
-R-
The
-R-
(disable source path) switch removes all directories from the standard search path for source files, effectively disabling this feature.
NOTE: This option is position-dependent on the command line; it only affects files following it.
-reserve register[, register …]
The
-reserve
(reserve register) switch directs the compiler not to use the specified registers. This guarantees that a known set of registers are available for inline assembly code or linked assembly modules. Separate each register name with a comma on the compiler command line.
2–46
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Common Switch Descriptions
You can reserve the following registers: b0
, l0
, m0
, i0
, b1
, l1
, m1
, i1
, b8
, l8
, m8
, i8
, b9
, l9
, m9
, i9
, ustat1
, ustat2
, ustat3
and ustat4
. When reserving an
L
(length) register, you must reserve the corresponding
I
(index) register; reserving an
L
register without reserving the corresponding
I
register may result in execution problems.
-restrict-hardware-loops maximum
The
-restrict-hardware-loops maximum
switch restricts the level of nested hardware loops that the compiler generates. The default setting is 6, which is the maximum number of levels that the hardware supports.
When compiling with the
-restrict-hardware-loops maximum
switch, the compiler will assume that any functions called by the code being compiler also do not use more hardware loops than the number specified. It is therefore necessary to make sure you compile any called functions with this switch too. Functions in the standard libraries shipped with CCES do not use more than 3 levels of nested hardware loops.
C++ code makes implicit calls to code in the CCES libraries, to provide functionality such as exception handling and memory management. These functions may use a maximum of one hardware loop level. When compiling C++ code, it is therefore not possible to restrict the hardware loop usage to zero, since one loop level is required for these implicit calls. Use of the
-restrict-hardware-loops 0
switch to compile C++ code will result in a build-time error.
-rtcheck
The
-rtcheck
(run-time checking) switch directs the compiler to generate additional code to check at runtime for common programming errors. This switch is equivalent to specifying all of the following switches:
•
•
•
•
•
•
•
NOTE: Because of the additional overhead imposed by the checking code, this switch should only be employed during application development, and should not be used to build products for release.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > Processor > Enable run-time checking.
-rtcheck-arr-bnd
The
-rtcheck-arr-bnd
(check array bounds at runtime) switch directs the compiler to generate additional code to verify that array accesses are within the bounds of the array.
CrossCore Embedded Studio 2.2.0
2–47
C/C++ Compiler Common Switch Descriptions
NOTE: Because of the additional overhead imposed by the checking code, this switch should only be employed during application development, and should not be used to build products for release.
NOTE: Invoke this switch in the IDE via the run-time checking options under
Project > Properties > C/C++
Build > Settings > Tool Settings > Compiler > Processor.
-rtcheck-div-zero
The
-rtcheck-div-zero
(check for division by zero at runtime) switch directs the compiler to generate additional code to verify that divisors are non-zero before performing division operations.
NOTE: Because of the additional overhead imposed by the checking code, this switch should only be employed during application development, and should not be used to build products for release.
NOTE: Invoke this switch in the IDE via the run-time checking options under
Project > Properties > C/C++
Build > Settings > Tool Settings > Compiler > Processor.
-rtcheck-heap
The
-rtcheck-heap
(check heap operations at runtime) switch directs the compiler to link against the debugging
variant of the heap library. For more information, see Heap Debugging
.
NOTE: Because of the additional overhead imposed by the checking code, this switch should only be employed during application development, and should not be used to build products for release.
NOTE: Invoke this switch in the IDE via the run-time checking options under
Project > Properties > C/C++
Build > Settings > Tool Settings > Compiler > Processor.
-rtcheck-null-ptr
The
-rtcheck-null-ptr
(check for NULL pointers at runtime) switch directs the compiler to generate additional code to verify that pointers are not NULL, before dereferencing them.
NOTE: Because of the additional overhead imposed by the checking code, this switch should only be employed during application development, and should not be used to build products for release.
NOTE: Invoke this switch in the IDE via the run-time checking options under
Project > Properties > C/C++
Build > Settings > Tool Settings > Compiler > Processor.
2–48
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Common Switch Descriptions
-rtcheck-shift-check
The
-rtcheck-shift-check
(check shift values at runtime) switch directs the compiler to generate additional code to verify that, when shifting a value X by some amount Y, Y is a positive amount, and less than the number of bits used to represent X's type.
NOTE: Because of the additional overhead imposed by the checking code, this switch should only be employed during application development, and should not be used to build products for release.
NOTE: Invoke this switch in the IDE via the run-time checking options under
Project > Properties > C/C++
Build > Settings > Tool Settings > Compiler > Processor.
-rtcheck-stack
The
-rtcheck-stack
(check for stack overflow at runtime) switch directs the compiler to link with modified startup code and interrupt vector code that uses the CB7I interrupt to trap occurrences of stack overflow. For more
information, see Stack Overflow Detection .
NOTE: Invoke this switch in the IDE via the run-time checking options under
Project > Properties > C/C++
Build > Settings > Tool Settings > Compiler > Processor.
-rtcheck-unassigned
The
-rtcheck-unassigned
(check variables are assigned at runtime) switch directs the compiler to generate additional code to verify that variables have been assigned a value before they are used.
NOTE: Because of the additional overhead imposed by the checking code, this switch should only be employed during application development, and should not be used to build products for release.
NOTE: Invoke this switch in the IDE via the run-time checking options under
Project > Properties > C/C++
Build > Settings > Tool Settings > Compiler > Processor.
-S
The
-S
(stop after compilation) switch directs cc21k
to stop compilation before running the assembler. The compiler outputs an assembly file with an
.s
extension.
-s
The
-s
(strip debug information) switch directs the compiler to remove debug information (symbol table and other items) from the output executable file during linking.
CrossCore Embedded Studio 2.2.0
2–49
C/C++ Compiler Common Switch Descriptions
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Linker >
General > Strip all symbols.
-sat-associative
The
-sat-associative
(saturating addition is associative) switch instructs the compiler to consider saturating addition operations as associative:
(a+b)+c
may be rewritten as a+(b+c)
, when the addition operator saturates.
The default is that saturating addition is not associative.
-save-temps
The
-save-temps
(save intermediate files) switch directs the compiler to retain intermediate files, generated and normally removed as part of the various compilation stages. These intermediate files are placed in the
-pathoutput specified output directory or the build directory if the
-path-output switch is not used. See the
C/C++
Compiler Common Switches table in
Compiler Command-Line Switches for a list of intermediate files.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > General > Save temporary files.
-sectionid=section_name[, id=section_name...]
The
-section
switch controls the placement of types of data produced by the compiler. The data is placed into the section_name
section as specified on the command line.
The compiler currently supports the following section identifiers; see
Placement of Compiler-Generated Code and
for more information. code data pm_data constdata pm_constdata bsz sti switch strings vtbl vtable autoinit
Controls placement of machine instructions. Default is either seg_pmco
or seg_swco
, depending on whether normal-word code or short-word code is being generated.
Controls placement of initialized variable data. Default is seg_dmda
.
Controls placement of initialized data declared with the
_pm
keyword.
Controls placement of constant data.
Controls placement of constant data declared with the
_pm
keyword.
Controls placement of zero-initialized variable data. Default is seg_dmda
.
Controls placement of the static C++ class constructor "start" functions. Default is seg_pmco
.
For more information, see
Constructors and Destructors of Global Class Instances .
Controls placement of jump-tables used to implement C/C++ switch statements.
Controls placement of string literals.
Controls placement of the C++ virtual lookup tables. Default is seg_vtbl
.
Synonym for vtbl
.
Controls placement of data used to initialise aggregate autos.
2–50
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Common Switch Descriptions
alldata
Controls placement of data
, constdata
, bss
, strings
and autoinit
all at once.
Note that alldata
is not a real section
kind
, but rather a placeholder for data
, constdata
, bsz
, strings
and autoinit
. Therefore,
-section alldata=X is equivalent to
-section data=X
-section constdata=X
-section bsz=X
-section strings=X
-section autoinit=X
Ensure that the section selected via the command line exists within the
.ldf
file. Refer to the
Linker chapter in the
Linker and Utilities Manual.
-short-word-code
The
-short-word-code
switch has the same effect as compiling with the
switch. It directs the compiler to generate instructions of short word size (16/32/48-bits). This switch only applies when compiling code targeted for processors that support VISA execution, shown in the Processors Supporting VISA Execution table (see
NOTE: Invoke this switch in the IDE by setting
Project > Properties > C/C++ Build > Settings > Tool Settings >
Compiler > Processor > Variable Instruction Set Encoding (VISA) to Generate VISA code.
-show
The
-show
(display command line) switch shows the command-line arguments passed to cc21k
, including expanded option files and environment variables. This option allows you to ensure that command-line options have been passed successfully.
-si-revision version
The
-si-revision version
(silicon revision) switch directs the compiler to build for a specific hardware revision (version). Any errata workarounds available for the targeted silicon revision will be enabled. For more information, see
Controlling Silicon Revision and Anomaly Workarounds Within the Compiler
.
-signed-bitfield
The
-signed-bitfield
(make plain bit-fields signed) switch directs the compiler to make plain bit-fields -- those which have not been declared with an explicit signed or unsigned keyword -- be signed. This is the default mode.
For more information, see
CrossCore Embedded Studio 2.2.0
2–51
C/C++ Compiler Common Switch Descriptions
-structs-do-not-overlap
The
-structs-do-not-overlap
switch specifies that the source code being compiled contains no structure copies such that the source and the destination memory regions overlap each other in a non-trivial way.
For example, in the statement
*p = *q; where p
and q
are pointers to some structure type S, the compiler, by default, always ensures that, after the assignment, the structure pointed to by p
contains an image of the structure pointed to by q
prior to the assignment. In the case where p
and q
are not identical (in which case the assignment is trivial) but the structures pointed to by the two pointers may overlap each other, doing this means that the compiler must use the functionality of the C library function memmove
rather than memcpy
.
It is slower to use memmove
to copy data than it is to use memcpy
. Therefore, if your source code does not contain such overlapping structure copies, you can obtain higher performance by using the command-line switch
structs-do-not-overlap
.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > Language Settings > Structs/classes do not overlap.
-swc
The
-swc
switch has the same effect as compiling with the
switch. It directs the compiler to generate instructions of short-word size (16/32/48-bits). This switch only applies when compiling code targeted for processors that support VISA execution, shown in the Processors Supporting VISA Execution table (in
).
NOTE: Invoke this switch in the IDE by setting
Project > Properties > C/C++ Build > Settings > Tool Settings >
Compiler > Processor > Variable Instruction Set Encoding (VISA) to Generate VISA code.
-syntax-only
The
-syntax-only
(check syntax only) switch directs the compiler to check the source code for syntax errors but not to write any output.
-sysdefs
The
-sysdefs
(system definitions) switch directs the compiler to define several preprocessor macros describing the current user and user's system. The macros are defined as character string constants.
The System Macros Defined table shows the macros if the system returns information.
2–52
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Common Switch Descriptions
Table 2-16: System Macros Defined
Macro
__HOSTNAME__
__SYSTEM__
__USERNAME__
Description
The name of the host machine
The Operating System name of the host machine
The current user's login name
-T filename
The
-T
(linker description file) switch directs the compiler to use the specified linker description file (
.ldf
) as control input for linking. If
-T
is not specified, a default
.ldf
file is selected based on the processor variant.
-threads
The
-threads
switch directs the compiler to link against the thread-safe variants of the C/C++ run-time libraries.
When used, the
-threads
switch defines the macro
_ADI_THREADS
as one (
1
) at the compile, assemble and link phases of a build.
.
NOTE: The use of the
-threads
switch does not imply that the compiler will produce thread-safe code when compiling C/C++ source. Make sure to use multi-threaded programming practices in your code.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Linker >
Processor > Link against thread-safe run-time libraries.
-time
The
-time
(tell time) switch directs the compiler to display the elapsed time as part of the output information about each phase of the compilation process.
-U macro
The
-U
(undefine macro) switch directs the compiler to undefine macros. If you specify a macro name, it is undefined. Note that the compiler processes all
-D
(define macro) switches on the command line before any
-U
(undefine macro) switches.
.
NOTE: Add instances of this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings
> Compiler > Preprocessor > Preprocessors undefines.
-unsigned-bitfield
The
-unsigned-bitfield
(make plain bit-fields unsigned) switch directs the compiler to make plain bit-fieldsthose which have not been declared with an explicit signed
or unsigned
keyword - be unsigned.
CrossCore Embedded Studio 2.2.0
2–53
C/C++ Compiler Common Switch Descriptions
For example, given the declaration
struct {
int a:2;
int b:1;
signed int c:2;
unsigned int d:2;
} x;
The
Bit-Field Values table lists the bit-field
values.
Table 2-17: Bit-Field Values
Field
x.a
x.b
x.c
x.d
-unsigned-bitfield
-2..1
0..1
-2..1
0..3
-signed-bitfield
0..3
-1..0
-2..1
0..3
Why
Plain field
Plain field
Explicit signed
Explicit unsigned
-v
The
-v
(version and verbose) switch directs the compiler to display both the version and command-line information for all the compilation tools as they process each file.
-verbose
The
-verbose
(display command line) switch directs the compiler to display command-line information for all the compilation tools as they process each file.
-version
The
-version
(display version) switch directs the compiler to display its version information.
-W{annotation|error|remark|suppress|warn} number[, number ...]
The
-W{...} number
(override error message) switch directs the compiler to override the severity of the specified diagnostic messages (annotations, errors, remarks, or warnings). The
number
argument specifies the message to override.
At compilation time, the compiler produces a number for each specific compiler diagnostic message. The
{D}
(discretionary) string after the diagnostic message number indicates that the you can override the severity of the diagnostic. Each diagnostic message is identified by a number that is the same in all compiler software releases.
NOTE: If the processing of the compiler command line generates a diagnostic, the position of the
-W
switch on the command line is important. If the
-W
switch changes the severity of the diagnostic, it must occur
2–54
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Common Switch Descriptions
before the command line switch that generates the diagnostic; otherwise, no change of severity will occur.
Also, as shown in the Console view and in help, error codes sometimes begin with a leading zero (for example, cc0025
). If you try to suppress error codes with
#pragma diag()
or
-W{annotation| error|remark|suppress|warn} number[, number ...]
, and supply the code with a leading zero, it does not work. This is because the compiler reads the number as an octal value, and will suppress a different warning or error.
-Wannotations
The
-Wannotations
(enable code generation annotations) switch directs the compiler to issue code generation annotations, which are messages milder than warnings that may help you to optimize your code.
NOTE: Invoke this switch in the IDE by settings
Project > Properties > C/C++ Build > Settings > Tool Settings >
Compiler > Warning > Warning/annotation/remark control to Errors, warnings and annotations.
-Werror-limit number
The
-Werror-limit
(maximum compiler errors) switch lets you set a maximum number of errors for the compiler before it aborts.
-Werror-warnings
The
-Werror-warnings
(treat warnings as errors) switch directs the compiler to treat all warnings as errors, with the result that a warning will cause the compilation to fail.
-Wremarks
The
-Wremarks
(enable diagnostic warnings) switch directs the compiler to issue remarks, which are diagnostic messages milder than warnings. Code generation annotations also is issued, unless disabled with the
NOTE: Invoke this switch in the IDE by settings
Project > Properties > C/C++ Build > Settings > Tool Settings >
Compiler > Warning > Warning/annotation/remark control to Errors, warnings, annotations and re-
marks.
-Wterse
The
-Wterse
(enable terse warnings) switch directs the compiler to issue the briefest form of warnings. This also applies to errors and remarks.
-w
The
-w
(disable all warnings) switch directs the compiler not to issue warnings.
NOTE: Invoke this switch in the IDE by settings
Project > Properties > C/C++ Build > Settings > Tool Settings >
Compiler > Warning > Warning/annotation/remark control to Errors only.
CrossCore Embedded Studio 2.2.0
2–55
C/C++ Compiler Common Switch Descriptions
NOTE: If the processing of the compiler command line generates a warning, the position of the
-w
switch on the command line is important. If the
-w
switch is located before the command line switch that causes the warning, the warning will be suppressed; otherwise, it will not be suppressed.
-warn-component
The
-warn-component
(warn if component elements are missing) switch instructs the compiler to issue warnings if it cannot locate libraries that are requested by the XML file of the component. For more information, see
-warn-protos
The
-warn-protos
(warn if incomplete prototype) switch directs the compiler to issue a warning when it calls a function for which an incomplete function prototype has been supplied. This option has no effect in C++ mode.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > Warning > Function declarations without prototypes.
-workaround workaround_id[, workaround_id…]
The
-workaround
(enable avoidance of specific errata) switch enables compiler code generator workarounds for specific hardware errata.
See Controlling Silicon Revision and Anomaly Workarounds Within the Compiler
for details of valid workarounds and the interaction of the
-si-revision
,
-workaround
, and
-no-workaround
switches.
-xref filename
The
-xref
(cross-reference list) switch directs the compiler to write cross-reference listing information to the specified file. When more than one source file has been compiled, the listing contains information about the last file processed.
For each reference to a symbol in the source program, a line of the form
symbol-id name ref-code filename line-number column-number is written to the named file.
The symbol-id
identifier represents a unique decimal number for the symbol, and ref-code
is one of the characters listed in the ref-code Characters table.
Table 2-18: ref-code Characters
Character
D d
Meaning
Definition
Declaration
2–56
CrossCore Embedded Studio 2.2.0
Compiler Command-Line Switches
C
R
A
U
E
Table 2-18: ref-code Characters (Continued)
Character Meaning
M
Modification
Address taken
Used
Changed (used and modified)
Any other type of reference
Error (unknown type of reference)
NOTE: Please note that the compiler’s
-xref
switch differs from the
-xref
switch used by the linker. Refer to the Linker and Utilities Manual for more information.
C Mode (MISRA) Compiler Switch Descriptions
The following switches apply only to the C compiler. See
for more information.
-misra
The
-misra
switch enables checking for MISRA-C Guidelines. Some rules or parts of rules are relaxed with this switch enabled. Rules relaxed by this option are 5.1, 5.7, 6.3, 6.4, 8.1, 8.2, 8.5, 10.3, 10.4, 10.5, 12.8, 13.7 and
19.7. This is explained in more detail, see
.
The
-misra
switch is not supported in conjunction with some switches. For more information, see
Command-Line Switch Restrictions . The switch predefines the
_MISRA_RULES
preprocessor macro.
-misra-linkdir directory
The
-misra-linkdir
switch specifies a directory in which to place
.misra
files. The default is a local directory called
MISRARepository
. The
.misra
files enable checking of violations of rules 5.5, 8.8 and 8.10.
-misra-no-cross-module
The switch implies
-misra
, but also disables checking for a number of rules that require the use of the prelinker to check across multiple modules for rule violation. The MISRA-C rules suppressed are 5.5, 8.8 and 8.10.
The
-misra-no-cross-module
switch is not supported in conjunction with some switches. For more information, see
MISRA-C Command-Line Switch Restrictions .
-misra-no-runtime
The switch implies
-misra
, but also disables run-time checking for MISRA-C rules 21, 17.1, 17.2 and 17.3. It limits the checking of rules 9.1, 12.8, 16.2 and 17.4.
The
-misra-no-runtime
switch is not supported in conjunction with some switches. For more information, see
MISRA-C Command-Line Switch Restrictions .
CrossCore Embedded Studio 2.2.0
2–57
C Mode (MISRA) Compiler Switch Descriptions
-misra-strict
The
-misra-strict
switch enables checking for MISRA-C Guidelines. The switch ensures a strict interpretation of the MISRA-C: 2004 Guidelines. See
for details.
The
-misra-strict
switch is not supported in conjunction with some switches. For more information, see
RA-C Command-Line Switch Restrictions . The switch predefines the
_MISRA_RULES
preprocessor macro.
-misra-suppress-advisory
The switch implies
-misra
, but suppresses the reporting of advisory rules.
The
-misra-suppress-advisory
switch is not supported in conjunction with some switches. For more infor-
mation, see MISRA-C Command-Line Switch Restrictions
.
-misra-testing
The switch implies
-misra
, but also suppresses checking of MISRA-C rules 20.4, 20.7, 20.8, 20.9, 20.10, 20.11, and 20.12.
The
-misra-testing
switch is not supported in conjunction with some switches. For more information, see
MISRA-C Command-Line Switch Restrictions .
-Wmis_suppress rule_number[, rule_number]
The
-Wmis_suppress
switch with a
rule_number
argument directs the compiler to suppress the specified diagnostic for a MISRA-C rule. The
rule_number
argument identifies the specific message to override.
-Wmis_warn rule_number[, rule_number]
The
-Wmis_warn
switch with a
rule_number
argument directs the compiler to override the severity of the specified diagnostic to produce a warning for a MISRA-C rule. The
rule_number
argument identifies the specific message to override.
MISRA-C Command-Line Switch Restrictions
The Switches Disallowed by MISRA-C table lists the command-line switches that are disallowed in MISRA-C mode.
Table 2-19: Switches Disallowed by MISRA-C
Switch Name
-Wsuppress
-W{annotation|error|remark|suppress|warn} number[, number ...]
)
-Wwarn
(
-W{annotation|error|remark|suppress|warn} number[, number ...]
)
2–58
CrossCore Embedded Studio 2.2.0
Compiler Command-Line Switches
Table 2-19: Switches Disallowed by MISRA-C (Continued)
Switch Name
C++ Mode Compiler Switch Descriptions
The following switches apply only when compiling in C++ mode.
-anach
The
-anach
(enable C++ anachronisms) directs the compiler to accept some language features that are prohibited by the C++ standard but still in common use. Use the
switch for greater standard compliance.
The following anachronisms are accepted in C++ mode when the
-anach
switch is enabled:
• Overload is allowed in function declarations. It is accepted and ignored.
• Definitions are not required for static data members that can be initialized using default initialization. The anachronism does not apply to static data members of template classes; they must always be defined.
• The number of elements in an array may be specified in an array delete operation. The value is ignored.
• A single operator++()
function can be used to overload both prefix and postfix ++ operations.
• A single operator--()
function can be used to overload both prefix and postfix -- operations.
• The base class name may be omitted in a base class initializer if there is only one immediate base class.
• A bound function pointer (a pointer to a member function for a given object) can be cast to a pointer to a function.
• A nested class name may be used as an un-nested class name provided no other class of that name has been declared. The anachronism is not applied to template classes.
• A reference to a nonconst
type may be initialized from a value of a different type. A temporary is created; it is initialized from the (converted) initial value, and the reference is set to the temporary.
• A reference to a nonconst
class type may be initialized from an rvalue
of the class
type or a derived class thereof. No (additional) temporary is used.
• A function with old-style parameter declarations is allowed and may participate in function overloading as though it were prototyped. Default argument promotion is not applied to parameter types of such functions when the check for compatibility is done, so that the following statements declare the overload of two functions named f
.
int f(int);
int f(x) char x; { return x; }
CrossCore Embedded Studio 2.2.0
2–59
C++ Mode Compiler Switch Descriptions
-check-init-order
It is not guaranteed that global objects requiring constructors are initialized before their first use in a program consisting of separately compiled units. The compiler will output warnings if these objects are external to the compilation unit and are used in dynamic initialization or in constructors of other objects. These warnings are not dependent on the
-check-init-order
switch.
In order to catch uses of these objects and to allow the opportunity for code to be rewritten, the
-check-initorder
(check initialization order) switch adds run-time checking to the code. This generates output to stderr that indicates uses of such objects are unsafe.
NOTE: This switch generates extra code to aid development, and should not be used when building production systems.
NOTE: Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compil-
er > Language Settings > Check initialization order.
-friend-injection
The
-friend-injection
switch directs the compiler to conform to perform name lookup in a non-standard way with respect to friend declarations. With this switch enabled a friend declaration will be injected into the scope enclosing the class containing the friend declaration.
-full-dependency-inclusion
The
-full-dependency-inclusion
switch ensures that when generating dependency information for implicitly-included
.cpp
files, the
.cpp
file will be re-included. This file is re-included only if the
.cpp
files are included more than once in the source (via re-inclusion of their corresponding header file). This switch is required only if your C++ sources files are compiled more than once with different macro guards.
NOTE: Enabling this switch may increase the time required to generate dependencies.
-implicit-inclusion
The
-implicit-inclusion
switch directs the compiler to enable the implicit inclusion of source files as a method of finding definitions of template entities to be instantiated. The compiler will automatically include a source file
.C
,
.c
or
.cpp
when the corresponding header file
.h
or
.hxx
is included. This is a mechanism that was common practice, but is not standard behavior.
.
NOTE: This switch is incompatible with the use of exported templates.
2–60
CrossCore Embedded Studio 2.2.0
C++ Mode Compiler Switch Descriptions
-no-anach
The
-no-anach
(disable C++ anachronisms) switch directs the compiler to disallow some old C++ language features that are prohibited by the C++ standard. See
for more information about these features. This is the default mode.
-no-friend-injection
The
-no-friend-injection
switch directs the compiler to conform to the ISO/IEC 14882:2003 standard with respect to friend declarations. The friend declaration is visible when the class of which it is a friend is among the associated classes considered by argument-dependent lookup. This is the default mode.
.
-no-implicit-inclusion
The
-no-implicit-inclusion
switch prevents implicit inclusion of source files as a method of finding definitions of template entities to be instantiated. This is compatible with the use of exported templates as defined by the
ISO/IEC:14883 standard. This is the default mode. This switch is accepted but ignored when compiling C files.
-no-rtti
The
-no-rtti
(disable run-time type identification) switch directs the compiler to disallow support for dynamic_cast
and other features of ANSI/ISO C++ run-time type identification. This is the default mode. Use
rtti
to enable this feature.
-no-std-templates
The
-no-std-templates
switch disables dependent name processing, i.e, the special lookup of names used in templates as required by the C++ standard.
.
-rtti
The
-rtti
(enable run-time type identification) switch directs the compiler to accept programs containing dynamic_cast
expressions and other features of ANSI/ISO C++ run-time type identification. The switch also causes the compiler to define the macro
__RTTI
to 1.
Invoke this switch in the IDE via
Project > Properties > C/C++ Build > Settings > Tool Settings > Compiler > Lan-
guage Settings > C++ exceptions and RTTI.
CrossCore Embedded Studio 2.2.0
2–61
C++ Mode Compiler Switch Descriptions
-std-templates
The
-std-templates
switch enables dependent name processing, that is, the special lookup of names used in templates as required by the C++ standard.
.
Environment Variables Used by the Compiler
The compiler refers to a number of environment variables during its operation, as listed below. The majority of the environment variables identify path names to directories. You should be aware that placing network paths into these environment variables may adversely affect the time required to compile applications.
• PATH This is your System search path, used to locate Windows applications when you run them. Windows uses this environment variable to locate the compiler when you execute it from the command line.
• TMP This directory is used by the compiler for temporary files, when building applications. For example, if you compile a C file to an object file, the compiler first compiles the C file to an assembly file which can be assembled to create the object file. The compiler usually creates a temporary directory within the TMP directory into which to put such files. However, if the
-save-temps
switch is specified, the compiler creates temporary files in the current directory instead. This directory should exist and be writable. If this directory does not exist, the compiler issues a warning.
• TEMP This environment variable is also used by the compiler when looking for temporary files, but only if
TMP was examined and was not set or the directory that TMP specified did not exist.
• ADI_DSP The compiler locates other tools in the tool-chain through the CCES installation directory, or through the
-path-install
switch. If neither is successful, the compiler looks in
ADI_DSP
for other tools.
• CC21K_OPTIONS If this environment variable is set, and
CC21K_IGNORE_ENV
is not set, this environment variable is interpreted as a list of additional switches to be prepended to the command line. Multiple switches are separated by spaces or new lines. A vertical-bar (|) character may be used to indicate that any switches following it will be processed after all other command-line switches.
• CC21K_IGNORE_ENV If this environment variable is set,
CC21K_OPTIONS
is ignored.
Data Type and Data Type Sizes
The sizes of intrinsic C/C++ data types are selected by Analog Devices so that normal C/C++ programs execute with hardware-native data types and therefore at high speed. The following tables show the sizes used for each intrinsic
C/C++ data type.
Table 2-20: Data Type Sizes for ADSP-21xxx and ADSP-SCxxx Processors (with no support for byte-addressing or when using -charsize-32)
Type Bit Size Result of sizeof Operator
int
32 bits signed 1
2–62
CrossCore Embedded Studio 2.2.0
Compiler Command-Line Interface
Table 2-20: Data Type Sizes for ADSP-21xxx and ADSP-SCxxx Processors (with no support for byte-addressing or when using -charsize-32) (Continued)
Type
unsigned int long unsigned long long long unsigned long long bool char unsigned char short unsigned short pointer float short fract fract long fract unsigned short fract unsigned fract unsigned long fract double long double
Bit Size
32 bits unsigned
32 bits signed
32 bits unsigned
64 bits signed
64 bits unsigned
32 bits signed
32 bits signed
32 bits unsigned
32 bits signed
32 bits unsigned
32 bits
32 bits float
32 bits fixed-point
32 bits fixed-point
32 bits fixed-point
32 bits unsigned fixed-point
32 bits unsigned fixed-point
32 bits unsigned fixed-point either 32 or 64 bits float (default 32)
64 bits float
Result of sizeof Operator
1
2
1
1
1
1
1
1
2
1
1
1
1
1
1
1
1
1 either 1 or 2 (default 1)
2
Table 2-21: Data Type Sizes for ADSP-21xxx and ADSP-SCxxx Processors (with support for byte-addressing, when using -char-size-8)
Type Bit Size Result of sizeof Operator
int unsigned int long unsigned long long long unsigned long long
32 bits signed
32 bits unsigned
32 bits signed
32 bits unsigned
64 bits signed
64 bits unsigned
4
4
4
4
8
8
CrossCore Embedded Studio 2.2.0
2–63
Data Type and Data Type Sizes
Table 2-21: Data Type Sizes for ADSP-21xxx and ADSP-SCxxx Processors (with support for byte-addressing, when using -char-size-8)
(Continued)
Type
bool char unsigned char short unsigned short pointer float short fract fract long fract unsigned short fract
Bit Size
8 bits signed
8 bits signed
8 bits unsigned
16 bits signed
16 bits unsigned
32 bits
32 bits float
32 bits fixed-point
32 bits fixed-point
32 bits fixed-point
32 bits unsigned fixed-point
Result of sizeof Operator
1
4
4
2
2
1
1
4
4
4
4 unsigned fract unsigned long fract double long double
32 bits unsigned fixed-point
32 bits unsigned fixed-point either 32 or 64 bits float (default 32)
64 bits float
4
4 either 4 or 8 (default 4)
8
The Analog Devices compiler does not support data sizes smaller than the addressable unit size on the processor. For
ADSP-21xxx and ADSP-SCxxx processors without byte-addressing support (see the
Processors Supporting Byte-Ad-
), this means that both short
and char
have the same size as int
. Although 32bit chars
are unusual, they do conform to the standard. For information about how to use the fixed-point data types in C, refer to
Using Native Fixed-Point Types
.
Integer Data Types
On any platform, the basic type int
is the native word size. For SHARC processors, it is 32 bits. Many library functions are available for 32-bit integers, and these functions provide support for the C/C++ data types int
and long int
. Pointers are the same size as int. 64-bit integer support is provided by the long long
and unsigned long long
data types, which are emulated data types, implemented through software.
Floating-Point Data Types
For SHARC processors, the float
data type is 32 bits long. The double
data type is option-selectable for 32 or 64 bits. The C and C++ languages tend to default to double
for constants and for many floating-point calculations. In general, computations using double word data types run more slowly than with 32-bit data types. On processors without native hardware support for double-precision floating-point arithmetic (see the
Processors Supporting
2–64
CrossCore Embedded Studio 2.2.0
Compiler Command-Line Interface
Native Double-Precision Floating-Point Arithmetic table in
), such computation relies largely on software-emulated arithmetic. On processors with native double-precision floating-point support, the instructions take longer to execute than single-precision floating-point instructions.
Type double
poses a special problem. Without some special handling, many programs would inadvertently end up using slow-speed, emulated, 64-bit floating-point arithmetic, even when variables are declared consistently as float
. In order to avoid this problem, Analog Devices provides the
switch, which allows you to set the size of double
to either 32 bits (default) or 64 bits. The 32-bit setting gives good performance and should be acceptable for most DSP programming. However, it does not conform fully to the ISO/IEC 9899:
1990 C standard, the ISO/IEC 9899: 1999 C standard or the ISO/IEC 14882: 2003 C++ standard, which all require that the
-double-size-64
switch be enabled.
For a larger floating-point type, the long double
data type provides 64-bit floating-point arithmetic.
For either size of double
, the standard
#include
files automatically redefine the math library interfaces so that functions such as sin
can be directly called with the proper size operands. Access to 64-bit floating-point arithmetic and libraries is always provided via long double
.
Therefore,
float sinf (float); /* 32-bit */
double sin (double); /* 32 or 64-bit */
Full descriptions of these functions and their implementation can be found in the
C/C++ Library Manual for
SHARC Processors.
Optimization Control
The general aim of compiler optimization is to generate correct code that executes quickly and is small in size. Not all optimizations are suitable for every application or possible all the time. Therefore, the compiler optimizer has a number of configurations, or optimization levels, which can be applied when needed. Each of these levels are enabled by one or more compiler switches or pragmas.
NOTE: Refer to the
Achieving Optimal Performance From C/C++ Source Code chapter for information on how
to obtain maximal code performance from the compiler.
Optimization Levels
The following list identifies several optimization levels. The levels are notionally ordered with least optimization listed first and most optimization listed last. The descriptions for each level outline the optimizations performed by the compiler and identify any switches or pragmas required, or that have direct influence on the optimization levels performed.
• Debug
The compiler produces debug information to ensure that the object code matches the appropriate source code
• Default
CrossCore Embedded Studio 2.2.0
2–65
Optimization Control
The compiler does not perform any optimization by default when none of the compiler optimization switches are used (or enabled in CCES IDE). Default optimization level can be enabled using the optimize_off pragma. See
General Optimization Pragmas .
• Procedural Optimizations
The compiler performs advanced, aggressive optimization on each procedure in the file being compiled. The optimizations can be directed to favor optimizations for speed (
-O1
or
O
) or space (
-Os
) or a factor between speed and space (
-Ov
). If debugging is also requested, the optimization is given priority so the debugging functionality may be limited. See
. Procedural optimizations for speed and space (
-O and
-Os
) can be enabled in C/C++ source using the pragma optimize_{for_speed|for_space}
.
For more information, see General Optimization Pragmas .
• Profile-Guided Optimizations (PGO)
The compiler performs advanced aggressive optimizations using profiler statistics (
.pgo
files) generated from running the application using representative training data. PGO can be used in conjunction with IPA and au-
for more information.
The most common scenario in collecting PGO data is to set up one or more simple file-to-device streams where the file is a standard ASCII stream input file and the device is any stream device supported by the simulator target, such as memory and peripherals. The PGO process can be broken down into the execution of one or more data sets where a data set is the association of zero or more input streams with one and only one
.pgo
output file.
For more information, see Using Profile-Guided Optimization .
NOTE: Be aware of the requirement for allowing command-line arguments in your project when using
PGO. See Support for argv/argc for details.
• Automatic Inlining
The compiler automatically inlines C/C++ functions which are not necessarily declared as inline in the source code. It does this when it has determined that doing so reduces execution time. How aggressively the compiler performs automatic inlining is controlled using the
-Ov
switch. Automatic inlining is enabled using the
-Oa switch which additionally enables procedural optimizations (
-O
for more information.
NOTE: When remarks are enabled, the compiler produces a remark to indicate each function that is inlined.
• Interprocedural Optimizations
The compiler performs advanced, aggressive optimization over the whole program, in addition to the per-file optimizations in procedural optimization. The interprocedural analysis (IPA) is enabled using the
-ipa
switch which additionally enables procedural optimizations (
-O
). See
for more information.
2–66
CrossCore Embedded Studio 2.2.0
Optimization Control
The compiler optimizer attempts to vectorize loops when it is safe to do so. When IPA is used it can identify additional safe candidates for vectorization which might not be classified as safe at a procedural optimization level. Additionally, there may be other loops that are known to be safe candidates for vectorization which can be identified to
the compiler with use of various pragmas. See Loop Optimization Pragmas
for more information.
Using the various compiler optimization levels is an excellent way of improving application performance. However consideration should be given to how applications are written so that compiler optimizations are given the best opportunity to be productive. These issues are the topic of the
Achieving Optimal Performance From C/C++ Source
Interprocedural Analysis
The cc21k
compiler has a capability called interprocedural analysis (IPA), an optimization that allows the compiler to optimize across translation units instead of within just one translation unit. This capability effectively allows the compiler to see all of the source files that are used in a final link at compilation time and make use of that information when optimizing.
Enable interprocedural analysis by selecting the Interprocedural analysis check box on the Compile > General page of the CCES Properties dialog box, or by specifying the
-ipa
command-line switch.
The
-ipa
switch automatically enables the
-O
switch to turn on optimization. See also
.
Use of the
-ipa
switch generates additional files along with the object file produced by the compiler. These files have
.ipa
filename extensions and should not be deleted manually unless the associated object file is also deleted.
All of the
-ipa
optimizations are invoked after the initial link, whereupon a special program called the prelinker reinvokes the compiler to perform the new optimizations, recompiling source files where necessary, to make use of gathered information.
NOTE: Because a file may be recompiled by the prelinker, do not use the
-S
option to see the final optimized assembler file when
-ipa
is enabled. Instead, use the
switch, so that the full compile/link
cycle can be performed first.
Interaction With Libraries
When IPA is enabled, the compiler examines all of the source files to build up usage information about all of the function and data items. It then uses that information to make additional optimizations across all of the source files by recompiling where necessary.
Because IPA operates only during the final link, the
-ipa
switch has no benefit when initially compiling source files to object format for inclusion in a library. IPA gathers information about each file and embeds this within the object format, but cannot make use of it at this point, because the library contents have not yet been used in a specific context.
When IPA is invoked during linking, it will recover the gathered information from all linked-in object files that were built with
-ipa
, and where necessary and possible, will recompile source files to apply additional optimizations.
Modules linked in from a library are not recompiled in this manner, as source is not available for them. Therefore,
CrossCore Embedded Studio 2.2.0
2–67
Compiler Command-Line Interface
the gathered information in a library module can be used to further optimize application sources, but does not provide a benefit to the library module itself.
If a library module makes references to a function in a user module in the program, this will be detected during the initial linking phase, and IPA will not eliminate the function. If the library module was not compiled with
-ipa
,
IPA will not make any assumptions about how the function may be called, so the function may not be optimized as effectively as if all references to it were in source code visible to IPA, or from library modules compiled with
-ipa
.
Controlling Silicon Revision and Anomaly Workarounds Within the Compiler
The compiler provides three switches which specify that code produced by the compiler will be generated for a specific revision of a specific processor, and appropriate silicon revision targeted system run time libraries will be linked against. Targeting a specific processor allows the compiler to produce code that avoids specific hardware errata reported against that revision. For the simplest control, use the
-si-revision
switch which automatically controls compiler workarounds.
NOTE: The compiler cannot apply errata workarounds to code inside asm()
constructs.
When developing using the CCES IDE, the silicon revision used to build sources is part of a projects processor settings.
This section describes:
•
•
•
•
Interactions Between the Silicon Revision and Workaround Switches
•
Using the
-si-revision
Switch
The
-si-revision version
(silicon revision) switch directs the compiler to build for a specific hardware revision. Any errata workarounds available for the targeted silicon revision will be enabled. The parameter
version
represents a silicon revision of the processor specified by the
For example,
cc21k -proc ADSP-21161 -si-revision 0.1 prog.c
If silicon version none
is used, then no errata workarounds are enabled, whereas specifying silicon version any
will enable all errata workarounds for the target processor.
If the
-si-revision
switch is not used, the compiler will build for the latest known silicon revision for the target processor at the time of release, and any errata workarounds which are appropriate for the latest silicon revision will be enabled.
2–68
CrossCore Embedded Studio 2.2.0
Controlling Silicon Revision and Anomaly Workarounds Within the Compiler
In the
SHARC\lib
CCES installation directory there are a number of subdirectories. Within each of these is a complete set of libraries built for specific parts and silicon revisions. When linking an executable, the compiler driver selects and links against the best of these sets of libraries that is correct for the target part and has been built with the necessary silicon anomaly workarounds enabled to match the silicon revision switch. Note that an individual set of libraries may cover more than one specific part or silicon revision, so if several silicon revisions are affected by the same errata, then one common set of libraries might be used.
The
__SILICON_REVISION__
macro is set by the compiler a hexadecimal number representing the major and minor numbers in the silicon revision. For example,
1.0
becomes
0x100
and
10.21
becomes
0xa15
.
If the silicon revision is set to any
, the
__SILICON_REVISION__
macro is set to
0xffff
and if the
-sirevision
switch is set to none
the compiler will not set the
__SILICON_REVISION__
macro.
The compiler driver will pass the
-si-revision
switch, as specified in the command line, when invoking other tools in the CCES toolchain.
NOTE: Visit www.analog.com/processors/tools/anomalies to get more information on specific anomalies, including anomaly IDs.
Using the
-workaround
Switch
The
-workaround workaround_id[, workaround_id…]
switch enables compiler code generator work-
arounds for specific hardware errata.
When workarounds are enabled, the compiler defines the macro
__WORKAROUNDS_ENABLED
at the compile, assembly, and link build stages. The compiler also defines individual macros for each of the enabled workarounds for each of these stages, as indicated by each macro description.
For a complete list of anomaly workarounds and associated workaround_id keywords, refer to the anomaly
.xml
files provided in the
<install_path>\System\ArchDef
directory. These are named in the format
<platform_name>-anomaly.xml
.
To find which workarounds are enabled for each chip and silicon revision, refer to the appropriate <chip_name>
compiler.xml
file in the same directory (for example,
ADSP-21488-compiler.xml
). Each
*-compiler.xml
file references an
*-anomaly.xml
file via the name in the
<cces-anomaly-dictionary>
element.
The anomaly
.xml
files relevant to SHARC processors have filenames of the form
SHARC-21xxx-anomaly.xml
or
SHARC-SCxxx-anomaly.xml
.
Using the
-no-workaround
Switch
The
-no-workaround workaround_id[, workaround_id …]
switch disables compiler code generator
workarounds for specific hardware errata. For a complete list of valid workaroundID
values, refer to the relevant
*-anomaly.xml
file. For more information, see Using the
.
The
-no-workaround
switch can be used to disable workarounds enabled via the
-si-revision version
or
workaround workaroundID
switch.
CrossCore Embedded Studio 2.2.0
2–69
Controlling Silicon Revision and Anomaly Workarounds Within the Compiler
All workarounds can be disabled by providing
-no-workaround
with all valid workarounds for the selected silicon revision or by using the option
-no-workaround all
. Disabling all workarounds via the
-no-workaround switch will link against libraries with no silicon revision in cases where the silicon revision is not none
.
Interactions Between the Silicon Revision and Workaround Switches
The interactions between
-si-revision
,
-workaround
, and
-no-workaround
can only be determined once all the command line arguments have been parsed.
To this effect options will be evaluated as follows:
1. The
-si-revision version
is parsed to determine which revision of the run-time libraries the application will link against. It also produces an initial list of all the default compiler errata workarounds to enable.
2. Any additional workarounds specified with the
-workaround
switch will be added to the errata list.
3. Any workarounds specified with
-no-workaround
will then be removed from this list.
4. If silicon revision is not none
or if any workarounds were declared via
-workaround
, the macro
__WORKAROUNDS_ENABLED
is defined at compile and assembly and link stages, even if
-no-workaround disables all workarounds.
Anomalies in Assembly Sources
If your project includes some hand-written assembly code, you will have to ensure that you explicitly avoid any relevant anomalies that apply to your target processor. This can be simplified by the use of the sys
\anomaly_macros_rtl.h
header file. This header file defines macros for each of the anomalies that affect the run-time libraries, which allow for conditional inclusion of avoidance code.
For example, the following code makes use of the
WA_09000014
macro to conditionally select code that avoids problems with conditional stores in delay slots.
#if WA_09000014
r4 = r4 - r12;
if not ac dm(-3, i3) = r12;
jump(pc, exit_malloc);
#else
jump(pc, exit_malloc) (DB);
r4 = r4 - r12;
if not ac dm(-3, i3) = r12;
#endif
Using Byte-Addressing
The processors in the Processors Supporting Byte-Addressing table (see
Processor Features ) have the ability to access
individual 8-bit bytes in memory. This feature is known as byte-addressing. Each individual 8-bit byte has a unique address, in contrast to earlier SHARC families which, in the C runtime environment, permitted access to a minimum of 32 bits at a time (known as word-addressing). Byte-addressing permits compatibility with code written assuming that the C/C++ char
type is 8 bits in length, and the C/C++ short
type is 16 bits in length.
2–70
CrossCore Embedded Studio 2.2.0
Using Byte-Addressing
This chapter discusses how to use byte-addressing with CrossCore Embedded Studio.
• Instructions on how to build an entirely byte-addressed application are given in
Building Byte-Addressed Applications From C/C++
.
• Differences between data formats and calling conventions for byte-addressed and word-addressed code are giv-
en in Data Formats and Calling Conventions
.
• The use of
#pragma byte_addressed
and
#pragma word_addressed
to facilitate use of functions and
• Finer-grained control of char-size is discussed in
Constructing Complex Byte-Addressed and Word-Addressed
.
• Extra considerations relating to the use of byte-addressed and word-addressed code together in C++ applica-
tions is discussed in Using Byte-Addressed and Word-Addressed Interfaces with C++ .
• Using assembly code in mixed byte-addressed and word-addressed applications is discussed in
.
Building Byte-Addressed Applications From C/C++
CrossCore Embedded Studio supports building applications both using word-addressing, enabling compatibility with existing SHARC applications, and using byte-addressing. Byte-addressing is enabled by the command line switch
-char-size-8
, which is the default setting, while word-addressing is enabled by the command line switch
-char-size-32
. Char-size may also be selected in the CrossCore Embedded Studio IDE through
Properties >
C/C++ Build > Settings > Tool Settings > CrossCore SHARC C/C++ Compiler > Processor > Char size.
Data Formats and Calling Conventions
If you are used to working with word-addressed code on earlier generations of the SHARC processor and are moving to byte-addressed code, there are several differences of you should be aware. These are:
• The sizes of native integer types, discussed in
.
• The endianness of 64-bit scalar types and bitfields, discussed in
• The scheme used for translating C/C++ source names into linkage names, discussed in
• The address space used for accesses to memory, discussed in
.
• Changes to the call-preserved register set, discussed in
Changes to Call-Preserved Registers
.
Sizes of Native Types
Sizes of the native C/C++ types when using the two switches are shown in the
Sizes of C/C++ Native Types With -
char-size-8 and -char-size-32 table.
CrossCore Embedded Studio 2.2.0
2–71
Data Formats and Calling Conventions
Table 2-22: Sizes of C/C++ Native Types With -char-size-8 and -char-size-32
Type
Size in Bits (
-char-size-8
)
char
8 short int long long float double
(*) long double short fract fract long fract pointer (e.g. int *
)
32
32
32
16
32
64
32
32 or 64
64
32
Size in Bits (
-char-size-32
)
32
32 same as
-char-size-8 same as
-char-size-8 same as
-char-size-8 same as
-char-size-8 same as
-char-size-8 same as
-char-size-8 same as
-char-size-8 same as
-char-size-8 same as
-char-size-8
NOTE: The signed and unsigned variants of these types are the same size as the corresponding plain type. The size of the double type depends on use of the switches
-double-size-32
and
-double-size-64
.
Endianness
64-bit values of type long long
, unsigned long long
, long double
, and double
(when the
-doublesize-64
switch is used) are stored in memory in big-endian format when compiling with
-char-size-32
, but are stored in little-endian format when compiling with
-char-size-8
. This is in order to promote easy inter-operation of the SHARC core with other cores and devices that store values in little-endian format. An example of how
64-bit values are laid out in memory is shown in the
Layout of 64-Bit Values in Memory When Building With -
char-size-8 and -char-size-32 table.
Table 2-23: Layout of 64-bit Values in Memory When Building With -char-size-8 and -char-size-32
Data
-char-size-8
Address: Value
-char-size-32
Address: Value
long long x =
0x1122334455667788LL; long double y = 1.0;
0x2c0000: 0x88
0x2c0001: 0x77
0x2c0002: 0x66
0x2c0003: 0x55
0x2c0004: 0x44
0x2c0005: 0x33
0x2c0006: 0x22
0x2c0007: 0x11
0x2c0000: 0x00
0x2c0001: 0x00
0x2c0002: 0x00
0x2c0003: 0x00
0x2c0004: 0x00
0xb0000: 0x11223344
0xb0001: 0x55667788
0xb0000: 0x3ff00000
0xb0001: 0x00000000
2–72
CrossCore Embedded Studio 2.2.0
Data Formats and Calling Conventions
Table 2-23: Layout of 64-bit Values in Memory When Building With -char-size-8 and -char-size-32 (Continued)
Data
-char-size-8
Address: Value
0x2c0005: 0x00
0x2c0006: 0xf0
0x2c0007: 0x3f
-char-size-32
Address: Value
The endianness of bitfields is also affected by the char-size setting. When
-char-size-32
is used, the fields are laid out starting from the most-significant end of each word. When
-char-size-8
is used, the fields are laid out starting from the least-significant end of each word. This is shown in the
Layout of Bitfields in Memory When
Building With -char-size-8 and -char-size-32 table.
Table 2-24: Layout of Bitfields in Memory When Building With -char-size-8 and -char-size-32
Data
-char-size-8
Address: Value
-char-size-32
Address: Value
struct bits { int a:4; int b:8; int c:24;} my_bits = {0x4, 0x8,
0x24};
0x2c0000: 0x84
0x2c0001: 0x00
0x2c0002: 0x00
0x2c0003: 0x00
0x2c0004: 0x24
0x2c0005: 0x00
0x2c0006: 0x00
0x2c0007: 0x00
0xb0000: 0x40800000
0xb0001: 0x00002400
Symbol Names
As the sizes of native types and the pointer representation are different in word-addressed and byte-addressed code, word-addressed code must not used where byte-addressed code is expected or vice versa. This is ensured by using a different linkage name for code compiled with the
-char-size-8
and
-char-size-32
switches. With
-charsize-32
, an underscore is prefixed to the C identifier, while with
-char-size-8
, a period is appended to the C identifier. This is shown in the
Linkage Names Used for C Symbols When Compiled With -char-size-8 and -char-
size-32 table.
Table 2-25: Linkage Names Used for C Symbols When Compiled With -char-size-8 and -char-size-32
C Name
-char-size-8 -char-size-32
my_function my_function.
_my_function my_data my_data.
_my_data
Byte-Addressed Memory Alias
Unlike word-addresses, byte-addresses must be able to represent which byte within a word is being accessed, requiring a distinct byte-addressed address format. Memory on the ADSP-215xx and ADSP-SC5xx processors can be accessed using a byte-addressed address space, which uses this format. Most of the memory may additionally be accessed using a word-addressed alias (the normal-word, or NW, address space) as on the ADSP-211xx/212xx/213xx/
CrossCore Embedded Studio 2.2.0
2–73
Data Formats and Calling Conventions
214xx SHARC processors, as well as the short-word (SW) and long-word (LW) address aliases. See the address map for more information.
In code compiled with
-char-size-8
, all pointers will by default be located in the byte-addressed alias space. This includes the frame and stack pointers i6
and i7
, and the addresses of any data buffers and global variables.
Changes to Call-Preserved Registers
When
-char-size-32
is used, the
MRF
and
MRB
registers are defined as call-preserved, so a function that modifies them is responsible for saving their values on entry and restoring these original values on exit from the function.
When
-char-size-8
is used, the
MRF
and
MRB
registers are defined as scratch registers. This means if they are modified in a function, their values are not restored on exit from the function. Therefore live values should not be kept in the
MRF
and
MRB
registers across a call to a function compiled with
-char-size-8
.
Mixed Char-Size Applications
This section contains:
•
Building a Mixed Char-Size Application
•
How to Use Different Char Sizes Together
•
•
Building a Mixed Char-Size Application
When building an entire application from C/C++ source, the
-char-size-8
and
-char-size-32
switches can be applied to the project as a whole. In this case, the sizes of native types, endianness, and mangling schemes are consistent across the entire application. In new applications, we recommended using the default setting (
-charsize-8
). The default setting offers greatest compatibility with code ported from other platforms and simplifies use of peripherals and inter-core communication.
In some cases, you may wish to reuse existing code written for earlier, word-addressed SHARC processors within an application using
-char-size-8
. Combining such code with the new
-char-size-8
parts of your application requires you to direct the compiler regarding the interface between the different application parts. The interface between the byte-addressed and word-addressed parts allows the compiler to perform any necessary conversions. This
approach is discussed in How to Use Different Char Sizes Together
.
To ensure that each source file in your project is built for the correct sizes of the native types, indicate the char-size setting. Indicate the setting for each source file whose char-size does not match the char-size set at the project options level. First, set the default char-size for the project using the
Project Properties dialog box. Next, for each source file to built using the opposite char-size, set the
Build Properties for that source file. The char
size setting is found under
C/C++ Build > Settings > Tools Settings > CrossCore SHARC C/C++ Compiler > Processor > Char size.
2–74
CrossCore Embedded Studio 2.2.0
Mixed Char-Size Applications
How to Use Different Char Sizes Together
The differences in calling conventions and data layout discussed in
Data Formats and Calling Conventions mean
that the compiler needs to know the char-size setting which was used when compiling the source file where each function or variable is defined.
To do this, declare functions and data defined in source files using the opposite char-size using
#pragma byte_addressed
or
#pragma word_addressed
.
#pragma byte_addressed
is used to mark symbols found in source files built with
-char-size-8
, allowing them to be referenced from code built with either
char-size-8
or
-char-size-32
. Likewise
#pragma word_addressed
is used to mark symbols found in source files built with
-char-size-32
.
These pragmas can be used in two ways. Firstly, the pragma can immediately precede a single function or external data declaration. For example, a source file built with
-char-size-8
might contain:
// function prototype for function built with -char-size-32
#pragma word_addressed extern int my_char_size_32_func(void);
// function prototype for function built with same char-size as current compilation extern int my_char_size_8_func(void);
Alternatively the pragmas can be used with push
and pop
to enclose a region where all symbols and types belong to the opposite char-size. For example, we might declare the following in a translation unit built with
-char-size-8
:
#pragma word_addressed(push)
// function prototype for function built with -char-size-32 extern int my_char_size_32_func(void);
// function prototype for another function built with -char-size-32 extern void another_char_size_32_func(int a);
#pragma word_addressed(pop)
// function prototype for function built with same char-size as
// current compilation int my_char_size_8_func(void);
Regardless of which method is used, we say such symbols are declared in an interface region.
Within an interface region of the opposite char-size, the compiler will interpret the declarations as they would be if compiled with that char-size. The sizes of basic types will be changed as per the
Sizes of C/C++ Native Types With -
char-size-8 and -char-size-32 table (in Sizes of Native Types
). Interface regions also tell the compiler about the
compiler understands that symbols use the alternative linkage name, 64-bit types and bitfields have the opposite endianness, addresses belong to the opposite address alias space, and that the
MRF
and
MRB
registers may not be callpreserved. The compiler is then able to perform type checking and type conversions as necessary when the external functions and data are used.
If no pragma is used, the compiler assumes the declaration uses the char-size being used for the current compilation.
If a function or variable is declared with the wrong char-size, the usual result is that the link step will fail due to unresolved symbols.
CrossCore Embedded Studio 2.2.0
2–75
Mixed Char-Size Applications
Type Coercions
The compiler will identify places where type coercions are necessary to convert between types for different char size regions. Where it is possible to do so, the compiler will automatically perform the necessary type coercions, while in cases where it is not possible to do so, a compile-time error or warning will be reported. For example, if you compile the following code with
-char-size-8
#pragma word_addressed extern long long ll; // big-endian long long long long double_ll(void) {
return 2 * ll; // compiler swaps endianness to convert to
// little-endian long long and multiplies by 2
} the compiler will swap the endianness of the word-addressed long long
, which is stored in memory in big-endian format, to a byte-addressed long long
, which the function multiplies by 2 and returns in registers in little-endian format. Therefore you do not need to perform such type coercions explicitly. Likewise, when built
-char-size-8
,
#pragma word_addressed extern void func(int *); // expects word-addressed pointer void alloc_and_call_func(void) {
int * buffer = malloc(10 * sizeof(int));
func(buffer); // compiler converts parameter to
// word-addressed alias space prior to call
} the compiler will emit code to convert the pointer buffer
into the word-addressed alias space as expected by the callee func
.
Values held in memory are not converted by the compiler. For example, the following code, when compiled with
char-size-8
, results in a compile-time warning, as the type in memory pointed at by buffer
(one or more littleendian long long s) does not match the type in memory expected by the callee (one or more big-endian long long s).
#pragma word_addressed extern void my_func(long long *); // expects word-addressed
// pointer to big-endian long long void call_func(long long *buffer) {
my_func(buffer); // cc0167: {D} warning: argument of type
// "long long *" is incompatible
// with parameter of type
// "word_addressed long long *word_addressed"
}
This and other similar warnings signify that the code will probably not function correctly.
Simple Interfaces
The simplest way to construct an interface between code compiled with
-char-size-8
and code compiled with
char-size-32
is to define a small set of functions and variables which should be accessible to code built with the
2–76
CrossCore Embedded Studio 2.2.0
Using Byte-Addressing
opposite char-size, and prototype these in a header file enclosed in
#pragma word_addressed(push)
and
#pragma word_addressed(pop),
or
#pragma byte_addressed(push)
and
#pragma byte_addressed(pop)
. To allow the compiler to insert all the necessary type conversions, within an interface region try to:
• Avoid use of pointers to types which differ between
-char-size-8
and
-char-size-32
. Specifically, avoid pointers to char s, short s, long long s and long double s, and also avoid pointers to other pointers.
• Avoid pointers to bitfields.
• Avoid use of scalar types whose size is less than 32 bits. Specifically, do not use char
, short
or bool
types within a
#pragma byte_addressed
interface region.
• Avoid use of pointers to functions.
Interfaces adhering to the above constraints can be constructed and used simply by enclosing the extern declarations in the word_addressed
or byte_addressed
pragmas. Constructing interfaces where the above constraints do not hold may require more advanced techniques. For more information, see
Constructing Complex Byte-Addressed and Word-Addressed Interfaces
.
Constructing Complex Byte-Addressed and Word-Addressed Interfaces
discussed the construction of simple interfaces between code compiled with
-charsize-8
and code compiled with
-char-size-32
. This section deals with construction of more advanced interfaces, which may contain pointers to types whose representation differs between
-char-size-8
and
-charsize-32
compilations.
To construct these interfaces, it is useful to understand
• Use of the keywords to explicitly specify char-size, discussed in
The byte_addressed and word_addressed Keywords .
• Constructs that are prohibited in interface regions, discussed in
Prohibited Constructs in Interface Regions
.
• The constraints on using sub-word types in word-addressed code, discussed in
• The behavior of typedefs in interface regions, discussed in
• How to use function pointers, discussed in
.
The byte_addressed and word_addressed Keywords
The byte_addressed
and word_addressed
keywords (which may be optional prefixed by two underscores:
__byte_addressed
and
__word_addressed
) may be used to explicitly specify char-size, memory space and endianness outside of interface regions. The byte_addressed
keyword specifies that the type has the representation used in a compilation using
-char-size-8
. The word_addressed
keyword specifies that the type has the representation used in a compilation using
-char-size-32
. The qualifier may be used on any scalar type, but only has an effect on types whose representation differs between
-char-size-8
and
-char-size-32
compilations. The
CrossCore Embedded Studio 2.2.0
2–77
Constructing Complex Byte-Addressed and Word-Addressed Interfaces
qualified types are shown in the
Types Explicitly Qualified With the byte_addressed and word_addressed Qualifiers
table.
Table 2-26: Types Explicitly Qualified With the byte_addressed and word_addressed Qualifiers
Type Size in Bits - Compiled Either
size-8
or
-char-size-32
-char-
Notes
byte_addressed char word_addressed char byte_addressed short word_addressed short byte_addressed int word_addressed int byte_addressed long long word_addressed long long byte_addressed float word_addressed float byte_addressed double word_addressed double byte_addressed long double word_addressed long double byte_addressed short fract word_addressed short fract byte_addressed fract word_addressed fract byte_addressed long fract word_addressed long fract byte_addressed
pointer (e.g. int * byte__addressed
) word_addressed
pointer (e.g. int
*word_addressed
64
32
32
32
32
32
32
32
8
32
16
32
32
32
64
64
32
32
32 or 64
32 or 64
64
32 byte_addressed word_addressed little-endian big-endian
has no effect on type
has no effect on type byte_addressed
has no effect on type word_addressed
has no effect on type little-endian if 64-bit big-endian if 64-bit little-endian big-endian byte_addressed
has no effect on type word_addressed
has no effect on type byte_addressed
has no effect on type word_addressed
has no effect on type byte_addressed
has no effect on type word_addressed
has no effect on type address in byte-addressed memory space address in word-addressed memory space
NOTE: The signed
and unsigned
variants of the integer types are the same sizes as the equivalent plain types.
Note than byte_addressed
and word_addressed
have no effect on the int
, float
, and fract types. The size of the double
type depends on use of the switches
-double-size-32
and
-doublesize-64
.
Within interface regions, explicit qualification overrides the implicit qualification resulting from the interface region.
So, for example, if a function my_func
is declared with:
2–78
CrossCore Embedded Studio 2.2.0
Constructing Complex Byte-Addressed and Word-Addressed Interfaces
#pragma word_addressed int * byte_addressed my_func(void);
It is expected to return an address in the byte-addressed memory space.
The byte_addressed
and word_addressed
qualifiers allow you to use types pertaining to the opposite charsize within your code. This can help you to perform any explicit conversions necessary in order to use functions and data within an interface region. For example, consider a simple function, compiled with
-char-size-32
, that returns a long long
result by storing it in a memory location passed from the caller: void my_func(long long *p) {
*p = 0x12345678abcdefLL;
}
This could be used from byte-addressed code by using the word_addressed
qualifier:
#pragma word_addressed void my_func(long long *p); void my_func_wrapper(long long *p) {
word_addressed long long ll;
my_func(&ll);
*p = ll;
}
Prohibited Constructs in Interface Regions
Interface regions are used to declare functions and data that are defined outside of the current source file, in translation units built with the opposite char-size. Consequently, there are a number of constraints on the constructs that can appear in an interface region:
• The compiler will produce an error if function definitions (i.e. function declarations which include their source implementation) or data definitions (i.e. data declarations that result in storage being allocated within the current translation unit) are found in an interface region.
• Word-addressed code cannot define or access values of less than 32 bits in size. A word_addressed
interface therefore cannot contain variables of type less than 32 bits in size, or of struct
, union
or class
types containing fields of less than 32 bits in size. For example, the compiler issues an error for the following declaration, as it declares a word-addressed variable less than 32 bits in size:
#pragma word_addressed extern byte_addressed char c;
However the following is a legal declaration, as it declares a pointer which is 32 bits in size:
#pragma word_addressed extern byte_addressed char *p;
Likewise, functions in word-addressed regions cannot generally take parameters or return values of these types.
A special exception is made to allow returning a value of type byte_addressedchar
, bool
or short
, which is returned sign- or zero-extended in the return register
R0
.
CrossCore Embedded Studio 2.2.0
2–79
Constructing Complex Byte-Addressed and Word-Addressed Interfaces
There are no constraints on types that may be used in byte_addressed
interface regions.
Using Sub-Word Types in Word-Addressed Code
Types of less than 32 bits in size, or struct
, union
or class
types containing fields of less than 32 bits in size, cannot be used directly in code compiled with
-char-size-32
. The compiler will produce an error when compiling the following two code examples with
-char-size-32
:
#pragma byte_addressed extern char c; char fn(void) {
return c; // error: cannot access 8-bit values in word_addressed code
} and byte_addressed short fn2(int x) {
return (byte_addressed short)x; // error: cannot create 16-bit values
// in word_addressed code
}
It is also illegal to perform pointer arithmetic on word-addressed pointers to types of size less than 32 bits, and struct s, union s and class es containing these types, as a word-addressed pointer may not be able to represent the address. So the following is reported as an error by the compiler, when building with
-char-size-32
: byte_addressed char *func(byte_addressed char *p) {
return p+5; // error: can't increment word_addressed pointer by 5 bytes
} while the following code produces an error when compiling with
-char-size-8
: char *word_addressed func(char *word_addressed p) {
return p+5; // error: can't increment word_addressed pointer by 5 bytes
}
Finally, when compiling with
-char-size-32
, the compiler also reports an error when performing pointer arithmetic on byte-addressed pointers to types of size less than 32 bits, and struct s, union s and class es containing these types, as the compiler does not represent the size of these types, which may not be integral numbers of words: byte_addressed char *byte_addressed func(byte_addressed char *byte_addressed p) {
return p+5; // error: incrementing by 5 bytes when building with -char-size-32
}
These restrictions do not affect what declarations may appear in an interface region itself, but only what may be used within code using the interface definition.
Use of typedefs
typedef s can be used to declare simpler names for types of the opposite char-size. For example, you could declare
typedef byte_addressed char char8_t;
typedef word_addressed char char32_t;
2–80
CrossCore Embedded Studio 2.2.0
Constructing Complex Byte-Addressed and Word-Addressed Interfaces
When typedef s are used within interface regions, they retain the char-size of the interface region. So an equivalent way to define these types is to write
#pragma byte_addressed(push)
typedef char char8_t;
#pragma byte_addressed(pop)
#pragma word_addressed(push)
typedef char char32_t;
#pragma word_addressed(pop)
However a typedef
with no explicit char-size (i.e. not defined within an interface region, and without a byte_addressed
or word_addressed
qualifier), when used within interface regions will take the prevailing char-size in the interface region: typedef char char_t;
#pragma byte_addressed extern char_t c1; // 8-bit char
#pragma word_addressed extern char_t c2; // 32-bit char
Function Pointers
As discussed in
Data Formats and Calling Conventions , functions built with
-char-size-8
and
-charsize-32
obey distinct calling conventions. Therefore calls through function pointers to functions built for the opposite char-size must use a function pointer type that specifies this. The easiest way to do this is to declare the function pointer type within the interface. For example
#pragma word_addressed(push) extern void fn(int *); typedef void (*wa_fn_ptr_t)(int);
#pragma word_addressed(pop) wa_fn_ptr_t my_fn_pointer = fn; // ok - function pointer type
// and function used as initializer
// are both word-addressed
Alternatively, the word_addressed
and byte_addressed
keywords can be used to explicitly state the kind of function pointed at. In the following example, the use of word_addressed
in the function pointer declaration declares the function pointed at as being in word-addressed code:
#pragma word_addressed extern void fn(int *p); void (word_addressed *my_fn_pointer)(int) = fn;
Using Byte-Addressed and Word-Addressed Interfaces with C++
Interface regions can be used in both C and C++ code. A number of C++ features, such as overloading, function templatization, exceptions, and run-time type identification, depend on the types of variables and function parameters. The way the word_addressed
and byte_addressed
type qualifiers are treated has an effect on these features.
CrossCore Embedded Studio 2.2.0
2–81
Using Byte-Addressing
Functions can be overloaded using the byte_addressed
and word_addressed
qualifiers, so long as the qualified type differs from the unqualified one. For example, you can create two versions of a function, one with a byte_addressed long long
argument and another with a word_addressed long long argument
, but you cannot do the same overloading using byte_addressed int
and word_addressed int
, as these two latter types are equivalent. Which types are affected by the byte_addressed
and word_addressed
qualifiers is
discussed in Data Formats and Calling Conventions
. Where an exact overloaded function match cannot be found, the compiler will call the closest match which may involve a conversion of parameters from byte_addressed
to word_addressed
types or vice versa.
Similarly, for template functions, use of template parameters that differ by the word_addressed
or byte_addressed
qualifiers will instantiate distinct copies of the template function, so long as the word_addressed
and byte_addressed
types differ.
A catch block will catch objects based on the bare type. For example, catch (long long a)
will catch a thrown object of either word_addressed long long
or byte_addressed long long
. Therefore it is necessary to be careful to only throw objects of the address space expected by the catch
block.
Run-time type identification also ignores the address-space qualifier. So byte_addressed long long
and word_addressed long long
will be identified as the same type.
Assembly Code
Assembly code can be called from C/C++ code if it is written to obey the calling conventions used in compiled code.
Differences in the calling conventions between code compiled with
-char-size-8
and
-char-size-32
are dis-
cussed in Data Formats and Calling Conventions .
Use of assembly code in applications with byte-addressing obeys many of the same constraints as C/C++ code. These are discussed in
.
This section additionally covers ways in which assembly code written for word-addressed applications may be used in byte-addressed applications with minimum overhead.
• The differences between the operation of the byte-addressed address alias and the SW, NW and LW address
alias spaces is discussed in Memory Alias Spaces Compared
.
• Declaring assembly functions for use in byte-addressed applications is discussed in
Using Word-Addressed Assembly Code Within a Byte-Addressed Application .
• Using
#pragma no_stack_translation
to avoid converting the frame and stack pointers at the call-site is
discussed in #pragma no_stack_translation .
• Writing char-size agnostic assembly code, which can be called directly from both byte-addressed and word-ad-
dressed parts of your application, is discussed in Char-Size Agnostic Assembly Code
.
• Using byte-addressed data from assembly code is discussed in
Defining Byte-Addressed Data in Assembly .
• Using
.IMPORT
in assembly files written for byte-addressing is discussed in
Byte-Addressed C structs in Assembly .
2–82
CrossCore Embedded Studio 2.2.0
Assembly Code
Memory Alias Spaces Compared
The byte-addressed alias space behaves differently from the SW and LW address aliases. The latter may be used with standard load and store instructions, and the alias space of the address determines the size of the data accessed in memory. By contrast, use of the byte-addressed alias does not change the size of data accessed. Instead, access size is specified by the instruction used. Therefore 32 bits of data can be loaded from memory using either a word-address or its equivalent byte-address. In addition, individual 8-bit bytes, or 16-bit shorts, can be accessed from a byte-address by using a sub-word load or store instruction. The
Behavior of Load Instructions for Different Memory Spaces
of Index Register table shows this distinction, where each instruction accesses the same underlying physical memory location.
Table 2-27: Behavior of Load Instructions for Different Memory Spaces of Index Register
Instruction
i5
in NW Space
i5
in LW Space
i5
in SW Space
r0 = dm(m4, i5)
(lw) r0 = dm(m4, i5) r0 = dm(m4, i5)
(sw) r0 = dm(m4, i5)
(swse) r0 = dm(m4, i5)
(bw) r0 = dm(m4, i5)
(bwse)
i5 in BW Space
Load 64 bits from address into registers r0
and r1
.
Offset m4
in words.
Load 64 bits from address into registers r0
and r1
.
Offset m4
in words.
Load 32 bits from address into register r0
. Offset m4 in words.
Load 64 bits from address into registers r0
and r1
.
Offset m4
in words.
Illegal
Illegal
Illegal
Illegal
Illegal
Illegal
Illegal
Illegal
Load 16 bits from address into register r0
, sign-extended to 32 bits if the
SSE
bit in
MODE1
is set.
Offset m4
in short words.
Load 16 bits from address into register r0
, sign-extended to 32 bits if the
SSE
bit in
MODE1
is set.
Offset m4
in short words.
Illegal
Illegal
Illegal
Illegal
Load 64 bits from address into registers r0
and r1
.
Offset m4
in words.
Load 32 bits from address into register r0
. Offset m4 in words.
Load 16 bits from address into register r0
, zero-extended to 32 bits. Offset m4
in short words.
Load 16 bits from address into register r0
, sign-extended to 32 bits. Offset m4
in short words.
Load 8 bits from address into register r0
, zero-extended to 32 bits. Offset m4
in bytes.
Load 8 bits from address into register r0
, sign-extended to 32 bits. Offset m4
in bytes.
Using Word-Addressed Assembly Code Within a Byte-Addressed Application
The rules for using word-addressed assembly code within a byte-addressed application are the same as for use of word-addressed C/C++ code. The recommended way is to leave your assembly code unchanged, and declare the function within a word-addressed interface region. For example
CrossCore Embedded Studio 2.2.0
2–83
Using Word-Addressed Assembly Code Within a Byte-Addressed Application
#pragma word_addressed int my_word_addressed_assembly_function(int a, int b);
Correct prototyping will ensure the compiler performs the necessary type-checking and conversions when calling the function from C/C++ code.
#pragma no_stack_translation
One overhead of calling word-addressed code from byte-addressed code (or vice versa) is the need to transform the frame and stack pointer into the opposite address space at the call site. Typical code produced by the compiler will perform
i7=b2w(i7);
b7=b2w(b7);
b6=b2w(b6);
cjump _f (db); // call to word-addressed function
dm(i7,m7)=r2;
dm(i7,m7)=.LCJ0-1;
.LCJ0:
b6=w2b(b6);
b7=w2b(b7);
i7=w2b(i7);
If your callee function is written in assembly code, you may be able to prove that it does not depend on the frame and stack pointers being in a particular address space. For this to be the case, all memory access to the stack must be to words or long words (using the
LW
qualifier), and all arithmetic on the frame and stack pointers must be achieved with the
(NW)
variant of the modify
instruction. If this is true, you can apply the no_stack_translation pragma to your function declaration. The pragma tells the compiler to suppress the translation of the frame and stack pointers to the callee's address space before the call, and the translation back to the native address space after the call.
Char-Size Agnostic Assembly Code
A step further is to write assembly code that can be called directly from either word-addressed or byte-addressed code without the need to declare it in an interface region. The requirements for such a function are:
• all accesses to memory must access words or long words (using the
LW
qualifier)
• all pointer arithmetic must be performed using the
(NW)
variant of the modify
instruction
• the code uses no bitfields or 64-bit floating-point or integer types
• the
MRF
and
MRB
registers are call-preserved
If your assembly code obeys these constraints, it can be called from either word-addressed or byte-addressed code without any frame pointer, stack pointer or operand conversions at the call site. In this case, you can add an additional entry point to the assembly function using the byte-addressed symbol name, and declare your function outside of any interface region.
_func: // word-addressed entry point
2–84
CrossCore Embedded Studio 2.2.0
Assembly Code
func.: // byte-addressed entry point
// function implementation here
._func.end:
.func..end:
Defining Byte-Addressed Data in Assembly
In assembly, byte-addressed data sections are introduced by supplying the
BW
type specifier to the
.SECTION
directive. Alternatively, the type specifier can just be omitted.
In byte-addressed sections, the directives shown in the Data Declaration Directives for Byte-Addressed Data table are supported for defining and initializing data items. The syntax of these directives is the same as for the
.VAR
directive.
Table 2-28: Data Declaration Directives for Byte-Addressed Data
.BYTE
.BYTE2
.BYTE4
.BYTE8
Single-byte items
Two-byte (short word) items
Four-byte (normal word) items
Eight-byte (long word) item
The
.VAR
directive itself behaves the same as the
.BYTE4
directive when used in a byte-addressed data section.
Floating-point initializer values are encoded in single precision format when appearing in a
.BYTE4
directive, and in double precision format when appearing in a
.BYTE8
directive. Floating-point initializer values are not allowed in
.BYTE
and
.BYTE2
directives.
ASCII strings in initializer values are represented as one item per character, meaning one byte per character in
.BYTE
directives, two bytes per character in
.BYTE2
directives, and so on.
Initializer values in
.BYTE2
,
.BYTE4
and
.BYTE8
directives are written in little-endian byte order. Instructions cannot be placed into byte-addressed sections.
An example of use of the data declaration directives is below:
.section/bw data1;
.byte byte = 0x12;
.byte2 short = 0x1234;
.byte4 word = 0x12345678;
.byte4 single = 1.0;
.byte8 double = 2.0;
.byte string[] = 'Hello', 0;
Byte-Addressed C structs in Assembly
The assembler has the ability to import struct type definitions from C headers and instantiate and access such structs from assembly code. To this end, the assembler supports the same
-char-size-8
and
-char-size-32
switches as the compiler, whereby the default is
-char-size-8
for SHARC+ processors. These determine how the types in
CrossCore Embedded Studio 2.2.0
2–85
Using Native Fixed-Point Types
C headers are interpreted. They also determine where
.STRUCT
directives can be used to instantiate struct types: with
-char-size-8
, they can only be used in byte-addressed (
BW
) sections, whereas with
-char-size-32
, they can only be used in word-addressed (
DM
) sections.
Finally, the char-size switches determine how struct sizes and field offsets are calculated by the
SIZEOF
and
OFFSETOF
operators, as well as in field accesses. This means that these should only be used in connection with pointers to the appropriate memory space, i.e. the byte-addressed space when using
-char-size-8
and the wordaddressed space when using
-char-size-32
.
Using Native Fixed-Point Types
This section provides an overview of the compiler's support for the native fixed-point type fract
, defined in Chapter 4 of the Extensions to support embedded processors ISO/IEC draft technical report, Technical Report 18037.
Fixed-Point Type Support
A fixed-point data type is one where the radix point is at a fixed position. This includes the integer types (the radix point is immediately to the right of the least-significant bit). However, this section uses the term to apply exclusively to those that have a non-zero number of fractional bits - that is, bits to the right of the radix point.
The SHARC processor has hardware support for arithmetic on 32-bit fixed-point data types. For example, it is able to perform addition, subtraction and multiplication on 32-bit fractional values. However, the C language does not make it easy to express the semantics of the arithmetic that maps to the underlying hardware support.
To make it easier to use this hardware capability, and to facilitate expression of DSP algorithms that manipulate fixed-point data, the compiler supports a number of native fixed-point types whose arithmetic obeys the fixed-point semantics. This makes it easy to write high-performance algorithms that manipulate fixed-point data, without having to resort to compiler built-ins, or inline assembly.
An emerging standard for such fixed-point types is set out in Chapter 4 of the Extensions to support embedded process-
ors ISO/IEC Technical Report 18037. CCES provides much of the functionality specified in that chapter, and the chapter is a useful reference that explains the subtleties of the semantics of the library functions and arithmetic operators. The following sections give an overview of these data types, the semantics of arithmetic using these types, and guidelines for how to write high-performance code using these types.
Native Fixed-Point Types
The keyword
_Fract
is used to declare variables of fixed-point type. The
_Accum
keyword, defined in the
ISO/IEC Technical Report to specify fixed-point data types with an integer as well as a fractional part is not currently supported by CCES for SHARC processors. The
_Fract
keyword may also be used in conjunction with the type specifiers short
and long
, and signed
and unsigned
. There are therefore 6 fixed-point types available, although many of these are aliases for types of the same size and format.
By including the header file stdfix.h
, the more convenient alternative spelling fract
may be used instead of
_Fract
. This header file also provides prototypes for many useful functions and it is highly recommended that you
2–86
CrossCore Embedded Studio 2.2.0
Using Native Fixed-Point Types
include it in source files that use fixed-point types. Therefore, the discussion that follows uses the spelling fract
, as does the rest of the CCES documentation.
The formats of the fixed-point types are given in the
Data Storage Formats, Ranges, and Sizes of the Native Fixed-
Point Types table. In the Representation column of the table, the number after the point indicates the number of fractional bits, while the number before the point refers to the number of integer bits, including a sign bit when it is preceded by "s". Signed types are in two's complement form. The range of values that can be represented is also given in the table. Note that the bottom of the range can be represented exactly, whereas the top of the range cannot
- only the value one bit less than this limit can be represented. The value returned by the sizeof
operator depends on whether the
-char-size-8
or
-char-size-32
switch is being used.
Table 2-29: Data Storage Formats, Ranges, and Sizes of the Native Fixed-Point Types
Type Representation Range
short fract s1.31
[-1.0,1.0) fract long fract unsigned short fract unsigned fract unsigned long fract s1.31
s1.31
0.32
0.32
0.32
[-1.0,1.0)
[-1.0,1.0)
[0.0,1.0)
[0.0,1.0)
[0.0,1.0)
sizeof Returns
1 (
-char-size-32
) or 4 (
char-size-8
)
1 (
-char-size-32
) or 4 (
char-size-8
)
1 (
-char-size-32
) or 4 (
char-size-8
)
1 (
-char-size-32
) or 4 (
char-size-8
)
1 (
-char-size-32
) or 4 (
char-size-8
)
1 (
-char-size-32
) or 4 (
char-size-8
)
The Technical Report also defines a
_Sat
(alternative spelling sat
) type qualifier for the fixed-point types. This stipulates that all arithmetic on fixed-point types shall be saturating arithmetic (that is, that the result of arithmetic that overflows the maximum or minimum value that can be represented by the type shall saturate at the largest or smallest representable value). When the sat
qualifier is not used, the standard says that arithmetic that overflows may behave in an undefined manner. CCES accepts the sat
qualifier for compatibility but will always produce code that saturates on overflow whether the sat
qualifier is used or not. This gives maximum reproducibility of results and permits code to be written without worrying about obtaining unexpected results on overflow.
Native Fixed-Point Constants
Fixed-point constants may be specified in the same format as for floating-point constants, inclusive of any decimal or binary exponent. For more information on these formats, refer to
strtofxfx . Suffixes are used to identify the type
of constants. The stdfix.h
header also declares macros for the maximum and minimum values of the fixed-point types. See the
Fixed-Point Type Constant Suffixes and Macros table for details of the suffixes and maximum and minimum fixed-point values.
CrossCore Embedded Studio 2.2.0
2–87
Using Native Fixed-Point Types
Table 2-30: Fixed-Point Type Constant Suffixes and Macros
Type Suffix Example
short fract hr 0.5hr
fract long fract unsigned short fract r lr uhr
0.5r
0.5lr
0.5uhr
unsigned fract unsigned long fract ur ulr
0.5ur
0.5ulr
Minimum Value
SFRACT_MIN
FRACT_MIN
LFRACT_MIN
0.0uhr
0.0ur
0.0ulr
Maximum Value
SFRACT_MAX
FRACT_MAX
LFRACT_MAX
USFRACT_MAX
UFRACT_MAX
ULFRACT_MAX
A Motivating Example
Consider a very simple example-pairwise addition of two sets of fractional values, saturating at the largest or smallest fractional value if the addition overflows. Assume that the data consist of vectors of 32-bit values, representing values in the range [-1.0,1.0). Then it is natural to write:
Example
#include <stdfix.h> void pairwise_add(fract *out, const fract *a, const fract *b, int n)
{
int i;
for (i = 0; i < n; i++)
out[i] = a[i] + b[i];
}
The above code shows that it is easy to express algorithms that manipulate fixed-point data and perform saturation on overflow without needing to find special ways to express these semantics through integer arithmetic.
Fixed-Point Arithmetic Semantics
The semantics of fixed-point arithmetic according to the Technical Report are as follows:
1. If a binary operator has one floating-point operand, the other operand is converted to floating-point and the operator is applied to two floating-point operands to give a floating-point result.
2. If the operator has two fixed-point operands of different signedness, convert the unsigned one to signed with-
out changing its size. (However, see also FX_CONTRACT
.)
3. Deduce the result type. The result type is the operand type of highest rank. Rank increases in the following order: short fract
, fract
, long fract
(or their unsigned equivalents). An operator with only one fixedpoint operand produces a result of this fixed-point type. (An exception is the result of a comparison, which gives a boolean result.)
2–88
CrossCore Embedded Studio 2.2.0
Using Native Fixed-Point Types
4. The result is the mathematical result of applying the operator to the operand values, converted to the result type deduced in step 3. In other words, the result is as if it was computed to infinite precision before converting this result to the final result type.
The conversions between different types are discussed in
.
Data Type Conversions and Fixed-Point Types
The rules for conversion to and from fixed-point types are as follows:
1. When converting to a fixed-point type, if the value of the operand can be represented by the fixed-point type, the result is this value. If the operand value is out of range of the fixed-point type, the result is the closest fixedpoint value to the operand value. In other words, conversion to fixed-point saturates the operand's mathematical value to the fixed-point type's range. If the operand value is within the range of the fixed-point type, but cannot be represented exactly, the result is the closest value either higher or lower than the operand value. For
more information, see Rounding Behavior
.
2. When converting to an integer type from a fixed-point type, the result is the integer part of the fixed-point type. The fractional part is discarded, so rounding is towards zero; both
(int)(0.9r)
and
(int)(-0.9r) give
0
.
3. When converting to a floating-point type, the result is the closest floating-point value to the operand value.
These rules have some important consequences of which you should be aware:
CAU-
TION:
Conversion of an integer to a fractional type is only useful when the integer is -1, 0, or 1. Any other integer value will be saturated to the fractional type. So a statement: fract f = 0x40000000; // try to assign 0.5 to f does not assign
0.5
to f
, but results in
FRACT_MAX
, because
0x40000000
is an integer greater than 1.
Instead, use: fract f = 0.5r;
- or - fract f = 0x40000000p-31r;
Note that the second format above uses the binary exponent syntax available for fixed-point constants; specifically the value
0x40000000
is scaled by 2
-31
.
CAU-
TION:
Assignment of a fractional value to an integer yields zero unless the fractional value is ment of an unsigned fractional value to an integer always results in zero.
-1.0
. Assign-
Compiler warnings will be produced to aid in the diagnosis of problems where these conversions are likely to produce unexpected results.
Bit-Pattern Conversion Functions: bitsfx and fxbits
The stdfix.h
header file provides functions to convert a bit pattern to a fixed-point type and vice versa. These functions are particularly useful for converting between native types ( fract
, unsigned fract
) and integer bitpatterns representing these values.
CrossCore Embedded Studio 2.2.0
2–89
Using Native Fixed-Point Types
For each fixed-point type, a corresponding integer type is declared, which is big enough to hold the bit pattern for the fixed-point type. These are int_fx_t
, where fx is one of hr, r, or lr, and uint_fx_t
where fx is one of uhr, ur, or ulr.
To convert a fixed-point type to a bit pattern, use the bitsfx
family of functions. fx may be any of hr
, r
, lr
, uhr
, ur
, or ulr
. For example, using the prototype uint_ur_t bitsur(unsigned fract); you can write
#include <stdfix.h> unsigned fract f; uint_ur_t f_bit_pattern; void foo(void) {
f = 0.5ur;
f_bit_pattern = bitsur(f); // gives 0x80000000
}
Similarly, to convert to a fixed-point type from a bit pattern, use the fxbits
family of functions. So, to convert from a int_lr_t
to a long fract
, use:
#include <stdfix.h>
#include <fract.h> int_lr_t f32; long fract lf; void foo(void) {
f32 = 0x40000000; // that's 0.5
lf = lrbits(f32); // gets 0.5lr as expected
}
For more information, see
Arithmetic Operators for Fixed-Point Types
You can use the
+
,
-
,
*
, and
/
operators on fixed-point types, which have the same meaning as their integer or
is discussed in
.
You can use
<<
to shift a fixed-point value up by a positive integer shift amount less than the fixed-point type size in bits. This gives the same result as multiplication by a power of 2, including overflow semantics:
#include <stdfix.h> fract f1, f2; void foo1(void) {
f1 = 0.125r;
f2 = f1 << 2; // gives 0.5r
2–90
CrossCore Embedded Studio 2.2.0
Using Native Fixed-Point Types
} void foo2(void) {
f1 = -0.125r;
f2 = f1 << 10; // gives -1.0r
}
You can also use
>>
to shift a fixed-point value down by an integer shift amount in the same range. This is defined to give the same result as division by a power of 2, including any rounding behavior:
#include <stdfix.h> fract f1, f2; void foo1(void) {
f1 = 0.5r;
f2 = f1 >> 2; // gives 0.125r
} void foo2(void) {
f1 = 0x00000003p-31r;
f2 = f1 >> 2; // gives 0x00000000p-31r when rounding
// mode is truncation
// and 0x00000001p-31r when rounding
// mode is biased or unbiased
}
Any of these operators can be used in conjunction with assignment, for example:
#include <stdfix.h> fract f1, f2; void foo1(void) {
f1 = 0.2r;
f2 = 0.3r;
f2 += f1;
}
In addition, there are a number of unary operators that may be used with fixed-point types. These are:
•
++
Equivalent to adding integer 1
•
--
Equivalent to subtracting integer 1
•
+
Unary plus, equivalent to adding value to 0.0 (no effect)
•
-
Unary negate, equivalent to subtracting value from 0.0
•
!
1 if equal to 0.0; 0 otherwise
FX_CONTRACT
Consider the example of a multiplying a signed fract
by an unsigned one:
CrossCore Embedded Studio 2.2.0
2–91
Using Native Fixed-Point Types
fract f; unsigned fract uf; f = f * uf;
According to the rules of fixed-point arithmetic, then since the two fract
operands differ in signedness, the unsigned one is first converted to signed fract
, and subsequently the two s1.31 operands are multiplied together to yield an s1.31 result. So the rules say that it should be equivalent to writing: fract tmp = uf; f = f * tmp;
However, this means that one bit of precision is lost from the unsigned operand before the multiplication. SHARC processors, however, have hardware support for multiplying two fractional operands of opposite signedness together directly, which involve no loss in precision. Use of this support is both more precise and more efficient.
For convenience, the compiler can do this step for you, using a mode known as
FX_CONTRACT
. The name
FX_CONTRACT
is used as the behavior is similar to that of
FP_CONTRACT
in C99. When
FX_CONTRACT
is on, the compiler may keep intermediate results in greater precision than that specified by the Technical Report. In other words, it may choose not to round away extra bits of precision or to saturate an intermediate result unnecessarily.
More precisely, the compiler keeps the intermediate result in greater precision when:
• Maintaining the higher-precision intermediate result will be more efficient-it maps better to the underlying hardware.
• The intermediate result is not stored back to any named variable.
• No explicit casts convert the type of the intermediate result.
In other words,
f = f * uf; will result in an instruction that multiplies the signed and unsigned operands together directly, but
f = f * (fract)uf;
- or -
fract tmp = uf;
f = f * tmp; will both force the unsigned operand to be converted to fract
type before the multiplication.
By default, the compiler permits
FX_CONTRACT
behavior. The
FX_CONTRACT
mode can be controlled with a prag-
ma (see also #pragma FX_CONTRACT {ON|OFF} ) or with command-line switches,
. The pragma can be used at file scope or within functions. It obeys the same scope rules as #pragma
FX_ROUNDING_MODE {TRUNCATION|BIASED|UNBIASED} discussed in the
example.
2–92
CrossCore Embedded Studio 2.2.0
Using Native Fixed-Point Types
Rounding Behavior
Some fixed-point operations are also affected by rounding. For example, multiplication of two fractional values to produce a fractional result of the same size requires discarding a number of bits of the exact result. For example, s1.31 * s1.31
produces an exact s2.62
result. This is saturated to s1.62
and the thirty-one least-significant bits must be discarded to produce an s1.31
result.
By default, any bits that must be discarded are truncated-in other words, they are simply chopped off the end of the value. For example:
#include <stdfix.h> fract f1, f2, prod; void foo(void) {
f1 = 0x3ffffffp-31r;
f2 = 0x10000000p-31r;
prod = f1 * f2; // gives 0x007fffffp-31r, discarding
// least-significant bits 0xe0000000
}
This is equivalent to always rounding down toward negative infinity. It tends to produce results whose accuracy tends to deteriorate as any rounding errors are generally in the same direction and are compounded as the calculations proceed.
If this does not give you the accuracy you require, you can use either biased or unbiased round-to-nearest rounding.
The compiler supports pragmas and switches to control the rounding mode. In the biased or unbiased rounding modes, the above product will be rounded to the nearest value that can be represented by the result type, so the final result will be
0x00800000p-31r
.
The difference between biased and unbiased rounding occurs when the value to be rounded lies exactly half-way between the two closest values that can be represented by the result type. In this case, biased rounding will always round toward the greater of the two values (applying saturation if this rounding overflows) whereas unbiased rounding will round toward the value whose least-significant bit is zero. For example:
#include <stdfix.h> fract f1, f2, prod; void foo1(void) {
f1 = 0x00008000p-31r;
f2 = 0x34568000p-31r;
prod = f1 * f2; // gives 0x3456p-31r in unbiased rounding
// mode, but 0x3457p-31r in biased rounding
// mode
} void foo2(void) {
f1 = 0x00008000p-31r;
f2 = 0x34578000p-31r;
prod = f1 * f2; // gives 0x3458p-31r in both unbiased
CrossCore Embedded Studio 2.2.0
2–93
Using Native Fixed-Point Types
// and biased rounding modes
}
In general, unbiased rounding is more costly than biased rounding in terms of cycles, but yields a more accurate result since rounding errors in the half-way case are not all in the same direction and therefore are not compounded so strongly in the final result.
The rounding discussed here only affects operations that yield a fixed-point result. Operations that yield an integer result round toward zero. There are also a few exceptions to the rounding rules:
• Conversion of a floating-point value to a fixed-point value rounds towards zero.
• The roundfx
, strtofxfx
, and fxdivi
functions always perform unbiased rounding. They do not support the truncation rounding mode.
Details of how to set rounding mode are given in Setting the Rounding Mode
.
Arithmetic Library Functions
The stdfix.h
header file also declares a number of functions that permit useful arithmetic operations on combi-
nations of fixed-point and integer types. These are the divifx ,
, and
strtofxfx families of functions.
divifx
The divifx
functions, where
fx
is one of r
, lr
, ur
, or ulr
, allow division of an integer value by a fixed-point value to produce an integer result. If you write
#include <stdfix.h> fract f; int i, quo; void foo(void) {
// BAD: division of int by fract gives fract result, not int
f = 0.5r;
i = 2;
quo = i / f;
} then the result of the division is a fract
whose integer part is stored in the variable quo
. This means that the value of quo
is zero, as the division overflows and thus produces a fractional result that is nearly one.
To get the desired result, write
#include <stdfix.h> fract f; int i, quo; void foo(void) {
// GOOD: uses divifx to give integer result
f = 0.5r;
2–94
CrossCore Embedded Studio 2.2.0
Arithmetic Library Functions
i = 2;
quo = divir(i, f);
} which stores the value 4 into the variable quo
.
idivfx
The idivfx
functions, where
fx
is one of r
, lr
, ur
, or ulr
, allow division of a fixed-point value by a fixed-point value to produce an integer result. If you write:
#include <stdfix.h> fract f1, f2; int quo; void foo(void) {
// BAD: division of two fracts gives fract result, not int
f1 = 0.5r;
f2 = 0.25r;
quo = f1 / f2;
} then the result of the division is a fract
whose integer part is stored in the variable quo
. This means that the value of quo
is zero, as the division overflows and thus produces a fractional result that is nearly one.
To get the desired result, write:
#include <stdfix.h> fract f1, f2; int quo; void foo(void) {
// GOOD: uses idivfx to give integer result
f1 = 0.5r;
f2 = 0.25r;
quo = idivr(f1, f2);
} which stores the value 2 into the variable quo
.
fxdivi
The fxdivi
functions, where
fx
is one of r
, lr
, ur
, or ulr
, allow division of an integer value by an integer value to produce a fixed-point result. If you write:
#include <stdfix.h> int i1, i2; fract quo; void foo(void) {
// BAD: division of int by int gives int result, not fract
i1 = 5;
CrossCore Embedded Studio 2.2.0
2–95
Arithmetic Library Functions
i2 = 10;
quo = i1 / i2;
} then the result of the division is an integer which is then converted to a fract
to be stored in the variable quo
.
This means that the value of quo
is zero, as the division is rounded to integer zero and then converted to fract
.
To get the desired result, write:
#include <stdfix.h> int i1, i2; fract quo; void foo(void) {
// GOOD: uses fxdivi to give fract result
i1 = 5;
i2 = 10;
quo = rdivi(i1, i2);
} which stores the value 0.5 into the variable quo
.
mulifx
The mulifx
functions, where
fx
is one of r
, lr
, ur
, or ulr
, allow multiplication of an integer value by a fixedpoint value to produce an integer result. If you write:
#include <stdfix.h> int i, prod; fract f; void foo(void) {
// BAD: multiplication of int by fract produces fract result, not int
i = 50;
f = 0.5r;
prod = i * f;
} then the result of the multiplication is a fract
whose integer part is stored in the variable prod
. This means that the value of prod
is zero, as the multiplication overflows and thus produces a fractional result that is nearly one.
To get the desired result, write
#include <stdfix.h> int i, prod; fract f; void foo(void) {
// GOOD: uses mulifx to give integer result
i = 50;
f = 0.5r;
prod = mulir(i, f);
2–96
CrossCore Embedded Studio 2.2.0
Arithmetic Library Functions
} which stores the value 25 into the variable prod
.
absfx
The absfx
functions, where
fx
is one of hr
, r
, or lr
, compute the absolute value of a fixed-point value.
In addition, you can also use the type-generic macro absfx()
, where the operand type can be any of the signed fixed-point types.
roundfx
The roundfx
functions, where
fx
is one of hr
, r
, lr
, uhr
, ur
, or ulr
, take two arguments. The first is a fixedpoint operand whose type corresponds to the name of the function called. The second gives a number of fractional bits. The first operand is rounded to the number of fractional bits given by the second operand. The second operand must specify a value between 0 and the number of fractional bits in the type. Rounding is unbiased to-nearest, and saturation occurs when the value exceeds that which can be represented in the requested number of fractional bits.
#include <stdfix.h> fract f, rnd; void foo1(void) {
f = 0x45608100p-31r;
rnd = roundr(f, 15); // produces 0x45610000p-31r;
} void foo2(void) {
f = 0x7fff9034p-31r;
rnd = roundr(f, 15); // produces 0x7fffffffp-31r;
}
In addition, you can also use the type-generic macro roundfx()
, where the first operand type can be any of the signed fixed-point types.
countlsfx
The countlsfx
functions, where
fx
is one of hr
, r
, lr
, uhr
, ur
, or ulr
, return the largest integer value k
such that its operand, when shifted up by k
, does not overflow. For zero input, the result is the size in bits of the operand type.
#include <stdfix.h> int scal1, scal2; void foo(void) {
scal1 = countlsr(-0.1r); // gives 3, because
// -0.1r<<3 = -0.8r
scal2 = countlsur(0.1ur); // gives 3, because
// 0.1ur<<3 = 0.8ur
}
CrossCore Embedded Studio 2.2.0
2–97
Arithmetic Library Functions
In addition, you can also use the type-generic macro countlsfx()
, where the operand type can be any of the signed fixed-point types.
strtofxfx
The strtofxfx
functions, where the second
fx
is one of hr
, r
, lr
, uhr
, ur
, or ulr
, parse a string representation of a fixed-point number and return a fixed-point result. They behave similarly to strtod
, and accept input in the same format.
Fixed-Point I/O Conversion Specifiers
The printf
and scanf
families of functions support conversion specifiers for the fixed-point types. These are given in the I/O Conversion Specifiers for the Fixed-Point Types table. Note that the conversion specifier for the signed types,
%r
, is lowercase while the one for the unsigned types,
%R
, is uppercase.
When used with the scanf
family of functions, these conversion specifiers accept input in the same format as consumed by the strtofxfx
(for more information, see
) functions, which is the same as that accepted for
%f
.
Table 2-31: I/O Conversion Specifiers for the Fixed-Point Types
Type
short fract fract long fract unsigned short fract unsigned fract unsigned long fract
Conversion Specifier
%hr
%r
%lr
%hR
%R
%lR
When used with the printf
family of functions, fixed-point values are printed:
• As hexadecimal values by default, or when using the Lite version of the CCES I/O library. For example, printf("fract: %r\n", 0.5r); // prints fract: 40000000
• Like floating-point values when linking with the version of the CCES I/O library with full fixed-point support, using the
-flags-link -MD__LIBIO_FX
switch. For example, printf("fract: %r\n", 0.5r); // prints fract: 0.500000
Optional precision specifiers are accepted that control the number of decimal places printed, and whether a trailing decimal point is printed. However, these will have no effect unless the version of the CCES I/O library with full fixed-point support is being used. For more information, see the run-time library manual for SHARC processors.
Setting the Rounding Mode
As discussed in
Rounding Behavior , these rounding modes are supported for fixed-point arithmetic:
2–98
CrossCore Embedded Studio 2.2.0
Using Native Fixed-Point Types
• Truncation (this is the default rounding mode)
• Biased round-to-nearest rounding
• Unbiased round-to-nearest rounding
To set the rounding mode, use a pragma or a compile-time switch.
The following compile-time switches control rounding behavior:
•
•
•
The given rounding mode will then be the default for the whole of the source file being compiled.
You can also use a pragma to allow finer-grained control of rounding. The pragmas are:
•
#pragma FX_ROUNDING_MODE TRUNCATION
•
#pragma FX_ROUNDING_MODE BIASED
•
#pragma FX_ROUNDING_MODE UNBIASED
If one of these pragmas is applied at file scope, it applies until the end of the translation unit or until another pragma at file scope changes the rounding mode.
If one of these pragmas is applied within a compound statement (that is, within a block enclosed by braces), the pragma applies to the end of the compound statement where it is specified. The rounding mode will return to the outer scope rounding mode on exit from the compound statement. An example of how to use these pragmas is given in the following example.
Example. Use of #pragma FX_ROUNDING_MODE to Control Rounding of Arithmetic on Fixed-Point Types
#include <stdfix.h>
#pragma FX_ROUNDING_MODE BIASED fract my_func(void) {
// rounding mode here is biased
{
#pragma FX_ROUNDING_MODE UNBIASED
// rounding mode here is unbiased
}
// rounding mode here is biased
}
#pragma FX_ROUNDING_MODE TRUNCATION fract my_func2(void) {
// rounding mode here is truncation
}
For more information, see
#pragma FX_ROUNDING_MODE {TRUNCATION|BIASED|UNBIASED}
.
CrossCore Embedded Studio 2.2.0
2–99
Language Standards Compliance
Language Standards Compliance
The compiler supports code that adheres to the ISO/IEC 9899:1990 C standard, ISO/IEC 9899:1999 C standard,
ISO/IEC 14882:2003 C++ standard and the ISO/IEC 14882:2011 C++ standard.
The compiler's level of conformance to the applicable ISO/IEC standards is validated using commercial test-suites from Plum Hall, Perennial and Dinkumware.
C Mode
The compiler shall compile any program that adheres to a freestanding implementation of the ISO/IEC 9899:1990
C standard, but it does not prohibit the use of language extensions (
C/C++ Compiler Language Extensions
) that are
compatible with the correct translation of standard-conforming programs. To enable this mode the
switch should be used.
The compiler does not support the C99 keywords
_Complex
and
_Imaginary
. The ISO/IEC 9899:1990 C standard library provided in C89 mode is used in C99 mode. This is the default mode (
In C mode, the best standard conformance is achieved using the default switches and the following non-default switches:
•
•
•
. See also Enumeration Constants That Are Not int Type .
The language extensions cannot be disabled to ensure strict compliance to the language standards. However, when
compiling for MISRA-C Compiler
compliance checking, language extensions are disabled.
When the
-c89
switch is used, these extensions already include many of the ISO/IEC 9899:1999 standard features.
The following features are only available in C99 mode.
• Type qualifiers may appear more than once in the same specifier-qualifier-list.
•
__func__
predefined identifier is supported.
• Universal character names (
\u
and
\U
) are accepted.
• The use of function declarations with non-prototyped parameter lists are faulted.
• The first statement of a for-loop can be a declaration, not just restricted to an expression.
• Type qualifiers and static
are allowed in parameter array declarators.
C++ Mode
The compiler shall compile any program that adheres to a freestanding implementation of the ISO/IEC 14882:2003
C++ standard, but it does not prohibit the use of language extensions (
C/C++ Compiler Language Extensions
) that are compatible with the correct translation of standard-conforming programs. The Library provided in C++ mode is a proper subset of the full Standard C++ Library and is designed specifically for the needs of the embedded market.
2–100
CrossCore Embedded Studio 2.2.0
Language Standards Compliance
Using the
switch, support for many of the ISO/IEC 14882:2011 C++ standard language features can be enabled. However, there is no support for library features that are new to ISO/IEC 14882:2011. The key language features supported by the
switch are (please refer to the ISO/IEC 14882:2011 standard for detail on usage):
• The
static_assert
construct
• friend class
syntax is extended to all nonclass types
• Mixed string literal concatenations
• The C99-style
_Pragma
operator
• An explicit instantiation maybe prefixed with the extern
keyword
• The keyword auto
can be used as a type specifier
• The keyword decltype
• Scoped enumeration types (defined with the keyword sequence enum class
)
• Lambdas
• Rvalue referemces
• Ref-qualifiers
• Functions cane be " deleted
"
• Special member functions can be explitly " defaulted
"
• Move constructors and move assignment operators
• Conversion functions can be marked explicit
• The keyword nullptr
can be used as both a null pointer constant and a null pointer-to-member constant
• The context-sensitive keyword final
• Alias and alias template declarations
• Variadic templates
• U-literals and the char16_t
and char32_t
keywords
• Inline namespaces
• Initializer lists
• The noexcept
specifier and keyword
• Range-based " for
" loops
• Non-static data member initializers
• constexpr
keyword
• Unrestricted unions
CrossCore Embedded Studio 2.2.0
2–101
MISRA-C Compiler
• Delegating constructors
• Ref-qualifiers on member functions
• Raw strings
• UTF-8 strings
In C++ mode, the best possible standard conformance is achieved using the following default switches:
•
•
•
•
In addition, the best possible standard conformance is achieved using the following non-default switches:
•
•
•
•
MISRA-C Compiler
This section provides an overview of MISRA-C compiler and MISRA-C 2004 Guidelines.
MISRA-C Compiler Overview
The Motor Industry Software Reliability Association (MISRA) in 1998 published a set of guidelines for the C programming language to promote best practice in developing safety related electronic systems in road vehicles and other embedded systems. The latest release of MISRA-C:2004 has addressed many issues raised in the original guidelines specified in MISRA-C:1998. Complex rules are now split into component parts. There are 121 mandatory and
20 advisory rules. The compiler issues a discretionary error for mandatory rules and a warning for advisory rules.
More information on MISRA-C can be obtained at www.misra.org.uk/ .
The compiler detects violations of the MISRA-C rules at compile-time, link-time and runtime. It has full support for the MISRA-C 2004 Guidelines. The majority of MISRA-C rules are easy to interpret.
Those that require further explanation can be found in
.
As a documented extension, the compiler supports the type qualifiers
__pm
and
__dm
(see
Keywords (pm dm) ) and the integral types
long long
and unsigned long long
. In addition, for processors that support byte-addressing (shown in
, the Processors Supporting Byte-Addressing table), the type qualifiers
__word_addressed
and
__byte_addressed
are supported (see
The byte_addressed and word_addressed Keywords ).
2–102
CrossCore Embedded Studio 2.2.0
MISRA-C Compiler
No other language extensions are supported when MISRA checking is enabled. Common extensions, such as the keywords section
and inline
, are not allowed in the MISRA-C mode, but the same effects can be achieved by using pragmas
#pragma section/#pragma default_section
. Rules can be suppressed by the use of
command-line switches or the MISRA-C extensions to Diagnostic Control Pragmas
.
NOTE: The run-time checking that is used for validating a number of rules should not be used in production code. The cost of detecting these violations is expensive in both run-time performance and code size. A subset of these run-time checks can also be enabled when MISRA-C is not enabled. For more informa-
.
Refer to the C Mode (MISRA) Compiler Switches table in
Compiler Command-Line Switches for more informa-
tion.
MISRA-C Compliance
The MISRA-C:2004 Guidelines Forum (visit http://www.misra.org.uk
) is an essential reference for ensuring that code developed or requiring modification complies to these guidelines. A rigorous checking tool such as this compiler makes achieving compliance a lot easier than using a less capable tool or simply relying on manual reviews of the code. The MISRA-C:2004 Guidelines Forum describes a compliance matrix that a developer uses to ensure that each rule has a method of detecting the rule violation. A compliance checking tool is a vital component in detecting rule violations. It is recognized in the guidelines document that in some circumstances it may be necessary to deviate from the given rules. A formal procedure has to be used to authorize these deviations rather than an individual programmer having to deviate at will.
Using the Compiler to Achieve Compliance
The CCES compiler is one of the most comprehensive MISRA-C: 2004 compliance checking tools available. The compiler has various command-line switches and
to enable you to achieve MISRA-C:
2004 compliance.
During development, it is recommended that the application is built with maximum compliance enabled.
Use the
-misra-strict
command-line switch to detect the maximum number of rule violations at compile-time.
However, if existing code is being modified, using
-misra-strict
may result in a lot of errors and warnings. The majority are usually common rule violations that are mainly advisory and typically found in header files as a result of macro expansion. These can be suppressed using the
-misra
command-line switch. This has the potential benefit of focusing change on individual source file violations, before changing headers that may be shared by more than one project.
The
-misra-no-cross-module
command-line switch disables checking rule violations that occur across source modules. During development, some external variables may not be fully utilized and rather than add in artificial uses to avoid rule violations, use this switch.
The
-misra-no-runtime
command-line switch disables the additional run-time overheads imposed by some rules. During development these checks are essential in ensuring code executes as expected. Use this switch in release mode to disable the run-time overheads.
CrossCore Embedded Studio 2.2.0
2–103
MISRA-C Compliance
You can use the
-misra-testing
command-line switch during development to record the behavior of executable code. Although the MISRA-C: 2004 Guidelines do not allow library functions, such as those that are defined in header
<stdio.h>
, it is recognized that they are an essential part of validating the development process.
During development, it is likely that you will encounter areas where some rule violations are unavoidable. In such circumstances you should follow the procedure regarding rule deviations described in the MISRA-C: 2004 Guidelines Forum. Use the
-Wmis_suppress
and
-Wmis_warn
switches
to control the detection of rule violations for whole source files. Finer control is provided by the diagnostic control pragmas. These pragmas allow you to suppress the detection of specified rule violations for any number of C statements and declarations.
Example
#include <misra_types.h>
#include <def21266.h>
#include "proto.h" /* prototype for func_state and my_state */ int32_t func_state(int32_t state)
{ return state & TIMOD1;
/* both operands signed, violates rule 12.7 */
}
#define my_flag 1 int32_t my_state(int32_t state)
{ return state & my_flag;
/* both operands signed, violates rule 12.7 */
}
In the above example,
<def21266.h>
uses signed masks and signed literal values for register values. The code is meaningful and trusted in this context. You may suppress this rule and document the deviation in the code. For code violating the rule that is not from the system header, you may wish to rewrite the code:
#include <misra_types.h>
#include <def21266.h>
#include "proto.h" /* prototype for func_state and my_state */
#ifdef _MISRA_RULES
#pragma diag(push)
#pragma diag(suppress:misra_rule_12_7:"Using the def file is a safe and justified deviation for rule 12.7")
#endif /* _MISRA_RULES */ int32_t func_state(int32_t state)
{ return state & TIMOD11; /* both operands signed, violates rule 12.7 */
}
#ifdef _MISRA_RULES
2–104
CrossCore Embedded Studio 2.2.0
MISRA-C Compiler
#pragma diag(pop) /* allow violations of 12.7 to be detected again */
#endif /* _MISRA_RULES */
#define my_flag 1u uint32_t my_state(uint32_t state)
{ return state & my_flag;/* o.k both unsigned */
}
Rules Descriptions
The following are brief explanations of how some of the MISRA-C rules are supported and interpreted in this
CCES release due to the fact that some rules are handled in a nonstandard way, or some are not handled at all.
NOTE: Since the data types char, short and int are all represented as 32-bit integers on the SHARC architecture,
MISRA rules relating to the size of variables may not be issued.
• Rule 1.4 (required): The compiler/linker shall be checked to ensure that 31 character significance and case sen-
sitivity are supported for external identifiers.
The compiler and linker fully support this requirement.
• Rule 1.5 (required): Floating-point implementations should comply with a defined floating-point standard.
Refer to
• Rule 2.4 (advisory): Sections of code should not be "commented out".
A diagnostic is reported if one of the following is encountered inside of a comment.
- character `{` or `}'
- character `;' followed by a new-line character
• Rule 5.1 (required): Identifiers (internal and external) shall not rely on the significance of more than 31 char-
acters.
This rule is only enforced when the
compiler switch is used.
• Rule 5.5 (advisory): No object or function identifier with static storage duration should be reused.
This rule is enforced by the compiler prelinker. The compiler generates
.misra
extension files that the prelinker uses to ensure that the same identifier is not used at file-scope within another module. This rule is not enforced if the
compiler switch is used.
• Rule 5.7 (advisory): No identifier shall be reused.
This rule is limited to a single source file. The rule is only enforced when the
compiler switch is used.
• Rule 6.3 (advisory): typedefs that indicate size and signedness should be used in place of basic types.
CrossCore Embedded Studio 2.2.0
2–105
MISRA-C Compiler
The typedefs
for the basic types are provided by the system header files
<stdint.h>
and
<stdbool.h>
.
The rule is only enforced when the
compiler switch is used.
• Rule 6.4 (advisory): Bit fields shall only be defined to be of type unsigned int or signed int.
The rule regarding the use of plain int
is only enforced when the
compiler switch is used.
• Rule 8.1 (required): Functions shall have prototype declarations and the prototype shall be visible at both the
function definition and the call.
For static and inline functions this rule is only enforced when the
compiler switch is used.
• Rule 8.5 (required): There shall be no definitions of objects or functions in a header file.
This rule does not apply to inline functions.
• Rule 8.8 (required): An external object or function shall be declared in one and only one file.
This rule is enforced by the compiler prelinker. The compiler generates
.misra
extension files that the prelinker uses to ensure that the global is used in another file. The rule is not enforced if the
switch is used.
• Rule 8.10 (required): All declarations and definitions of objects or functions at file scope shall have internal
linkage unless external linkage is required.
This rule is enforced by the compiler prelinker. The compiler generates
.misra
extension files that the prelinker uses to ensure that the global is used in another file. The rule is not enforced if the
switch is used.
• Rule 9.1 (required): All automatic variables shall have been assigned a value before being used.
The compiler attempts to detect some instances of violations of this rule at compile-time. There is additional code added at run-time to detect unassigned scalar variables. The additional Integral types with a size less than
switches. The run-time code is not added if
or
• Rule 10.5 (required): If the bitwise operators ~ and << are applied to an operand of underlying type unsigned
char or unsigned short, the result shall be immediately cast to the underlying type of the operand.
When constant-expressions violate this rule, they are detected only when the
switch is used.
• Rule 11.3 (advisory): A cast shall not be performed between a pointer type and an integral type.
The compiler always allows a constant of integral type to be cast to a pointer to a volatile type.
volatile int32_t *n; n = (volatile int32_t *)10;
There is only one case where this rule is not applied.
2–106
CrossCore Embedded Studio 2.2.0
MISRA-C Compiler
int32_t *n; n = (int32_t *)10;
• Rule 12.4 (required): The right-hand operand of a logical && or || operator shall not contain side-effects.
A function call used as the right-hand operand will not be faulted if it is declared with an associated
#pragma pure
directive.
• Rule 12.7 (required): Bitwise operators shall not be applied to operands whose underlying type is signed.
The compiler will not enforce this rule if the two operands are constants.
• Rule 12.8 (required): The right-hand operand of a shift operator shall lie between zero and one less than the
width in bits of the underlying type of the left-hand operand.
If the right-hand operand is not a constant expression, the violation will be checked by additional run-time code when
-misra-no-runtime
is not enabled. If both operands are constants, the rule is only enforced when neither the
This check is also available separately, via the
• Rule 12.12 (required): The underlying bit representations of floating-point values shall not be used.
MISRA-C rules such as 11.4 prevent casting of bit-patterns to floating-point values. Hexadecimal floatingpoint constants are also not allowed when MISRA-C switches are used.
• Rule 13.2 (advisory): Tests of a value against zero should be made explicit, unless the operand is effectively Boo-
lean. The compiler treats variables which use the type bool
(a typedef is declared in
<stdbool.h>
) as Effec-
tively Boolean and will not raise an error when these are implicitly tested as zero, as follows: bool b = 1; if (b)
...;
• Rule 13.7 (required): Boolean operations whose results are invariant shall not be used.
The compiler does not detect cases where there is a reliance on more than one conditional statement. Constant expressions violating the rule are only detected when the
compiler switch is used.
• Rule 16.2 (required): Functions shall not call themselves, either directly or indirectly.
A compile-time check is performed for a single file. Run-time code is added to ensure that functions do not call themselves directly or indirectly, but this code is not generated if the
compiler switch is used.
• Rule 16.4 (required): The identifiers used in the declaration and definition of a function shall be identical.
A declaration of a parameter name may have one leading underscore that the definition does not contain. This
is to prevent name clashing. If the
compiler switch is enabled, the underscore is significant
and results in the violation of this rule.
CrossCore Embedded Studio 2.2.0
2–107
MISRA-C Compiler
• Rule 16.5 (required): Functions with no parameters shall be declared and defined with parameter type void.
Function main
shall only be reported as violating this rule if the
compiler switch is used.
• Rule 16.10 (required): If a function returns error information, then the error information shall be tested.
A function declared with return type bool
, which is a typedef
declared in header file
<stdbool.h>
will be faulted if the result of the call is not used.
• Rule 17.1 (required): Pointer arithmetic shall only be applied to pointers that address an array or array ele-
ment.
Checking is performed at runtime. A run-time function looks at the value of the pointer and checks to see
whether it violates this rule. This check is also available via the
and
switches.
It can be disabled via the
• Rule 17.2 (required): Pointer subtraction shall only be applied to pointers that address elements of the same
array. Checking is performed at runtime. A run-time function looks at the value of the pointers and checks to see whether it violates this rule.
• Rule 17.3 (required): >, >=, <, <= shall not be applied to pointers that address elements of different arrays.
Checking is performed at runtime. A run-time function looks at the value of the pointers and checks to see whether it violates this rule.
• Rule 17.4 (required): Array indexing shall be the only allowed form of pointer arithmetic.
Checking is performed at runtime to ensure the object being indexed is an array. A run-time function looks at the value of the pointers and checks to see whether it violates this rule.
All other forms of pointer arithmetic are reported at compile-time as violations of this rule.
• Rule 17.6 (required): The address of an object with automatic storage shall not be assigned to another object
that may persist after the first object has ceased to exist.
Rule is not enforced under the following circumstances: if the address of a local variable is passed as a parameter to another function, the compiler cannot detect whether that address has been assigned to a global object.
• Rule 18.2 (required): An object shall not be assigned to an overlapping object.
The rule is not enforced by the compiler.
• Rule 18.3 (required): An area of memory shall not be reused for unrelated purposes.
The rule is not enforced by the compiler.
• Rule 19.4 (required): C macros shall only expand to a braced initializer, a constant, a string literal, a parenthe-
sized expression, a type qualifier, a storage class specifier, or a do-while-zero construct.
Use of
#pragma diag(suppress:misra_rule_19_4)
will suppress violations of this rule for any macro expansion during the scope of the suppression. If a macro is defined within the scope of the suppression, then
2–108
CrossCore Embedded Studio 2.2.0
MISRA-C Compiler
the macro expansion will not be detected for violation of rule 19.4 even if the expansion point does not
suppress the rule. See Diagnostic Control Pragmas .
• Rule 19.7 (advisory): A function shall be used in preference to a function-like macro.
The rule is only enforced when the compiler switch
• Rule 19.15 (required): Precautions shall be taken in order to prevent the contents of a header file being inclu-
ded twice.
The compiler will report this violation if a header file is included more than once and does not prevent redeclarations of types, variables or functions.
• Rule 20.3 (required): The validity of values passed to library functions shall be checked.
This is not enforced by the compiler. The rule puts the responsibility on the programmer.
• Rule 20.4 (required): Dynamic heap memory allocation shall not be used.
Prototype declarations for functions performing heap allocation should be declared with an associated
#pragma misra_func(heap)
directive. This directive allows the compiler to detect violations of this rule when these functions are used.
• Rule 20.7 (required): The setjmp macro and longjmp function shall not be used.
Prototype declarations for these should be declared with an associated
#pragma misra_func(jmp)
directive. This directive allows the compiler to detect violations of this rule when these functions are used.
• Rule 20.8 (required): The signal handling facilities of <signal.h> shall not be used.
Prototype declarations for functions in this header should be declared with an associated
#pragma misra_func(handler)
directive. This directive allows the compiler to detect violations of this rule when these functions are used.
• Rule 20.9 (required): The input/output library <stdio.h> shall not be used.
Prototype declarations for functions in this header should be declared with an associated
#pragma misra_func(io)
directive. This directive allows the compiler to detect violations of this rule when these functions are used.
• Rule 20.10 (required): The library functions atof, atoi and atol from library <stdlib.h> shall not be used.
Prototype declarations for these functions should be declared with an associated
#pragma misra_func(string_conv)
directive. This directive allows the compiler to detect violations of this rule when these functions are used.
• Rule 20.11 (required): The library functions abort, exit, getenv and system from library <stdlib.h> shall not be
used.
Prototype declarations for these functions should be declared with an associated
#pragma misra_func(system)
directive. This directive allows the compiler to detect violations of this rule when these functions are used.
CrossCore Embedded Studio 2.2.0
2–109
Run-Time Checking
• Rule 20.12 (required): The time handling functions of library <time.h> shall not be used.
Prototype declarations for these functions should be declared with an associated
#pragma misra_func(time)
directive. This directive allows the compiler to detect violations of this rule when these functions are used.
• Rule 21.1 (required): Minimization of run-time failures shall be ensured by the use of at least one of: (a) static
analysis tools/techniques; (b) dynamic analysis tools/techniques; (c) explicit coding of checks to handle run-time faults.
The compiler performs some static checks on uses of unassigned variables before conditional code and use of constant expressions. The compiler performs run-time checks for arithmetic errors, such as division by zero, array bound errors, unassigned variable checking and pointer dereferencing. Run-time checking has a negative
effect on code performance. The
compiler switch turns off the run-time checking.
Run-Time Checking
The compiler provides support for detecting common programming mistakes, such as dereferencing a NULL pointer, or accessing an array beyond its bounds. The compiler does this by generating additional code to check for such conditions at runtime. Such code occupies space and incurs a performance penalty, so you should only use run-time checking when developing and debugging your application; products for release should always have run-time checking disabled.
The compiler's run-time checks are a subset of those enabled when MISRA-C run-time checking is active. For more
information, see MISRA-C Compiler
.
The following sections describe run-time checking in more detail:
•
•
•
Response When Problems Are Detected
•
Limitations of Run-Time Checking
Enabling Run-Time Checking
Because of the associated overheads, run-time checking is disabled by default. You can enable run-time checking:
• By specifying command-line switches;
• Through the IDE, via run-time checking options under Project > Properties > C/C++ Build > Settings > Tool
Settings > Compiler > Run-time Checks.
In both cases, you can enable all supported run-time checks, or just enable specific subsets.
Once run-time checking is enabled to some level, you can further turn that checking off and on again within your code, with pragmas. This allows you to narrow your focus down to particular functions, or to exclude certain functions from checking.
2–110
CrossCore Embedded Studio 2.2.0
Enabling Run-Time Checking
Command-Line Switches for Run-Time Checking
The following switches are used to turn run-time checking on:
•
. Turns on all run-time checks.
•
. Turns on checking of array boundaries.
•
. Turns on checking for division by zero.
•
. Turns on checking of heap operations.
•
. Turns on checking for NULL pointer dereferencing.
•
. Turns on checking of shift operations.
•
. Turns on checking for stack overflow.
•
. Turns on checking for use of variables before they've been assigned values.
The following switches are used to turn run-time checking off:
•
. Turns off all run-time checks.
•
. Turns off checking of array boundaries.
•
. Turns off checking for division by zero.
•
. Turns off checking of heap operations.
•
. Turns off checking for NULL pointer dereferencing.
•
. Turns off checking of shift operations.
•
. Turns off checking for stack overflow.
•
. Turns off checking for use of variables before they've been assigned values.
You can use combinations of these switches to enable the subset you require. For example, the following two sets of switches are equivalent:
•
-rtcheck -no-rtcheck-arr-bnd -no-rtcheck-div-zero
\
-no-rtcheck-heap -no-rtcheckstack
•
-rtcheck-null-ptr -rtcheck-shift-check -rtcheck-unassigned
For more information, see
Pragmas for Run-Time Checking
The following pragmas are used to enable and disable run-time checks.
•
switches.
CrossCore Embedded Studio 2.2.0
2–111
Run-Time Checking
•
#pragma rtcheck(off) . Turns off all run-time checking.
Note that these pragmas do not affect which run-time checks apply. Use command-line switches to select the appropriate checks, then use the pragmas to enable those checks during compilation of your functions of interest.
These pragmas cannot disable and re-enable heap-operation checking, or stack overflow detection. These checks are provided by linking in alternative library support, which apply to the whole application.
For more information, see
Supported Run-Time Checks
The following run-time checks are supported by the compiler:
Array Boundary Checks
When generating code to access arrays, the compiler generates additional code to see whether the location accessed falls within the boundaries of an array.
Division by Zero Checks
When generating code to perform an integer or floating-point division, the compiler generates additional code to check that the divisor is non-zero.
Heap Checks
The debugging version of the heap library checks for leaks, multiple frees of the same pointer, writes beyond the bounds of an allocation, and so on.
NULL Pointer Checks
When generating code to read the value pointed to by a pointer, the compiler generates additional code to verify that the pointer is not NULL.
Shift Checks
When generating code to shift a value X by some amount Y, the compiler generates additional code to check that:
• Y is not a negative value.
• Y is less than the number of bits required to represent X's type.
2–112
CrossCore Embedded Studio 2.2.0
Run-Time Checking
Stack Overflow Checks
When enabled, the run-time environment makes use of the SHARC processors circular-buffer mechanism for stack overflow detection: if the stack pointer (I7) advances beyond the limits defined by its corresponding base and length registers, a circular-buffer interrupt (CB7I) occurs and is trapped.
Unassigned Variable Checks
When generating code to read the value of a variable, the compiler generates additional code to make sure a value has previously been assigned to the variable.
Response When Problems Are Detected
In most cases, the additional code generated by the compiler includes code for emitting a diagnostic message to the stderr
stream. This message is emitted when the run-time check finds a problem.
When stack overflow is detected, however, the generated code transfers control to the special label
__adi_stack_overflowed
, as emitting a diagnostic to the stderr stream would require additional stack space.
The IDE normally places a breakpoint on the
__adi_stack_overflowed
label. For more information, see
.
The heap debugging library also provides support for logging problems to a file instead of reporting them immediately to the stderr stream. For more information, see
.
Limitations of Run-Time Checking
Besides the space/performance overheads incurred by the additional code, the following limitations apply to runtime checking:
• Compiled code only
Because the run-time checks rely on additional code emitted during function compilation, the run-time checks can only apply to code compiled by the compiler, while run-time checks are enabled. Hand-written assembly or previously-compiled code cannot make benefit from run-time checking.
• No asm statements
The compiler has no visibility into the contents of asm statements, so any actions carried out by asm statements will not be checked by any enabled run-time checking. For more information, see
.
• Stdio support required
Because the generated diagnostics are emitted to the stderr stream, run-time checking is only beneficial when the application supports the standard error stream, and the stream is attached to some suitable output device
(such as the IDE console, which is the usual case when running an application within the debugger).
CrossCore Embedded Studio 2.2.0
2–113
C/C++ Compiler Language Extensions
C/C++ Compiler Language Extensions
The compiler supports a set of extensions to the ANSI standard for the C and C++ languages. These extensions add support for DSP hardware and allow some C++ programming features when compiling in C mode. Most extensions are also available when compiling in C++ mode.
This section contains information on ISO/IEC 9899:1999 standard features that are supported in C89 mode:
•
•
•
•
•
Non-Constant Initializer Support
•
•
Hexadecimal Floating-Point Numbers
•
•
•
•
Enumeration Constants That Are Not int Type
•
This section also contains information on other language extensions:
•
The fract Native Fixed-Point Type
•
Inline Assembly Language Support Keyword (asm)
•
Dual Memory Support Keywords (pm dm)
•
•
Placement Support Keyword (section)
•
Placement of Compiler-Generated Code and Data
•
•
Preprocessor Generated Warnings
•
•
•
2–114
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Language Extensions
•
•
•
Accessing External Memory on ADSP-2126x and ADSP-2136x Processors
The additional keywords that are part of the C/C++ extensions do not conflict with any ANSI C/C++ keywords.
The formal definitions of these extension keywords are prefixed with a leading double underscore (
__
command-line switch is used, the compiler defines the shorter form of the keyword extension that omits the leading underscores.
This section describes only the shorter forms of the keyword extensions, but in most cases you can use either form in your code. For example, all references to the inline
keyword in this text appear without the leading double underscores, but you can interchange inline
and
__inline
in your code.
You might need to use the longer form (such as
__inline
) exclusively if porting a program that uses the extra
Analog Devices keywords as identifiers. For example, a program might declare local variables, such as pm
or dm
. In this case, use the
-no-extra-keywords
switch, and if you need to declare a function as inline, or allocate variables to memory spaces, you can use
__inline
or
__pm
/
__dm
respectively.
This section provides an overview of the extensions, with brief descriptions, and directs you to text with more information on each extension.
The Keyword Extensions table provides a brief description of each keyword extension and directs you to sections of this chapter that document the extensions in more detail. The Operational Extensions table provides a brief description of each operational extension and directs you to sections that document these extensions in more detail.
Table 2-32: Keyword Extensions
Keyword Extensions
inline asm() dm pm section("string") bool
, true
, false restrict
keyword
Description
Directs the compiler to integrate the function code into the code of the calling
function(s). For more information, see Function Inlining
.
Places ADSP-21xxx/SCxxx assembly language instructions directly in your C/C++
program. For more information, see Inline Assembly Language Support Keyword
Specifies the location of a static or global variable or qualifies a pointer declaration
"
*
" as referring to Data Memory (DM). For more information, see
.
Specifies the location of a static or global variable or qualifies a pointer declaration
"
*
" as referring to Program Memory (PM). For more information, see Dual
Memory Support Keywords (pm dm)
.
Specifies thesection in which an object or function is placed. The section
keyword has replaced the segment
keyword of the previous releases of the compiler software. For more information, see
Placement Support Keyword (section)
.
A boolean type. For more information, see Boolean Type .
Specifies restricted pointer features. For more information, see Restricted Pointers .
CrossCore Embedded Studio 2.2.0
2–115
C/C++ Compiler Language Extensions
Table 2-32: Keyword Extensions (Continued)
Keyword Extensions
fract
Description
Support for fixed-point arithmetic. For more information, see Using Native Fixed-
Table 2-33: Operational Extensions
Operation Extensions
Variable-length arrays
Long identifiers
Non-constant initializers
Designated initializers
Compound literal expressions
Preprocessor-generated warnings
C++ style comments
Description
Support for variable-length arrays lets you use arrays whose length is not known
until run time. For more information, see Variable-Length Array Support
.
Supports identifiers of up to 1022 characters in length. For more information, see
.
Support for non-constant initializers lets you use non-constants as elements of aggregate initializers for automatic variables. For more information, see
Non-Constant Initializer Support
.
Support for designated initializers lets you specify elements of an aggregate initial-
izer in arbitrary order. For more information, see Designated Initializers .
Support for compound literals lets you create an aggregate array or structure value
from component values within an expression. For more information, see Compound Literals .
Lets you generate warning messages from the preprocessor. For more information,
see Preprocessor Generated Warnings
.
Allows for "//" C++ style comments in C programs. For more information, see
.
Function Inlining
The inline
keyword directs the compiler to integrate the code for the function you declare as inline
into the code of its callers. Inline function support and the inline
keyword is a standard feature of C++ and the ISO/IEC
9899:1999 C standard; the compiler provides this keyword as a C extension in C89 mode. For more information, see
.
Function inlining eliminates the function call overhead and increases the speed of your program’s execution. Argument values that are constant and that have known values may permit simplifications at compilation time so that not all of the inline function’s code need be included.
The following example shows a function definition that uses the inline keyword.
inline int max3 (int a, int b, int c) {
return max (a, max(b, c));
}
2–116
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Language Extensions
The compiler can decide not to inline a particular function declared with the inline keyword, with a diagnostic remark cc1462 issued if the compiler chooses to do this. The diagnostic can be raised to a warning by use of the -
Wwarn switch. For more information, see
-W{annotation|error|remark|suppress|warn} number[,
Function inlining can also occur by use of the
(automatic function inlining) switch, which enables the inline expansion of C/C++ functions that are not necessarily declared inline in the source code. The amount of auto-inlining the compiler performs is controlled using the
(optimize for speed versus size) switch.
The compiler follows a specific order of precedence when determining whether a call can be inlined. The order is:
1. If the definition of the function is not available (for example, it is a call to an external function), the compiler cannot inline the call.
2. If the
switch is specified, the compiler does not inline the call. If the call is to a function that
has #pragma always_inline specified (see Inline Control Pragmas
), a warning will also be issued.
3. If the call is to a function that has
#pragma never_inline
specified, the call will not be inlined.
4. If the call is via a pointer-to-function, the call will not be inlined unless the compiler can prove that the pointer will always point to the same function definition.
5. If the call is to a function that has a variable number of arguments, the call will not be inlined.
6. If the module contains asm statements at global scope (outside function definitions), the call may not be inlined because the asm statement restricts the compiler’s ability to reorder the resulting assembly output.
7. If the call is to a function that has
specified, the call is inlined. If the call exceeds the current speed/space ratio limits, the compiler will issue a warning, but will still inline the call.
8. If the call is to a function that has the inline
qualifier, or has
#pragma inline
specified, and the
switch has been used, the compiler inlines the call. If the call exceeds the current speed/space
ratio limits, the compiler will issue a warning, but will still inline the call.
9. If the caller and callee are mapped to different code sections, the call will not be inlined unless the callee has the inline
qualifier or has
#pragma inline
specified.
10. If the call is to a function that has the inline
qualifier or has
#pragma inline
specified and optimization is enabled, the called function will be compared against the current speed/size ratio limits for code size and stack size. The calling function will also be examined against these limits. Depending on the limits and the relative sizes of the caller and callee, the inlining may be rejected.
11. If the call is to a function that does not have the inline
qualifier or
#pragma inline
, and does not have
#pragma weak_entry
switch has been specified to enable automatic inlining, the called function will be considered as a possible candidate for inlining, according to the current speed/size ratio limits, as if the inline qualifier were present.
The compiler bases its code-related speed/size comparisons on the
num switch. When
-Ov
is in the range
1...100, the compiler performs a calculation upon the size of the generated code using the
-Ov
value, and this will determine whether the generated code is “too large” for inlining to occur. When
-Ov
has the value 1, only very
CrossCore Embedded Studio 2.2.0
2–117
Function Inlining
small functions are considered small enough to inline; when
-Ov
has the value 100, larger functions are more likely to be considered suitable as well.
When
-Ov
has the value 0, the compiler is optimizing for space. The speed/space calculation will only accept a call for inlining if it appears that the inlining is likely to result in less code than the call itself would (although this is an approximation, since the inlining process is a high-level optimization process, before actual machine instructions have been selected).
Inlining and Optimization
The inlining process operates regardless of whether optimization has been selected (although if optimization is not enabled, then inlining will only happen when forced by
or the
The speed/size calculation still has an effect, although an optimized function is likely to have a different size from a non-optimized one, which is smaller (and therefore more likely to be inlined) and is dependent on the kind of optimization done.
A non-optimized function has loads and stores to temporary values which are optimized away in the optimized version, but an optimized function may have unrolled or vectorized loops with multiple variants, selected at run-time for the most efficient loop kernel, so an optimized function may run faster, but not be smaller.
Given that the optimization emphasis may be changed within a module – or even turned off completely – by the optimization pragmas, it is possible for either, both, or neither of the caller and callee to be optimized. The inlining process still operates, and is only affected by this in as far as the speed/size ratios of the resulting functions are concerned.
Inlining and Out-of-Line Copies
In C++ mode, the compiler conforms to the ISO/IEC:14882:2003 C++ standard and the ISO/IEC:14882:2011 C+
+ standard. In this mode the compiler will generate an out-of-line copy if the address of the function has been taken or the compiler has not been able to inline the call. Also, the out-of-line copy will have external linkage and be generated once by the prelinker if the function has been declared extern inline
.
The following paragraphs describe the differences between C89 mode (
inline is standard conforming in C99 mode and is an extension in C89 mode.
An inline function declared static
behaves the same whether it is declared in C89 mode or C99 mode. No out-ofline copy is generated if all calls to the function are inlined and the address of the function is not taken.
An inline
function declared with no storage class specifier behaves as follows. In C99 mode, if the address of the function is taken, then all calls not inlined within the current translation unit refer to an externally defined instance of the function, not the instance declared within this translation unit. In C99 mode, if the address of the function is not taken, it behaves as if the function has been declared with the keyword static
. In C89 mode, it always behaves as if the function has been declared with the keyword static
.
An inline
function declared extern
behaves differently in C99 mode (
) and C89 mode (
). In C99 mode, an external definition is always created. Therefore such a declaration should not be within a header file as this
2–118
CrossCore Embedded Studio 2.2.0
Function Inlining
will result in a multiply defined symbol. In C89 mode, no out-of-line copy is created; therefore, if the address of the function is taken or a call is not inlined then an external reference is created and must be satisfied elsewhere.
Inlining and Global asm Statements
Inlining imposes a particular ordering on functions. If functions A and B are both marked as inline, and each calls the other, only one of the inline qualifiers can be followed. Depending on which the compiler chooses to apply, either A will be generated with inline versions of B, or B will be generated with inline versions of A. Either case may result in no out-of-line copy of the inlined function being generated. The compiler reorders the functions within a module to get the best inlining result. Functionally, the code is the same, but this affects the resulting assembly file.
When global asm
statements are used with the module, between the function definitions, the compiler cannot do this reordering process, because the asm statement might be affecting the behavior of the assembly code that is generated from the following C function definitions. Because of this, global asm
statements can greatly reduce the compiler’s ability to inline a function call.
Inlining and Sections
Before inlining, the compiler checks any section directives or pragmas on the function definitions. For example,
section("secA") inline int add(int a, int b) { return a + b; }
section("secB") int times_two(int a) { return add(a, a); }
Since add()
and times_two()
are to be generated into different code sections, this call is ignored during the inlining process, so the call is not inlined. If the callee is marked with
#pragma always_inline , however, or the
switch is in force, the compiler inlines the call despite the mismatch in sections.
Inlining and Run-Time Checking
When run-time checking is enabled, the compiler generates the additional code for the checks when the function is first defined. The implications for function inlining are as follows:
• When a function defined with run-time checking enabled is inlined into a function without run-time checking enabled, the inlined version still includes the run-time checks.
• When a function defined with run-time checking disabled is inlined into a function with run-time checking enabled, the inlined version does not acquire any run-time checks.
For more information, see
.
Variable Argument Macros
This ISO/IEC 9899:1999 C standard feature is enabled as an extension in C89 mode and in C++ mode. The final parameter in a macro declaration may be an ellipsis (
...
) to indicate the parameter stands for a variable number of arguments. In the replacement text for the macro, the predefined name
__VA_ARGS__
represents the parameters that were supplied for the ellipsis in the macro invocation. At least one argument must be provided for the ellipsis, in an invocation.
For example:
CrossCore Embedded Studio 2.2.0
2–119
C/C++ Compiler Language Extensions
#define tracec99(file,line,...) logmsg(file,line, __VA_ARGS__) can be used with differing numbers of arguments: the following statements: tracec99("a.c", 999, "one", "two", "three"); tracec99("a.c", 999, "one", "two"); tracec99("a.c", 999, "one"); tracec99("a.c", 999); expand to the following code: logmsg("a.c", 999, "one", "two", "three"); logmsg("a.c", 999, "one", "two"); logmsg("a.c", 999, "one"); logmsg("a.c", 999, ); // error - must provide an argument
NOTE: This variable argument macro syntax comes from the ISO/IEC 9899:1999 C standard. The compiler sup-
ports C99 variable argument macro formats in C89, C99 and C++ modes. See GCC Variable Argument
for additional information related to GNU compatibility support.
Restricted Pointers
The restrict
keyword is a standard feature of the ISO/IEC 9899:1999 C standard. The keyword is available as an extension in C89 and C++ modes.
The use of restrict
is limited to the declaration of a pointer and specifies that the pointer provides exclusive initial access to the object to which it points. More simply, the restrict
keyword is a way to identify that a pointer does not create an alias. Also, two different restricted pointers cannot designate the same object, and therefore, they are not aliases.
The compiler is free to use the information about restricted pointers and aliasing to better optimize C/C++ code that uses pointers. The restrict
keyword is most useful when applied to function parameters about which the compiler would otherwise have little information.
For example,
void fir(short *in, short *c, short *restrict out, int n)
The behavior of a program is undefined if it contains an assignment between two restricted pointers, except for the following cases:
• A function with a restricted pointer parameter may be called with an argument that is a restricted pointer.
• A function may return the value of a restricted pointer that is local to the function, and the return value may then be assigned to another restricted pointer.
If a program uses a restricted pointer in a way that it does not uniquely refer to storage, then the behavior of the program is undefined.
2–120
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Language Extensions
Variable-Length Array Support
The compiler supports variable-length automatic arrays. This ISO/IEC 9899:1999 standard feature is also allowed as an extension in C89 mode. For more information, see
. Variable-length arrays are not supported in C++
mode.
Unlike other automatic arrays, variable-length arrays are declared with a non-constant length. This means that the space is allocated when the array is declared, and deallocated when the brace-level is exited. The compiler does not allow jumping into the brace-level of the array, and produces a compilation error message if this is attempted.
The compiler does allow breaking or jumping out of the brace-level, and it deallocates the array when this occurs.
You can use variable-length arrays as function arguments, such as: void tester (int len, char data[len][len])
{
/* code using data[][] */
}
The variable used for the array length must be in scope, and must have been previously declared.
The compiler calculates the length of an array at the time of allocation. It then remembers the array length until the brace-level is exited and can return it as the result of the sizeof()
function performed on the array.
Because variable-length arrays must be stored on the stack, it is impossible to have variable-length arrays in program memory. The compiler issues an error if an attempt is made to use a variable-length array in pm
.
As an example, if you were to implement a routine for computation of a product of three matrices, you need to allocate a temporary matrix of the same size as the input matrices. Declaring an automatic variable-size matrix is more convenient than allocating it from a heap. Note, however, that variable-length arrays are allocated on the stack, which means that sufficient stack space must be available.
The expression declares an array with a size that is computed at run time. The length of the array is computed on entry to the block and saved in case the sizeof()
operator is used to determine the size of the array. For multidimensional arrays, the boundaries are also saved for address computation. After leaving the block, all the space allocated for the array is deallocated. For example, the following program prints 10, not 50.
main ()
{
foo(10);
} void foo (int n)
{
char c[n];
n = 50;
printf("%d", sizeof(c));
}
CrossCore Embedded Studio 2.2.0
2–121
C/C++ Compiler Language Extensions
Non-Constant Initializer Support
The compiler does not require the elements of an aggregate initializer for an automatic variable to be constant expressions. This is a standard feature of the ISO/IEC 9899:1999 C standard and the ISO/IEC 14882:2003 C++ standard. The compiler supports it as an extension in C89 mode.
The following example shows an initializer with elements that vary at runtime.
void initializer (float a, float b)
{
float the_array[2] = { a-b, a+b };
} void foo (float f, float g)
{
float beat_freqs[2] = { f-g, f+g };
}
Designated Initializers
This is a standard feature of the ISO/IEC 9899:1999 C standard. The compiler supports it as an extension in C89 and C++ modes. This feature lets you specify the elements of an array or structure initializer in any order by specifying their designators - the array indices or structure field names to which they apply. All designators must be constant expressions, even in automatic arrays. For an array initializer, the syntax
[INDEX]
appearing before an initializer element value specifies the index initialized by that value. Subsequent initializer elements are then applied to the sequentially following elements of the array, unless another use of the
[INDEX]
syntax appears. The index values must be constant expressions, even when the array being initialized is automatic.
The following example shows equivalent array initializers-the first in C89 form (without using the extension) and the second in C99 form, using the designators. Note that the
[INDEX]
designator precedes the value being assigned to that element.
/* Example 1 C Array Initializer */
/* C89 array initializer (no designators) */ int a[6] = { 0, 0, 15, 0, 29, 0 };
/* Equivalent C99 array initializer (with designators) */ int a[6] = { [4] 29, [2] 15 };
You can combine this technique of designated elements with initialization of successive non-designated elements.
The two instructions below are equivalent. Note that any non-designated initial value is assigned to the next consecutive element of the structure or array.
/* Example 2 Mixed Array Initializer */
/* C89 array initializer (no designators) */ int a[6] = { 0, v1, v2, 0, v4, 0 };
/* Equivalent C99 array initializer (with designators) */ int a[6] = { [1] v1, v2, [4] v4 };
2–122
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Language Extensions
The following example shows how to label the array initializer elements when the designators are characters or enum type.
/* Example 3 C Array Initializer With enum Type Indices */
/* C99 C array initializer (with designators) */ int whitespace[256] =
{
[' '] 1, ['\t'] 1, ['\v'] 1, ['\f'] 1, ['\n'] 1, ['\r'] 1
}; enum { e_ftp = 21, e_telnet = 23, e_smtp = 25, e_http = 80, e_nntp = 119 }; char *names[] = {
[e_ftp] "ftp",
[e_http] "http",
[e_nntp] "nntp",
[e_smtp] "smtp",
[e_telnet] "telnet"
};
In a structure initializer, specify the name of the field to initialize with fieldname: before the element value. The C89 and C99 struct initializers in the example below are equivalent.
/* Example 4 struct Initializer */
/* C89 struct Initializer (no designators) */ struct point {int x, y;}; struct point p = {xvalue, yvalue};
/* Equivalent C99 struct Initializer (with designators) */ struct point {int x, y;}; struct point p = {y: yvalue, x: xvalue};
Hexadecimal Floating-Point Numbers
This is a standard feature of the ISO/IEC:9899 1999 C standard. The compiler supports this as an extension in C89 mode and in C++ mode.
Hexadecimal floating-point numbers have the following syntax.
hexadecimal-floating-constant:
{0x|0X} hex-significand binary-exponent-part [ floating-suffix ] hex-significand: hex-digits [ . [ hex-digits ]] binary-exponent-part: {p|P} [+|-] decimal-digits floating-suffix: { f | l | F | L }
The hex-significand
is interpreted as a hexadecimal rational number. The digit sequence in the exponent part is interpreted as a decimal integer. The exponent indicates the power of two by which the significand is to be scaled.
The floating suffix has the same meaning that it has for decimal floating constants: a constant with no suffix is of type double
, a constant with suffix
F
is of type float
, and a constant with suffix
L
is of type long double
.
Hexadecimal floating constants enable the programmer to specify the exact bit pattern required for a floating-point constant. For example, the declaration:
CrossCore Embedded Studio 2.2.0
2–123
C/C++ Compiler Language Extensions
float f = 0x1p-126f; causes f
to be initialized with the value
0x800000
.
Declarations Mixed With Code
In C89 mode, the compiler accepts declarations placed in the middle of code. This allows the declaration of local variables to be placed at the point where they are required. Therefore, the declaration can be combined with initialization of the variable. This is a standard feature of the ISO/IEC 9899:1999 C standard and the ISO/IEC
14882:2003 C++ standard.
For example, in the following function the declaration of d
is delayed until its initial value is available, so that no variable is uninitialized at any point in the function.
void func(Key k) {
Node *p = list; while (p && p->key != k)
p = p->next; if (!p)
return;
Data *d = p->data; while (*d)
process(*d++);
}
Compound Literals
This is a standard feature of the ISO/IEC:9899 1999 standard. The compiler supports it as an extension in C89 mode. It is not allowed in C++ mode.
The following example shows an ISO/IEC 9899:1990 standard C struct
usage, followed by an equivalent
ISO/IEC 9899:1999 standard C code that has been simplified using a compound literal.
/* C89/C++ Constructor struct */
/* Standard C struct */ struct foo {int a; char b[2];}; struct foo make_foo(int x, char *s)
{
struct foo temp;
temp.a = x;
temp.b[0] = s[0];
if (s[0] != '\0')
temp.b[1] = s[1];
else
temp.b[1] = '\0';
return temp;
}
/* Equivalent C99 constructor struct */ struct foo make_foo(int x, char *s)
2–124
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Language Extensions
{
return((struct foo) {x, {s[0], s[0] ? s[1] : '\0'}});
}
C++ Style Comments
The compiler accepts C++ style comments in C programs, beginning with
//
and ending at the end of the line.
This is essentially compatible with standard C, except for the following case. a = b
//* highly unusual */ c
; which a standard C compiler processes as: a = b / c; and a C++ compiler and cc21k
process as: a = b;
Enumeration Constants That Are Not int Type
The compiler allows enumeration constants to be integer types other than int
, such as unsigned int
, long long
, or unsigned long long
. See Enumeration Type Implementation Details
for more information.
Boolean Type
The compiler supports a Boolean data type bool
, with values true
and false
. This is a standard feature of the
ISO/IEC 14882:2003 C++ standard, and is available as a standard feature in the ISO/IEC 9899:1999 C standard when the stdbool.h
header is included. It is supported as an extension in C89 mode, and as an extension in C99 mode when the stdbool.h
header has not been included.
The bool
keyword is a unique signed integral type. There are two built-in constants of this type: true
and false
.
When converting a numeric or pointer value to bool
, a zero value becomes false,
and a non-zero value becomes true
. A bool
value may be converted to int
by promotion, taking true
to one and false
to zero. A numeric or pointer value is converted automatically to bool
when needed.
The fract Native Fixed-Point Type
The compiler has support for the native fixed-point type fract
, as defined by Chapter 4 of the Extensions to support
embedded processors ISO/IEC draft technical report TR 18037. This support is available for the C language only. A discussion of how to use this support is given in
Using Native Fixed-Point Types
.
Inline Assembly Language Support Keyword (asm)
The cc21k asm()
construct is used to code ADSP-21xxx/SCxxx assembly language instructions within a C/C++ function. The asm()
construct is useful for expressing assembly language statements that cannot be expressed easily or efficiently with C/C++ constructs.
CrossCore Embedded Studio 2.2.0
2–125
C/C++ Compiler Language Extensions
Use asm()
to code complete assembly language instructions and specify the operands of the instruction using C/C+
+ expressions. When specifying operands with a C/C++ expression, you do not need to know which registers or memory locations contain C/C++ variables.
NOTE: The compiler does not analyze code defined with the asm()
construct; it passes this code directly to the assembler. The compiler does perform substitutions for operands of the formats %0 through %9. However, it passes everything else through to the assembler without reading or analyzing it. It means that the compiler cannot apply any enabled workarounds for silicon errata that may be triggered either by the contents of the asm construct, or by the sequence of instructions formed by the asm()
construct and the surrounding code produced by the compiler. asm()
constructs are executable statements, and as such, may not appear before declarations within C/C++ functions in MISRA-C mode. asm()
constructs may also be used at global scope, outside function declarations. Such asm()
constructs are used to pass declarations and directives directly to the assembler. They are not executable constructs, and may not have any inputs or outputs, or affect any registers. In addition, when optimizing, the compiler sometimes changes the order in which generated functions appear in the output assembly file. However, if global-scope asm constructs are placed between two function definitions, the compiler ensures that the function order is retained in the generated assembly file. Consequently, function inlining may be inhibited.
An asm()
construct without operands takes the form of:
asm("nop;");
The complete assembly language instruction, enclosed in quotes, is the argument to asm()
.
NOTE: The compiler generates a label before and after inline assembly instructions when generating debug code
switch). These labels are used to generate the debug line information used by the debugger. If the
inline assembler inserts conditionally assembled code, an undefined symbol error is likely to occur at link time. For example, the following code could cause undefined symbols if
MACRO
is undefined:
asm("#ifdef MACRO");
asm(" // assembly statements");
asm("#endif");
If the inline assembler changes the current section and thus causes the compiler labels to be placed in another section, such as a data section (instead of the default code section), then the debug line information is incorrect for these lines.
Using asm()
constructs with operands requires some additional syntax described in the following sections.
•
•
Assembly Construct Operand Description
•
Using long long Types in asm Constraints
•
Assembly Constructs With Multiple Instructions
•
Assembly Construct Reordering and Optimization
•
Assembly Constructs With Input and Output Operands
2–126
CrossCore Embedded Studio 2.2.0
Inline Assembly Language Support Keyword (asm)
•
Assembly Constructs With Compile-Time Constants
•
Assembly Constructs and Flow Control
•
Guidelines on the Use of asm() Statements
asm() Construct Syntax
Use asm()
constructs to specify the operands of the assembly instruction using C/C++ expressions. You do not need to know which registers or memory locations contain C/C++ variables. Use the following general syntax for your asm()
constructs.
asm [volatile] (
template
[:[constraint(output operand)[,constraint(output operand)…]]
[:[constraint(input operand)[,constraint(input operand)…]]
[:clobber string]]]
);
The syntax elements are:
• template
The template is a string containing the assembly instruction(s) with
%number
indicating where the compiler should substitute the operands. Operands are numbered in order of appearance from left to right, starting at 0.
Each instruction should end with a semicolon, and enclose the entire string within double quotes. For more information on templates containing multiple instructions, see
Assembly Constructs With Multiple Instructions .
• constraint
The constraint string directs the compiler to use certain groups of registers for the input and output operands.
Enclose the constraint string within double quotes. For more information on operand constraints, see
.
• output operand
The output operands are the names of C/C++ variables that receive output from corresponding operands in the assembly instructions.
• input operand
The input operand is a C/C++ expression that provides an input to a corresponding operand in the assembly instruction.
• clobber string
The clobber string notifies the compiler that a list of registers are overwritten by the assembly instructions. Enclose each name within double quotes, and separate each quoted register name with a comma. The input and output operands are guaranteed not to use any of the clobbered registers, so you can read and write the clobbered registers as often as you like. See the Register Names for asm() Constructs table in
CrossCore Embedded Studio 2.2.0
2–127
asm() Construct Syntax
for the list of individual registers. See the
Clobbered Register Sets table (in
) for the list of register sets.
It is vital that any register overwritten by an assembly instruction and not allocated by the constraints is included in the clobber list. The list must include memory if an assembly instruction accesses memory.
asm() Construct Syntax Rules
These rules apply to assembly construct template syntax.
• The template is the only mandatory argument to asm()
. All other arguments are optional.
• An operand constraint string followed by a C/C++ expression in parentheses describes each operand. For output operands, it must be possible to assign to the expression; that is, the expression must be legal on the left side of an assignment statement.
• A colon separates:
• The template from the first output operand
• The last output operand from the first input operand
• The last input operand from the clobbered registers
• A space must be added between adjacent colon field delimiters in order to avoid a clash with the C++
“::” reserved global resolution operator.
• A comma separates operands and registers within arguments.
• The number of operands in arguments must match the number of operands in your template.
• The maximum permissible number of operands is ten (%0, %1, %2, %3, %4, %5, %6, %7, %8, and %9).
NOTE: The compiler cannot check whether the operands have data types that are reasonable for the instruction being executed. The compiler does not parse the assembler instruction template, does not interpret the template, and does not verify whether the template contains valid input for the assembler.
asm() Construct Template Example
The following example shows how to apply the asm()
construct template to the SHARC assembly language assignment instruction.
{
int result, x;
asm (
"%0 = %1;" :
"=d" (result) :
"d" (x)
);
}
In the above example:
2–128
CrossCore Embedded Studio 2.2.0
Inline Assembly Language Support Keyword (asm)
j l k
• The template is
"%0 = %1;"
. The
%0
is replaced with operand zero ( result
), the %1 is replaced with operand one
(x)
.
• The output operand is the C/C++ variable result
. The letter d
is the operand constraint for the variable.
This constrains the output to a data register
R{0-15}
. The compiler generates code to copy the output from the
R
register to the variable result
, if necessary. The
=
in
=d
indicates that the operand is an output.
• The input operand is the C/C++ variable x
. The letter d
in the operand constraint position for this variable constrains x
to a data register
R{0-15}
. If x
is stored in a different kind of register or in memory, the compiler generates code to copy the value into an
R
register before the asm()
construct uses it.
Assembly Construct Operand Description
The second and third arguments to the asm()
construct describe the operands in the assembly language template.
Several pieces of information must be conveyed for the compiler to know how to assign registers to operands. This information is conveyed with an operand constraint. The compiler needs to know what kind of registers the assembly instructions can operate on, so it can allocate the correct register type.
You convey this information with a letter in the operand constraint string that describes the class of allowable registers.
The asm() Operand Constraints table describes the correspondence between constraint letters and register classes.
NOTE: The use of any letter not listed in the asm() Operand Constraints table results in unspecified behavior.
The compiler does not check the validity of the code by using the constraint letter.
f
F d e
I h a b c
Table 2-34: asm() Operand Constraints
Constraint Register Type
DAG2 B registers
Q2 R registers
Q3 R registers
All R registers
DAG2 L registers
Floating-point registers
Accumulator register
DAG1 B registers
64-bit R register pair
DAG1 L registers
Q1 R registers
Q4 R registers
Register
B8 - B15
R4 - R7
R8 - R11
R0 - R15
L8 - L15
F0 - F15
MRF, MRB
B0 - B7
R0 - R15 (For more information, see Using long long
L0 - L7
R0 - R3
R12 - R15
CrossCore Embedded Studio 2.2.0
2–129
Inline Assembly Language Support Keyword (asm)
w x r u y z n
=&constraint
=constraint
&constraint
?constraint
+constraint
#constraint
Table 2-34: asm() Operand Constraints (Continued)
Constraint Register Type Register
General registers
User registers
DAG1 I registers
DAG1 M registers
DAG2 I registers
R0 - R15
USTAT1 - USTAT4
I0 - I7
M0 - M7
I8 - I15
DAG2 M registers M8 - M15
None. For more information, see
Assembly Constructs With Compile-Time Constants .
Indicates that the constraint is applied to an output operand that may not overlap an input operand.
Indicates that the constraint is applied to an output operand.
Indicates the constraint is applied to an input operand that may not be overlapped with an output operand.
Indicates the constraint is temporary.
Indicates the constraint is both an input and output operand.
Indicates that the constraint is an input operand whose value is changed.
To assign registers to the operands, cc21k
must also be informed which operands in an assembly language instruction are inputs, which are outputs, and which outputs may not overlap inputs.
The compiler is told this in three ways, such as:
• The output operand list appears as the first argument after the assembly language template. The list is separated from the assembly language template with a colon. The input operands are separated from the output operands with a colon and always follow the output operands.
• The operand constraints (in the asm() Operand Constraints table) describe which registers are modified by an assembly language instruction. The “=” in =constraint indicates that the operand is an output; all output operand constraints must use =. Operands that are input-outputs must use “+”. (See below.)
• The compiler may allocate an output operand in the same register as an unrelated input operand, unless the output or input operand has the & constraint modifier. This is because cc21k
assumes that the inputs are consumed before the outputs are produced. This assumption may be false if the assembler code actually consists of more than one instruction. In such a case, use =& for each output operand that must not overlap an input.
Operand constraints indicate what kind of operand they describe by means of preceding symbols. The possible preceding symbols are: no symbol, =, +, &, ?, and #.
• (no symbol)
2–130
CrossCore Embedded Studio 2.2.0
Inline Assembly Language Support Keyword (asm)
The operand is an input. It must appear as part of the third argument to the asm()
construct. The allocated register is loaded with the value of the C/C++ expression before the asm()
template is executed. Its C/C++ expression is not modified by the asm()
, and its value may be a constant or literal.
Example: d
• = symbol
The operand is an output. It must appear as part of the second argument to the asm()
construct. Once the asm()
template has been executed, the value in the allocated register is stored into the location indicated by its
C/C++ expression; therefore, the expression must be one that would be valid as the left-hand side of an assignment.
Example: =d
• + symbol
The operand is both an input and an output. It must appear as part of the second argument to the asm() construct. The allocated register is loaded with the C/C++ expression value, the asm()
template is executed, and then the allocated register’s new value is stored back into the C/C++ expression. Therefore, as with pure outputs, the C/C++ expression must be one that is valid on the left-hand side of an assignment.
Example: +d
• ? symbol
The operand is temporary. It must appear as part of the third argument to the asm()
construct. A register is allocated as working space for the duration of the asm()
template execution. The register’s initial value is undefined, and the register’s final value is discarded. The corresponding C/C++ expression is not loaded into the register, but must be present. This expression is normally specified using a literal zero.
Example: ?d
• & symbol
This operand constraint may be applied to inputs and outputs. It indicates that the register allocated to the input (or output) may not be one of the registers that are allocated to the outputs (or inputs). This operand constraint is used when one or more output registers are set while one or more inputs are still to be referenced.
(This situation sometimes occurs if the asm()
template contains more than one instruction.)
Example: &d
• # symbol
The operand is an input, but the register's value is clobbered by the asm()
template execution. The compiler may make no assumptions about the register’s final value. An input operand with this constraint will not be allocated the same register as any other input or output operand of the asm()
. The operand must appear as part of the second argument to the asm()
construct.
Example: #d
CrossCore Embedded Studio 2.2.0
2–131
Inline Assembly Language Support Keyword (asm)
The
asm() Operand Constraints table lists the registers that may be allocated for each register constraint letter. The use of any letter not listed in the
Constraint column of the table results in unspecified behavior. The compiler does not check the validity of the code by using the constraint letter. The
Register Names for asm() Constructs table lists the registers that may be named as part of the clobber list.
Table 2-35: Register Names for asm() Constructs
Clobber String Meaning
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13",
"r14", "r15"
General data registers
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13",
"f14", "f15"
Floating-point data registers
"i0", "i1", "i2", "i3", "i4", "i5", "i8", "i9", "i10", "i11", "i12", "i13", "i14", "i15" Index registers
"m0", "m1", "m2", "m3", "m4", "m8", "m9", "m10", "m11", "m12" Modifier registers
"b0", "b1", "b2", "b3", "b4", "b7", "b8", "b9", "b10", "b11", "b12", "b13",
"b14", "b15"
Base registers
"l0", "l1", "l2", "l3", "l4", "l5", "l8", "l9", "l10", "l11", "l12", "l13", "l14", "l15" Length registers
"mrf", "mrb" Multiplier result registers
"acc", "mcc", "scc", "btf"
"lcntr"
Condition registers
Loop counter register
"px"
"ustat1", "ustat2", "ustat3", "ustat4"
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "s12", "s13",
"s14", "s15"
"msf", "msb"
"sacc", "smcc", "sscc", "sbtf"
"memory"
PX register
User-defined status registers
Shadow data registers
Shadow multiplier result registers
Shadow condition registers
Unspecified memory locations
It is also possible to claim registers directly, instead of requesting a register from a certain class using the constraint letters. You can claim the registers directly by simply naming the register in the location where the class letter would be. The register names are the same as those used to specify the clobber list, as shown in the
asm() Operand Con-
straints table.
For example, asm("%0 = %1 * %2;"
:"=r13"(result) /* output */
:"r14"(x),"r15"(y) /* input */
:”astat” /* clobber */
); loads x
into r14
, loads y
into r15
, executes the operation, and then stores the total from r13
back into result
.
2–132
CrossCore Embedded Studio 2.2.0
Inline Assembly Language Support Keyword (asm)
NOTE: Naming the registers in this way allows the asm()
construct to specify several registers that must be related, such as the DAG registers for a circular buffer. This also allows the use of registers not covered by the register classes accepted by the asm()
construct.
Using long long Types in asm Constraints
It is possible to use an asm()
constraint to specify a long long
value, in which case the compiler will claim a valid register pair. The syntax for operands within the template is extended to allow the suffix
“H”
for the high-numbered register of the register pair, and the suffix
“L”
for the low-numbered register of the pair. A long long
type is represented by the constraint letter
“I”
. Note that when using a char-size of 8 bits, the high-numbered register contains the least-significant bits of the long long
value and the low-numbered register contains the most-significant bits, while when using a char-size of 32 bits, the high-numbered register contains the most-significant bits of the long long
value and the low-numbered register contains the least-significant bits.
For example, long long int res; int main(void) {
long long result64, x64 = 123;
asm(
"%0H = %1H; %0L = %1L;" :
"=I" (result64) :
"I" (x64)
);
res = result64;
}
In this example, the template is
“%0H=%1H; %0L=%1L;”
. When compiling with a char-size of 32 bits, the
%0H
is replaced with the register containing the least-significant 32 bits of operand zero ( result64
), and
%0L
is replaced with the register containing the most-significant 32 bits of operand zero ( result64
). Similarly,
%1H
and
%1L
are replaced with the registers containing the least-significant 32 bits and most-significant 32 bits, respectively, of operand one (x64).
Assembly Constructs With Multiple Instructions
There can be many assembly instructions in one template. Normal rules for line-breaking apply. In particular, the statement may spread over multiple lines. You are recommended not to split a string over more than one line, but to use the C language’s string concatenation feature. If you are placing the inline assembly statement in a preprocessor macro, see
The following listing is an example of multiple instructions in a template.
/* (pseudo code) r7 = x; r6 = y; result = x + y; */ asm (“r7=%1;”
"r6=%2;"
"%0=r6+r7;"
: "=d" (result) /* output */
: "d" (x), "d" (y) /* input */
CrossCore Embedded Studio 2.2.0
2–133
Inline Assembly Language Support Keyword (asm)
: "r7", "r6", “astat”); /* clobbers */
Do not attempt to produce multiple-instruction asm constructs via a sequence of single-instruction asm constructs, as the compiler is not guaranteed to maintain the ordering.
For example, the following should be avoided:
/* BAD EXAMPLE: Do not use sequences of single-instruction
** asms. Use a single multiple-instruction asm instead. */ asm("r7=%0;" : : "d" (x) : "r7"); asm("r6=%0;" : : "d" (y) : "r6"); asm("%0=r6+r7;" : "=d" (result) : “astat”);
Assembly Construct Reordering and Optimization
For the purpose of optimization, the compiler assumes that the side effects of an asm()
construct are limited to changes in the output operands. This does not mean that you cannot use instructions with side effects, but be careful to notify the compiler that you are using them by using the clobber specifiers.
The compiler may eliminate supplied assembly instructions if the output operands are not used, move them out of loops, or reorder them with respect to other statements, where there is no visible data dependency. Also, if your instruction does have a side effect on a variable that otherwise appears not to change, the old value of the variable may be reused later if it happens to be found in a register.
Use the keyword volatile
to prevent an asm()
instruction from being moved or deleted. For example,
asm volatile("idle;");
A sequence of asm volatile()
constructs is not guaranteed to be completely consecutive; it may be moved across jump instructions or in other ways that are not significant to the compiler. To force the compiler to keep the output consecutive, use only one asm volatile()
construct, or use the output of the asm() construct in a C/C++ statement.
Assembly Constructs With Input and Output Operands
When an asm construct has both inputs and outputs, there are two aspects to consider:
• Whether a value read from an input variable will be written back to the same variable or a different variable on output.
• Whether the input and output values will reside in the same register or different registers.
The most common case is when both input and output variables and input and output registers are different. In this case, the asm construct reads from one variable into a register, performs an operation which leaves the result in a different register, and writes that result from the register into a different output variable:
asm("%0 = %1;" : "=d" (newptr) : "d" (oldptr));
When the input and output variables are the same, it is usual that the input and output registers are also the same.
In this case, you use the “+” constraint:
2–134
CrossCore Embedded Studio 2.2.0
Inline Assembly Language Support Keyword (asm)
asm("%0 += 4;" : "+d" (sameptr));
When the input and output variables are different, but the input and output registers have to be the same (usually because of requirements of the assembly instructions), you indicate this to the compiler by using a different syntax for the input’s constraint. Instead of specifying the register or class to be used, you specify the output to which the input must be matched.
For example, asm("modify(%0,m7);"
:"=w" (newptr) // an output, given an I register,
// stored into newptr.
:"0" (oldptr)); // an input, given same reg as %0,
// initialized from oldptr
This specifies that the input oldptr
has 0 (zero) as its constraint string, which means it must be assigned the same register as %0 ( newptr
).
Assembly Constructs With Compile-Time Constants
The n
input constraint informs the compiler that the corresponding input operand should not have its value loaded into a register. Instead, the compiler is to evaluate the operand, and then insert the operand’s value into the assembly command as a literal numeric value. The operand must be a compile-time constant expression. For example, int r; int arr[100]; asm("%0 = %1;" : "=d" (r) : "d" (sizeof(arr))); // "d" constraint produces code similar to:
R0 = 100 (X); // compiler loads value into register
R1 = R0; // compiler replaces %1 with register whereas: int r; int arr[100]; asm("%0 = %1;" : "=d" (r) : "n" (sizeof(arr))); // "n" constraint produces code similar to:
R1 = 100; // compiler replaces %1 with value
If the expression is not a compile-time constant, the compiler gives an error: int r; int arr[100]; asm("%0 = %1;" : "=d" (r) : "n" (arr)); // error: operand
// for "n" constraint
// must be a compile-time constant
CrossCore Embedded Studio 2.2.0
2–135
Inline Assembly Language Support Keyword (asm)
Assembly Constructs and Flow Control
NOTE: Do not place flow control operations within an asm()
construct that “leaves” the asm()
construct, such as calling a procedure or performing a jump to another piece of code that is not within the asm()
construct itself. Such operations are invisible to the compiler, may result in multiple-defined symbols, and may violate assumptions made by the compiler.
For example, the compiler is careful to adhere to the calling conventions for preserved registers when making a procedure call. If an asm()
construct calls a procedure, the asm()
construct must also ensure that all conventions are obeyed, or the called procedure may corrupt the state used by the function containing the asm()
construct.
It is also inadvisable to use labels in asm()
statements, especially when function inlining is enabled. If a function containing such asm statements is inlined more than once in a file, there will be multiple definitions of the label, resulting in an assembler error. If possible, use PC-relative jumps in asm statements.
Guidelines on the Use of asm() Statements
There are certain operations that are performed more efficiently using other compiler features, and result in source code that is clearer and easier to read.
Accessing System Registers
System registers are accessed most efficiently using the functions in sysreg.h
instead of using asm()
statements.
For example, the following asm()
statement: asm("R0 = 0; bit tst MODE1 IRPTEN; if TF r0 = r0 + 1; %0 = r0;"
: "=d"(test) : : "r0"); can be written as:
#include <sysreg.h>
#include <def21160.h> test = sysreg_bit_tst(sysreg_MODE1, IRPTEN);
Refer to Access to System Registers for more information.
Accessing Memory-Mapped Registers (MMRs)
MMRs can be accessed using the macros in the
Cdef*.h
files (for example,
Cdef21160.h
) that are supplied with
CCES.
For example,
IOSTAT
can be accessed using asm()
statements, such as: asm("R0 = 0x1234567; dm(IOSTAT) = R0;" : : : "r0");
This can be written more cleanly and efficiently as:
#include <Cdef21160.h>
...
*pIOSTAT = 0x1234567;
2–136
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Language Extensions
Dual Memory Support Keywords (pm dm)
This section describes cc21k
language extension keywords to C and C++ that support the dual-memory space, modified Harvard architecture of the ADSP-21xxx and ADSP-SCxxx processors. There are two keywords used to designate memory space: dm
and pm
. They can be used to specify the location of a static or global variable or to qualify a pointer declaration.
The following rules apply to dual memory support keywords:
• The memory space keyword ( dm
or pm
) refers to the expression to the right of the keyword.
• You can specify a memory space for each level of pointer. This corresponds to one memory space for each
*
in the declaration.
• The compiler uses Data Memory (DM) as the default memory space for all variables. All undeclared spaces for data are Data Memory spaces.
• The compiler always uses Program Memory (PM) as the memory space for functions. Function pointers always point to Program Memory.
• You cannot assign memory spaces to automatic variables. All automatic variables reside on the stack, which is always in Data Memory.
• Literal character strings always reside in Data Memory.
The following listing shows examples of dual memory keyword syntax.
int pm buf[100];
/* declares an array buf with 100 elements in Program Memory */ int dm samples[100];
/* declares an array samples with 100 elements in Data Memory */ int points[100];
/* declares an array points with 100 elements in Data Memory */ int pm * pm xy;
/* declares xy to be a pointer which resides in Program
Memory and points to a Program Memory integer */ int dm * dm xy;
/* declares xy to be a pointer which resides in Data Memory and
points to a Data Memory integer */ int *xy;
/* declares xy to be a pointer which resides in Data Memory
and points to a Data Memory integer */ int pm * dm datp;
/* declares datp to be a pointer which resides in Data Memory
and points to a Program Memory integer */ int pm * datp;
/* declares datp to be a pointer which resides in Data Memory
and points to a Program Memory integer */ int dm * pm progd;
/* declares progd to be a pointer which resides in Program
Memory and points to a Data Memory integer */
CrossCore Embedded Studio 2.2.0
2–137
Dual Memory Support Keywords (pm dm)
int * pm progd;
/* declares progd to be a pointer which resides in Program
Memory and points to a Data Memory integer */ float pm * dm * pm xp;
/* declares xp as a pointer in Program Memory,
that points to a pointer in Data Memory,
which in turn points to a float back in Program Memory */
Memory space specification keywords cannot qualify type names and structure tags, but you can use them in pointer declarations. The following shows examples of memory space specification keywords in typedef
and struct statements.
/* Dual Memory Support Keyword typedef & struct Examples */ typedef float pm * PFLOATP;
/* PFLOATP defines a type which is a pointer to /
/* a float which resides in pm.*/ struct s {int x; int y; int z;}; static pm struct s mystruct={10,9,8};
/* Note that the pm specification is not used in */
/* the structure definition. The pm specification */
/* is used when defining the variable mystruct */
Memory Keywords and Assignments/Type Conversions
The compiler allows a value of type pointer to pm
to be assigned to a variable of type pointer to dm
. Moreover, when
switch is used, the compiler also allows a value of type pointer to
dm
to be assigned to a variable of type pointer to pm
. The same rules also apply when passing arguments to functions that take pointertyped parameters.
Care must be taken when such coversions are performed. The compiler assumes that a variable of type pointer to dm and a variable of type pointer to pm
can never point to the same object. It uses this assumption to generate more efficient code, by resolving the potential alias between two pointers. If you use values of both type pointer to pm
and pointer to dm
to reference the same underlying object, incorrect code may be generated, as the compiler is free to reorder any accesses to the object that use the different pointer types.
For example, suppose you have two stores in C and the pointers contain the same address:
*pm_ptr = v1;
*dm_ptr = v2;
In this case, the compiler may perform the two accesses in either order. This could result in the DM v2
store occurring before the PM v1
store and not as in the source if the pointers were the same address. While this is a contrived example, it is worth considering if mixing pm
and dm
pointers.
The following listings show a code segment with variables in different memory spaces being assigned and a code segment with illegal mixing of memory space assignments.
/* Legal Dual Memory Space Variable Assignment Example */
2–138
CrossCore Embedded Studio 2.2.0
Dual Memory Support Keywords (pm dm)
int pm x; int dm y; x = y; /* Legal code */
/* Illegal Dual Memory Space Type Cast Example */
/* when -compatible-pm-dm isn't used */ int pm *x; int dm *y; int dm a; x = y; /* Compiler will flag error cc0513 */ x = &a; /* Compiler will flag error cc0513 */
Memory Keywords and Function Declarations/Pointers
Functions always reside in Program Memory. Pointers to functions always point to Program Memory. The following listing shows some sample function declarations with pointers.
/* Dual Memory Support Keyword Function Declaration (With Pointers) Syntax Examples */ int * y(); /* function y resides in */
/* pm and returns a */
/* pointer to an integer */
/* which resides in dm */ int pm * y(); /* function y resides in */
/* pm and returns a */
/* pointer to an integer */
/* which resides in pm */ int dm * y(); /* function y resides in */
/* pm and returns a */
/* pointer to an integer */
/* which resides in dm */ int * pm * y(); /* function y resides in */
/* pm and returns a */
/* pointer to a pointer */
/* residing in pm that */
/* points to an integer */
/* which resides in dm */
Memory Keywords and Function Arguments
The compiler checks calls to prototyped functions for memory space specifications consistent with the function prototype. The following listing shows sample code that cc21k
flags as inconsistent use of memory spaces between a function prototype and a call to the function.
/* Illegal Dual Memory Support Keywords & Calls To Prototyped Functions */ extern int foo(int pm *);
/* declare function foo() which expects a pointer to an int residing in pm
as its argument and which returns an int */
CrossCore Embedded Studio 2.2.0
2–139
Dual Memory Support Keywords (pm dm)
int x; /* define int x in dm */ foo(&x); /* call function foo() */
/* using dm pointer (location of x) as the */
/* argument. cc21k FLAGS AS AN ERROR; this is an */
/* inconsistency between the function's */
/* declared memory space argument and function */
/* call memory space argument */
Memory Keywords and Macros
Using macros when making memory space specification for variables or pointers can make your code easier to maintain. If you must change the definition of a variable or pointer (moving it to another memory space), declarations that depend on the definition may need to be changed to ensure consistency between different declarations of the same variable or pointer.
To make changes of this type easier, you can use C/C++ preprocessor macros to define common memory spaces that must be coordinated. The following listing shows two code segments that are equivalent after preprocessing. The code segment guarded by
EASILY_CHANGED
lets you redefine the memory space specifications by redefining the macros
SPACE1
and
SPACE2
, and making it easy to redefine the memory space specifications at compile-time.
/* Dual Memory Support Keywords & Macros */
#ifdef EASILY_CHANGED
/* pm and dm can be easily changed at compile-time. */
#define SPACE1 pm
#define SPACE2 dm char SPACE1 * foo (char SPACE2 *); char SPACE1 * x; char SPACE2 y; x = foo(&y);
#else
/* not so easily changed. */ char pm * foo (char dm *); char pm * x; char dm y; x = foo(&y);
#endif
Memory Banks
By default, the compiler assumes that all memory may be accessed with equal performance, but this is not always the case: some parts of your application may be in faster internal memory, and other parts in slower, external memory.
The compiler supports the concept of memory banks to group code or data with equivalent performance characteristics. By providing this information to the compiler, you can improve the performance of your application.
2–140
CrossCore Embedded Studio 2.2.0
Memory Banks
Memory Banks Versus Sections
Note that memory banks are different from sections:
• Section is a "hard" directive, using a name that is meaningful to the linker. If the
.ldf
file does not map the named section, a linker error occurs.
• A memory bank is a "soft" informational characterization, using a name that is not visible to the linker. The compiler uses optimization to take advantage of the bank's performance characteristics. However, if the
.ldf
file maps the code or data to memory that performs differently, the application still functions (albeit with a possible reduction in performance).
Pragmas and Qualifiers
Memory banks may be referenced through both memory bank pragmas and memory bank qualifiers:
• Use memory bank pragmas to specify the memory banks used by all the code or data of a function. For example:
#pragma data_bank(bank_external) int *getptr(void) { return ptr2; }
• Use memory bank qualifiers to specify the memory bank referenced by individual variables. For example: int bank("bank_internal") *ptr1; int bank("bank_external") *ptr2;
Memory Bank Selection
The compiler applies the following process for determine which bank is being referenced.
Memory Banks for Code
The compiler uses the following process for deducing the memory bank which contains instructions:
1. If the function is immediately preceded by
#pragma code_bank(bank)
, then the function's instructions are considered to reside in memory bank bank
.
2. If the function is immediately preceded by
#pragma code_bank
or
#pragma code_bank()
, then the function's instructions are not considered to reside in any defined memory bank.
3. Otherwise, if
#pragma default_code_bank(defbank)
has been used in the compilation unit prior to the definition of the function, the function's instructions are considered to reside in memory bank defbank
.
4. Otherwise, the function's instructions are not considered to reside in any defined memory bank.
For more information, see
.
Memory Banks for Data
The compiler uses the following process for deducing which memory bank contains variables that are auto storage class:
CrossCore Embedded Studio 2.2.0
2–141
Memory Bank Selection
1. If the variable declaration includes a memory bank qualifier, for example,
int bank("bank") x; then the variable will be considered to reside in bank bank
.
2. Otherwise, if the function is immediately preceded by
#pragma stack_bank(bank)
, then the variable is considered to reside in memory bank bank
.
3. Otherwise, if the function is immediately preceded by
#pragma stack_bank
or
#pragma stack_bank()
, then the variable is not considered to reside in any memory bank.
4. Otherwise, if
#pragma default_stack_bank(defbank)
has been used in the compilation unit prior to the definition of the function, the variable is considered to reside in memory bank defbank
.
5. Otherwise, the variable is not considered to reside in any defined memory bank.
For more information, see
.
The compiler uses the following process for selecting the memory bank to contain static
variables defined within a function:
1. If the variable declaration includes a memory bank qualifier, for example,
static int bank("bank") x; then the variable will be considered to reside in bank bank
.
2. Otherwise, if the function is immediately preceded by
#pragma data_bank(bank)
, then the variable is considered to reside in memory bank bank
.
3. Otherwise, if the function is immediately preceded by
#pragma data_bank
or
#pragma data_bank()
, then the variable is not considered to reside in any memory bank.
4. Otherwise, if
#pragma default_data_bank(defbank)
has been used in the compilation unit prior to the definition of the function, the variable is considered to reside in memory bank defbank
.
5. Otherwise, the variable is not considered to reside in any defined memory bank.
For more information, see
.
The compiler uses the following process for selecting the memory bank to contain variables defined at global scope:
1. If the variable declaration includes a memory bank qualifier, for example,
int bank("bank") x; then the variable will be considered to reside in bank bank
.
2. Otherwise, if
#pragma default_data_bank(defbank)
has been used in the compilation unit prior to the definition of the variable, the variable is considered to reside in memory bank defbank
.
3. Otherwise, the variable is not considered to reside in any defined memory bank.
The identified memory bank is used for pointer dereferences. For example:
2–142
CrossCore Embedded Studio 2.2.0
Memory Banks
#pragma data_bank(bank_external)
int f(int *a, int *b) {
return *a + *b; // *a and *b both considered to be
} // loads from "bank_external"
For more information, see
.
Performance Characteristics
You can specify the performance characteristics of a memory bank. This will allow the compiler to generate optimal code when accessing the bank. You can specify the following characteristics:
• Cycles required to read the memory bank. Use
to specify this characteristic.
• Cycles required to write the memory bank. Use
to specify this characteristic.
• The maximum bit width supported by accesses to the memory bank. Use
) to specify this characteristic.
Memory Bank Kinds
Each memory bank has a defined kind. The memory bank kinds supported on SHARC processors are listed in the
Memory Bank Kinds table. Not all kinds are available on all processors.
Table 2-36: Memory Bank Kinds
Kind
internal external
Description
Corresponds to internal memory
Corresponds to memory that is external to the processor.
Predefined Banks
The compiler predefines a memory bank for each supported memory bank kind, using the same name but with a bank_
prefix. For example, the following uses the internal and external memory banks:
#pragma code_bank("bank_external")
int next_counter(void) {
static int bank("bank_internal") counter;
return counter++;
}
These predefined memory banks have predefined performance characteristics, such as read and write cycle counts, that are appropriate for the kind of memory. You can override these performance characteristics via pragmas. For more information, see
The memory bank kinds are listed in the Memory Bank Kinds table (see
CrossCore Embedded Studio 2.2.0
2–143
Memory Banks
Defining Additional Banks
New memory banks are defined when first used, whether this happens in a memory bank pragma, or in a memory bank qualifier. When created, memory banks have kind internal
, unless otherwise specified by
#pragma memory_bank_kind
.
The compiler does not attach any significance to the name of any new memory banks you create.
Placement Support Keyword (section)
The section
keyword directs the compiler to place an object or function in an assembly
.SECTION
of the compiler's intermediate output file. You name the assembly
.SECTION
directive with the section()
's string literal parameter. If you do not specify a section()
for an object or function declaration, the compiler uses a default section.
For information on the default sections, see Memory Section Usage .
Applying section()
is meaningful only when the data item is something that the compiler can place in the named section. Apply section()
only to top-level, named objects that have a static duration, are explicitly static
, or are given as external-object definitions.
The following example shows the definition of a static variable that is placed in the section called bingo
.
static section("bingo") int x;
The section()
keyword has the limitation that section initialization qualifiers cannot be used within the section name string. The compiler may generate labels containing this string, which will result in assembly syntax errors.
Additionally, the keyword is not compatible with any pragmas that precede the object or function. For finer control over section placement and compatibility with other pragmas, use
#pragma section
.
Refer to #pragma section/#pragma default_section for more information.
NOTE: Note that section has replaced the segment
keyword in earlier releases of the compiler. Although the segment()
keyword is supported by the compiler of the current release, we recommend that you revise legacy code.
Placement of Compiler-Generated Code and Data
If the section()
keyword (
Placement Support Keyword (section) ) is not used, the compiler emits code and data
into default sections. The
-section
-sectionid=section_name[, id=section_name...]
be used to specify alternatives for these defaults on the command-line, and the default_section
pragma ( #pragma section/#pragma default_section
) can be used to specify alternatives for some of them within the source file.
In addition, when using certain features of C/C++, the compiler may be required to produce internal data structures.
The
-section
switch and the default_section
pragma allow you to override the default location where the data would be placed. For example,
cc21k -section vtbl=vtbl_data test.cpp -c++
2–144
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Language Extensions
would instruct the compiler to place all the C++ virtual function look-up tables into the section vtbl_data
, rather than the default vtbl
section. It is the user's responsibility to ensure that appropriately named sections exist in the
.ldf
file.
When both
-section
switches and default_section
pragmas are used, the default_section pragmas take priority.
Long Identifiers
The compiler supports C identifiers of up to 1022 characters in length; C++ identifiers typically have a slightly shorter limit, as the limit applies to the identifier after name mangling is used to transform it into a suitable symbol for linking, and for C++, some of the symbol space is required to represent the identifier's type.
Preprocessor Generated Warnings
The preprocessor directive
#warning
causes the preprocessor to generate a warning and continue preprocessing.
The text on the remainder of the line that follows
#warning
is used as the warning message.
Compiler Built-In Functions
The compiler supports built-in functions (sometimes called intrinsics) that enable efficient use of hardware resources. Knowledge of these functions is built into the cc21k
compiler. Your program uses them via normal function call syntax. The compiler notices the invocation and generates one or more machine instructions, just as it does for normal operators, such as
+
and
*
.
Built-in functions have names which begin with
__builtin_
. Note the C standard reserves identifiers beginning with double underlines (
__
), so these names do not conflict with user program identifiers.
This section describes:
•
•
•
Circular Buffer Built-In Functions
•
Compiler Performance Built-In Functions
•
Floating-Point Built-in Functions
•
•
•
Exclusive Transaction Built-In Functions
•
The cc21k
compiler provides built-in versions of some C library functions as described in
Using Compiler Built-In
C Library Functions of the C/C++ Library Manual for SHARC Processors.
CrossCore Embedded Studio 2.2.0
2–145
Compiler Built-In Functions
builtins.h
The builtins.h
header file defines prototypes for all built-in functions supported by the compiler; include this header file in any module that invokes a built-in function.
The header file also defines short names for each built-in function: for each built-in function
__builtin_func()
, the header file defines the short name
func()
. These short names can be disabled selectively or en masse, by defining macros prior to include the header file. The Macros controlling builtins.h table lists these macros.
Table 2-37: Macros controlling builtins.h
Macro Name Effect
__NO_SHORTNAMES
__SPECIFIC_NAMES
__ENABLE_FUNC
__DISABLE_FUNC
__DEFINED_FUNC
If defined, prevents any short names from being defined.
If defined, short name
func
will only be defined if corresponding macro
__ENABLE_FUNC
is defined.
Causes short name
func
to be defined, if
__SPECIFIC_NAMES
is also defined.
Prevents short name
func
from being defined.
Multiple-inclusion guard. The header file defines this macro when it defines short name
func
, but will not define short name
func
if this macro is already defined.
Access to System Registers
The sysreg.h
header file defines a set of functions that provide efficient system access to registers, modes, and addresses not normally accessible from C source. These functions are specific to individual architectures.
This section describes the functions that provide access to system registers. These functions are based on underlying hardware capabilities of the ADSP-21xxx/SCxxx processors. The functions are defined in the header file sysreg.h
.
They allow direct read and write access, as well as the testing and modifying of bit sets.
The functions are:
• int sysreg_read (const int SR_number);
The sysreg_read
function reads the value of the designated register and returns it.
• void sysreg_write (const int SR_number, const int new_value);
The sysreg_write
function stores the specified value in the nominated system register.
• void sysreg_write_nop (const int SR_number, const int new_value);
The sysreg_write_nop
function stores the specified value in the nominated system register, but also places a
NOP;
after the instruction.
• void sysreg_bit_clr (const int SR_number, const int bit_mask);
The sysreg_bit_clr
function clears all the bits of the nominated system register that are set in the supplied bit mask.
• void sysreg_bit_clr_nop (const int SR_number, const int bit_mask);
2–146
CrossCore Embedded Studio 2.2.0
Compiler Built-In Functions
The sysreg_bit_clr_nop
function clears all the bits of the nominated system register that are set in the supplied bit mask, but also places NOP; after the instruction.
• void sysreg_bit_set (const int SR_number, const int bit_mask);
The sysreg_bit_set
function sets all the bits of the nominated system register that are also set in the supplied bit mask.
• void sysreg_bit_set_nop (const int SR_number, const int bit_mask);
The sysreg_bit_set_nop
function sets all the bits of the nominated system register that are also set in the supplied bit mask, but also places
NOP;
after the instruction.
• void sysreg_bit_tgl (const int SR_number, const int bit_mask);
The sysreg_bit_tgl
function toggles all the bits of the nominated system register that are set in the supplied bit mask.
• void sysreg_bit_tgl_nop (const int SR_number, const int bit_mask);
The sysreg_bit_tgl_nop
function toggles all the bits of the nominated system register that are set in the supplied bit mask, but also places NOP; after the instruction.
• int sysreg_bit_tst (const int SR_number, const int bit_mask);
If all of the bits that are set in the supplied bit mask are also set in the nominated system register, the sysreg_bit_tst
function returns a non-zero value.
• int sysreg_tst (const int SR_number, const int value);
If the contents of the nominated system register are equal to the supplied value, the sysreg_tst
function returns a non-zero value.
NOTE: The
SR_number
parameters must be compile-time constants; sysreg.h
defines suitable macros. The effect of using the incorrect function for the size of the register or using an undefined register number is undefined.
On all ADSP-21xxx/SCxxx processors, use the following system registers:
sysreg_IMASK sysreg_IMASKP
sysreg_ASTAT sysreg_STKY
sysreg_USTAT1 sysreg_USTAT2
sysreg_USTAT3 sysreg_USTAT4
sysreg_MODE1 sysreg_MODE2
sysreg_IRPTL sysreg_ASTATX
sysreg_STKYY sysreg_MMASK
sysreg_ASTATY sysreg_FLAGS
On the ADSP-211xx/212xx/213xx/214xx processors, the following system register is also available:
sysreg_LIRPTL
On the ADSP-215xx/SC5xx processors, the following system register is also available:
CrossCore Embedded Studio 2.2.0
2–147
Compiler Built-In Functions
sysreg_MODE1STK
Header files specific to each processor provide symbolic names for the individual bits in the system registers of the processor; for example, def21160.h
(for the ADSP-21160 processor) and def21469.h
(for the ADSP-21469 processor). Including the header platform_include.h
automatically includes the def21xxx.h
header for the processor for which the application is being compiled.
Circular Buffer Built-In Functions
The C/C++ compiler provides built-in functions that use the processor's circular buffer mechanisms. These functions provide automatic circular buffer generation, circular indexing, and circular pointer references.
Automatic Circular Buffer Generation
If optimization is enabled, the compiler automatically attempts to use circular buffer mechanisms where appropriate.
For example, void func(int *array, int n, int incr)
{
int i;
for (i = 0; i < n; i++)
array [ i % 10 ] += incr;
}
The compiler recognizes that the
[i % 10 ]
expression is a circular reference, and uses a circular buffer if possible.
There are cases where the compiler is unable to verify that the memory access is always within the bounds of the buffer. The compiler is conservative in such cases, and does not generate circular buffer accesses.
The compiler can be instructed to still generate circular buffer accesses even in such cases, by specifying the
force-circbuf
switch.
For more information, see
.
Circular Buffer Increment of an Index
The following operation performs a circular buffer increment of an index:
ptrdiff_t circindex(ptrdiff_t ptr, ptrdiff_t incr, size_t len);
The equivalent operation is: index += incr; if (index < 0)
index += len; else if (index >= len)
index -= len;
NOTE: Note that, for correct operation, the increment should not exceed the buffer length.
2–148
CrossCore Embedded Studio 2.2.0
Circular Buffer Built-In Functions
Circular Buffer Increment of a Pointer
The following operation provides a circular buffer increment of an pointer.
void *circptr(const void *ptr, ptrdiff_t incr,
const void *base, size_t buflen);
The equivalent operation is: ptr += incr; if (ptr < base)
ptr += buflen; else if (ptr >= (base+buflen))
ptr -= buflen;
NOTE: Note that, for correct operation, the increment should not exceed the buffer length.
For more information on circindex
and circptr
library functions, refer to the C/C++ Library Manual for
SHARC Processors.
The compiler also attempts to generate circular buffer increments for modulus array references, such as array[ index %nitems ]
. For this to happen, the compiler must be able to determine that the starting value for index is within the range
0...(nitems-1)
. When the
switch is specified, the compiler
always treats array references of the form
[i%n]
as a circular buffer operation on the array.
Compiler Performance Built-In Functions
The compiler performance built-in functions do not have any effect on the functional behavior of compiled code.
Instead, they provide the compiler with additional information about the code being compiled, allowing the compiler to generate more efficient code. The facilities are:
•
•
Expected Behavior
The expected_true
and expected_false
functions provide the compiler with information about the expected behavior of the program. You can use these built-in functions to tell the compiler which parts of the program are most likely to be executed; the compiler can then arrange for the most common cases to be those that execute most efficiently.
#include <processor_include.h> int expected_true(int cond); int expected_false(int cond);
For example, consider the code extern int func(int); int example(int call_the_function, int value)
{
CrossCore Embedded Studio 2.2.0
2–149
Compiler Performance Built-In Functions
int r = 0;
if (call_the_function)
r = func(value);
return r;
}
If you expect that parameter call_the_function
to be true in the majority of cases, you can write the function in the following manner: extern int func(int); int example(int call_the_function, int value)
{
int r = 0;
if (expected_true(call_the_function))
// indicate most likely true
r = func(value);
return r;
}
This indicates to the compiler that you expect call_the_function
to be true in most cases, so the compiler arranges for the most efficient case to be to call function func()
. If, on the other hand, you were to write the function as: extern int func(int); int example(int call_the_function, int value)
{
int r = 0;
if (expected_false(call_the_function))
// indicate most likely false
r = func(value);
return r;
} then the compiler arranges for the generated code to be most efficient for the opposite case, of not calling function func()
.
These built-in functions do not change the operation of the generated code, which will still evaluate the boolean expression as normal. Instead, they indicate to the compiler which flow of control is most likely, helping the compiler to ensure that the most commonly-executed path is the one that uses the most efficient instruction sequence.
The expected_true
and expected_false
built-in functions only take effect when optimization is enabled in the compiler. They are only supported in conditional expressions.
Known Values
The
__builtin_assert()
function provides the compiler with information about the values of variables which it may not be able to deduce from the context. For example, consider the code int example(int value, int loop_count)
{
int r = 0;
2–150
CrossCore Embedded Studio 2.2.0
Compiler Built-In Functions
int i;
for (i = 0; i < loop_count; i++) {
r += value;
}
return r;
}
The compiler has no way of knowing what values may be passed in to the function. If you know that the loop count will always be greater than four, you can allow the optimizer to make use of that knowledge using
__builtin_assert()
: int example(int value, int loop_count)
{
int r = 0;
int i;
__builtin_assert(loop_count > 4);
for (i = 0; i < loop_count; i++) {
r += value;
}
return r;
}
The optimizer can now omit the jump over the loop body it would otherwise have to emit to cover loop_count
== 0
. In more complicated code, further optimizations may be possible when bounds for variables are known.
Integral Built-in Functions
The compiler provides the following functions for operations on integral types, declared in builtins.h
:
• int avg(int x, int y);
Adds
x
and
y
, and divides the result by 2.
• int clip(int val, int range);
If
val
is greater than
|range|
, the function returns
|range|
, else if
val
is less than
-|range|
the function returns
-|range|
, else
val
is returned unchanged. This built-in function generates the clip val by
range
instruction.
Floating-Point Built-in Functions
The compiler provides the following functions for operations on floating-point types, declared in builtins.h
:
• float frecipsf(float val);
Creates a seed value for computing
1/val
, the reciprocal of
val
. This built-in function generates the recips instruction.
• float frsqrtsf(float val);
Creates a seed value for computing the reciprocal square root of val. This built-in function generates the rsqrts
instruction.
CrossCore Embedded Studio 2.2.0
2–151
Compiler Built-In Functions
• float fscalbf(float val, int amount);
Scales the exponent of
val
by adding
amount
to the exponent. This built-in function generates the scalb
val by amount
instruction.
• float faddabsf(float x, float y);
Adds
x
and
y
, then returns the absolute value of the sum.
• float favgf(float x, float y);
Adds
x
and
y
, and divides the result by 2.
• float fsubabsf(float x, float y);
Subtracts
y
from
x
, then returns the absolute value of the resulting amount.
• float faddsubf(float x, float y, float *z);
Subtracts
y
from
x
, storing the result in the location pointed to by z
, then returns the result of adding x
to y
.
• float fsignf(float x, float y);
Returns
x
if
y
is non-negative, and
-x
if
y
is negative. This built-in function generates the copysign instruction.
• float fclipf(float val, float range);
If
val
is greater than
|range|
, the function returns
|range|
, else if
val
is less than
-|range|
the function returns
-|range|
, else
val
is returned unchanged. This built-in function generates the clip val by
range
instruction.
• int conv_fix(float val);
Converts val
to an integer. This built-in function generates the fix instruction, so rounding is affected by the
MODE1
register.
• int conv_fix_by(float val, int amount);
Scales
val
by adding
amount
to
val
exponent, then converts the result to an integer. This built-in function generates the fix val by amount
instruction, so rounding is affected by the
MODE1
register.
• float conv_float_by(int val, int amount);
Converts
val
to a floating-point value, then scales that value by adding
amount
to the value's exponent. This built-in function generates the float val by amount
instruction, so rounding is affected by the MODE1 register.
• int conv_trunc_by(float val, int amount);
Scales
val
by adding
amount
to
val
's exponent, then converts the result to an integer. This built-in function generates the trunc val by amount
instruction; rounding always truncates towards zero.
• int logbf(float val);
2–152
CrossCore Embedded Studio 2.2.0
Compiler Built-In Functions
Returns the unbiased exponent of val
. This built-in function generates the logb
instruction.
• int mantf(float val);
Returns the mantissa of val
with the hidden bit reinstated. This built-in function generates the mant
instruction.
• int fpack(float val);
Returns an integer representing val
in 16-bit floating-point format. This built-in function generates the fpack
instruction.
• float funpack(int val);
Returns the float value corresponding to the 16-bit floating-point value represented in val
. This built-in function generates the funpack
instruction.
Fractional Built-In Functions
The SHARC compiler provides a set of fractional built-in functions, declared in builtins.h
. These built-in functions are:
• float conv_RtoF(int __a);
Converts a fractional value to floating point representation. This function is implemented by a float
instruction. Conversion from a fractional value to a floating-point value may result in some precision loss.
An alternative way to generate the same code is to use the fract
native fixed-point type. In this case, we can cast the fract
-typed argument to float
type without the need to use a built-in function. For more informa-
tion, refer to Using Native Fixed-Point Types .
• int conv_FtoR(float __a);
Converts a floating point value to a fractional representation. This function is implemented by a fix
instruction and does not saturate. Conversion of a floating point value that cannot be represented as a fractional value will return 0xFFFFFFFF.
Similar functionality is provided by the fract
native fixed-point type. In this case, we can simply cast the float
-typed argument to fract
type without the need to use a built-in function. The behavior of this cast differs from the built-in, in that it does not depend on the rounding mode specified in the
MODE1
register. For
more information, refer to Using Native Fixed-Point Types .
• int RxR(int __a, int __b);
Multiplies two fractional values, returning a fractional value. This function is implemented by a multiply instruction followed by a sat
instruction. The function will saturate. The operation
(-1)*(-1)
will return
0x7FFFFFFF
.
An alternative way to generate the same code is to use the fract
native fixed-point type. In this case, we multiply two fract
-typed arguments directly using the unbiased rounding mode, without the need to use a built-
in function. For more information, see Using Native Fixed-Point Types .
CrossCore Embedded Studio 2.2.0
2–153
Compiler Built-In Functions
• int RxItoI(int __a, int __b);
Multiplies a fractional value with an integral value, returning an integral value. This function is implemented as a multiply instruction followed by a sat
instruction.
Similar functionality is provided by the fract
native fixed-point type through use of the mulir
function (see
saturate.
• int RxItoR(int __a, int __b);
Multiplies a fractional value with an integral value, returning a fractional value. This function is implemented by a multiply instruction followed by a sat
instruction. This function will saturate. Any negative number that cannot be represented by fract will return
0x80000000
, and any positive number that cannot be represented will return
0x7FFFFFFF
.
An alternative way to generate the same code is to use the fract
native fixed-point type. In this case, we multiply a fract
-typed and an int
-typed argument directly without the need to use a built-in function. For more information, see
Using Native Fixed-Point Types
.
• int sat_add(int __a, int __b);
Adds two fractional values, returning a fractional value. This function will saturate. Any negative number that cannot be represented by fract will return
0x80000000
, and any positive number that cannot be represented will return
0x7FFFFFFF
.
An alternative way to generate the same code is to use the fract
native fixed-point type. In this case, we add two fract
-typed values directly without the need to use a built-in function. For more information, see
• int sat_sub(int __a, int __b);
Subtracts second fractional value argument from the first, returning a fractional value. This function will saturate. Any negative number that cannot be represented by fract will return
0x80000000
, and any positive number that cannot be represented will return
0x7FFFFFFF
.
An alternative way to generate the same code is to use the fract
native fixed-point type. In this case, we subtract two fract
-typed values directly without the need to use a built-in function. For more information, see
Using Native Fixed-Point Types .
• unsigned int leftz(unsigned int __a);
Returns the number of leading zeros in operand
__a
. This built-in function generates the leftz
instruction.
• unsigned int lefto(unsigned int __a);
Returns the number of leading ones in operand
__a
. This built-in function generates the lefto
instruction.
2–154
CrossCore Embedded Studio 2.2.0
Compiler Built-In Functions
Multiplier Built-In Functions
The SHARC compiler provides a set of built-in functions that can be used to directly generate instructions that target the multiplier registers
MRF
and
MRB
. These built-in functions use the acc80
type, which can be used to store the value of the multiplier registers but cannot be used to perform any arithmetic using standard C operators such as
+
,
-
or
*
.
The following built-in functions may be used to initialize acc80
values and are declared in builtins.h
:
• acc80 A_init(long long __a);
Returns an acc80
-typed result containing initialized to the value
__a
, suitable for use in subsequent multiplier built-in functions.
• acc80 A_zero(void);
Returns an acc80
-typed result containing the value zero, suitable for use in subsequent multiplier built-in functions.
• acc80 A_mr0(acc80 __a, unsigned int __b);
Sets the bottom 32 bits of
__a
to the value held in
__b
.
This built-in function generates a copy into either the
MR0F
or
MR0B
register.
• acc80 A_mr1(acc80 __a, unsigned int __b);
Sets bits 32 to 63 of
__a
to the value held in
__b
.
This built-in function generates a copy into either the
MR1F
or
MR1B
register.
• acc80 A_mr2(acc80 __a, int __b);
Sets bits 64 to 79 of __a to the value held in __b. This built-in function generates a copy into either the
MR2F or
MR2B
register.
The following built-in functions may be used to multiply two values to produce an acc80
result. They are declared in builtins.h
:
• acc80 A_mul_ssi(int __a, int __b);
Multiplies signed integers
__a
and
__b
to produce an acc80
-typed result. This built-in function generates the
(SSI)
variant of the multiply instruction.
• acc80 A_mul_sui(int __a, unsigned int __b);
Multiplies signed integer
__a
by unsigned integer
__b
to produce an acc80
-typed result. This built-in function generates the
(SUI)
variant of the multiply instruction.
• acc80 A_mul_usi(unsigned int __a, int __b);
Multiplies unsigned integer
__a
by signed integer
__b
to produce an acc80
-typed result. This built-in function generates the
(USI)
variant of the multiply instruction.
CrossCore Embedded Studio 2.2.0
2–155
Compiler Built-In Functions
• acc80 A_mul_uui(unsigned int __a, unsigned int __b);
Multiplies unsigned integers
__a
and
__b
to produce an acc80
-typed result. This built-in function generates the
(UUI)
variant of the multiply instruction.
• acc80 A_mul_ssf(int __a, int __b);
Multiplies signed fractional values
__a
and
__b
to produce an acc80
-typed result. This built-in function generates the
(SSF)
variant of the multiply instruction.
• acc80 A_mul_suf(int __a, unsigned int __b);
Multiplies signed fractional value
__a
by unsigned fractional value
__b
to produce an acc80
-typed result.
This built-in function generates the
(SUF)
variant of the multiply instruction.
• acc80 A_mul_usf(unsigned int __a, int __b);
Multiplies unsigned fractional value
__a
by signed fractional value
__b
to produce an acc80
-typed result.
This built-in function generates the
(USF)
variant of the multiply instruction.
• acc80 A_mul_uuf(unsigned int __a, unsigned int __b);
Multiplies unsigned fractional values
__a
and
__b
to produce an acc80
-typed result. This built-in function generates the
(UUF)
variant of the multiply instruction.
• acc80 A_mul_ssfr(int __a, int __b);
Multiplies signed fractional values
__a
and
__b
to produce an acc80
-typed result. This built-in function generates the
(SSFR)
variant of the multiply instruction.
• acc80 A_mul_sufr(int __a, unsigned int __b);
Multiplies signed fractional value
__a
by unsigned fractional value
__b
to produce an acc80
-typed result.
This built-in function generates the
(SUFR)
variant of the multiply instruction.
• acc80 A_mul_usfr(unsigned int __a, int __b);
Multiplies unsigned fractional value
__a
by signed fractional value
__b
to produce an acc80
-typed result.
This built-in function generates the
(USFR)
variant of the multiply instruction.
• acc80 A_mul_uufr(unsigned int __a, unsigned int __b);
Multiplies unsigned fractional values
__a
and
__b
to produce an acc80
-typed result. This built-in function generates the
(UUFR)
variant of the multiply instruction.
The following built-in functions may be used to perform multiply-accumulates, multiplying two values and adding the result to an acc80
operand to produce an acc80
result. They are declared in builtins.h
:
• acc80 A_mac_ssi(acc80 __a, int __b, int __c);
Multiplies signed integers
__b
and
__c
and adds the product to
__a
to produce an acc80
-typed result. This built-in function generates the
(SSI)
variant of the multiply-accumulate instruction.
• acc80 A_mac_sui(acc80 __a, int __b, unsigned int __c);
2–156
CrossCore Embedded Studio 2.2.0
Compiler Built-In Functions
Multiplies signed integer
__b
by unsigned integer
__c
and adds the product to
__a
to produce an acc80
typed result. This built-in function generates the
(SUI)
variant of the multiply-accumulate instruction.
• acc80 A_mac_usi(acc80 __a, unsigned int __b, int __c);
Multiplies unsigned integer
__b
by signed integer
__c
and adds the product to
__a
to produce an acc80
typed result. This built-in function generates the
(USI)
variant of the multiply-accumulate instruction.
• acc80 A_mac_uui(acc80 __a, unsigned int __b, unsigned int __c);
Multiplies unsigned integers
__b
and
__c
and adds the product to
__a
to produce an acc80
-typed result.
This built-in function generates the
(UUI)
variant of the multiply-accumulate instruction.
• acc80 A_mac_ssf(acc80 __a, int __b, int __c);
Multiplies signed fractional values
__b
and
__c
and adds the product to
__a
to produce an acc80
-typed result. This built-in function generates the
(SSF)
variant of the multiply-accumulate instruction.
• acc80 A_mac_suf(acc80 __a, int __b, unsigned int __c);
Multiplies signed fractional value
__b
by unsigned fractional value
__c
and adds the product to
__a
to produce an acc80
-typed result. This built-in function generates the
(SUF)
variant of the multiply-accumulate instruction.
• acc80 A_mac_usf(acc80 __a, unsigned int __b, int __c);
Multiplies unsigned fractional value
__b
by signed fractional value
__c
and adds the product to
__a
to produce an acc80
-typed result. This built-in function generates the
(USF)
variant of the multiply-accumulate instruction.
• acc80 A_mac_uuf(acc80 __a, unsigned int __b, unsigned int __c);
Multiplies unsigned fractional values
__b
and
__c
and adds the product to
__a
to produce an acc80
-typed result. This built-in function generates the
(UUF)
variant of the multiply-accumulate instruction.
• acc80 A_mac_ssfr(acc80 __a, int __b, int __c);
Multiplies signed fractional values
__b
and
__c
and adds the product to
__a
to produce an acc80
-typed result. This built-in function generates the
(SSFR)
variant of the multiply-accumulate instruction.
• acc80 A_mac_sufr(acc80 __a, int __b, unsigned int __c);
Multiplies signed fractional value
__b
by unsigned fractional value
__c
and adds the product to
__a
to produce an acc80
-typed result. This built-in function generates the
(SUFR)
variant of the multiply-accumulate instruction.
• acc80 A_mac_usfr(acc80 __a, unsigned int __b, int __c);
Multiplies unsigned fractional value
__b
by signed fractional value
__c
and adds the product to
__a
to produce an acc80
-typed result. This built-in function generates the
(USFR)
variant of the multiply-accumulate instruction.
CrossCore Embedded Studio 2.2.0
2–157
Compiler Built-In Functions
• acc80 A_mac_uufr(acc80 __a, unsigned int __b, unsigned int __c);
Multiplies unsigned fractional values
__b
and
__c
and adds the product to
__a
to produce an acc80
-typed result. This built-in function generates the
(UUFR)
variant of the multiply-accumulate instruction.
The following built-in functions may be used to perform multiply-subtracts, multiplying two values and subtracting the result from an acc80
operand to produce an acc80
result. They are declared in builtins.h
:
• acc80 A_msub_ssi(acc80 __a, int __b, int __c);
Multiplies signed integers
__b
and
__c
and subtracts the product from
__a
to produce an acc80
-typed result. This built-in function generates the
(SSI)
variant of the multiply-accumulate instruction.
• acc80 A_msub_sui(acc80 __a, int __b, unsigned int __c);
Multiplies signed integer
__b
by unsigned integer
__c
and subtracts the product from
__a
to produce an acc80
-typed result. This built-in function generates the
(SUI)
variant of the multiply-accumulate instruction.
• acc80 A_msub_usi(acc80 __a, unsigned int __b, int __c);
Multiplies unsigned integer
__b
by signed integer
__c
and subtracts the product from
__a
to produce an acc80
-typed result. This built-in function generates the
(USI)
variant of the multiply-accumulate instruction.
• acc80 A_msub_uui(acc80 __a, unsigned int __b, unsigned int __c);
Multiplies unsigned integers
__b
and
__c
and subtracts the product from
__a
to produce an acc80
-typed result. This built-in function generates the
(UUI)
variant of the multiply-accumulate instruction.
• acc80 A_msub_ssf(acc80 __a, int __b, int __c);
Multiplies signed fractional values
__b
and
__c
and subtracts the product from
__a
to produce an acc80
typed result. This built-in function generates the
(SSF)
variant of the multiply-accumulate instruction.
• acc80 A_msub_suf(acc80 __a, int __b, unsigned int __c);
Multiplies signed fractional value
__b
by unsigned fractional value
__c
and subtracts the product from
__a
to produce an acc80
-typed result. This built-in function generates the
(SUF)
variant of the multiply-accumulate instruction.
• acc80 A_msub_usf(acc80 __a, unsigned int __b, int __c);
Multiplies unsigned fractional value
__b
by signed fractional value
__c
and subtracts the product from
__a
to produce an acc80
-typed result. This built-in function generates the
(USF)
variant of the multiply-accumulate instruction.
• acc80 A_msub_uuf(acc80 __a, unsigned int __b, unsigned int __c);
Multiplies unsigned fractional values
__b
and
__c
and subtracts the product from
__a
to produce an acc80
typed result. This built-in function generates the
(UUF)
variant of the multiply-accumulate instruction.
2–158
CrossCore Embedded Studio 2.2.0
Compiler Built-In Functions
• acc80 A_msub_ssfr(acc80 __a, int __b, int __c);
Multiplies signed fractional values
__b
and
__c
and subtracts the product from
__a
to produce an acc80
typed result. This built-in function generates the
(SSFR)
variant of the multiply-accumulate instruction.
• acc80 A_msub_sufr(acc80 __a, int __b, unsigned int __c);
Multiplies signed fractional value
__b
by unsigned fractional value
__c
and subtracts the product from
__a
to produce an acc80
-typed result. This built-in function generates the
(SUFR)
variant of the multiply-accumulate instruction.
• acc80 A_msub_usfr(acc80 __a, unsigned int __b, int __c);
Multiplies unsigned fractional value
__b
by signed fractional value
__c
and subtracts the product from
__a
to produce an acc80
-typed result. This built-in function generates the
(USFR)
variant of the multiply-accumulate instruction.
• acc80 A_msub_uufr(acc80 __a, unsigned int __b, unsigned int __c);
Multiplies unsigned fractional values
__b
and
__c
and subtracts the product from
__a
to produce an acc80
typed result. This built-in function generates the
(UUFR)
variant of the multiply-accumulate instruction.
The following built-in functions may be used to saturate or round an acc80
operand to produce an acc80
result.
They are declared in builtins.h
:
• acc80 A_sat_si(acc80 __a);
Saturates
__a
to a 32-bit signed integer and produces an acc80
-typed result. This built-in function generates the
(SI)
variant of the
SAT
instruction.
• acc80 A_sat_ui(acc80 __a);
Saturates
__a
to a 32-bit unsigned integer and produces an acc80
-typed result. This built-in function generates the
(UI)
variant of the
SAT
instruction.
• acc80 A_sat_sf(acc80 __a);
Saturates
__a
to a 64-bit signed fractional value and produces an acc80
-typed result. This built-in function generates the
(SF)
variant of the
SAT
instruction.
• acc80 A_sat_uf(acc80 __a);
Saturates
__a
to a 64-bit unsigned fractional value and produces an acc80
-typed result. This built-in function generates the
(UF)
variant of the
SAT
instruction.
• acc80 A_rnd_sf(acc80 __a);
Rounds
__a
to a 32-bit signed fractional value and produces an acc80
-typed result. This built-in function generates the
(SF)
variant of the
RND
instruction.
• acc80 A_rnd_uf(acc80 __a);
CrossCore Embedded Studio 2.2.0
2–159
Compiler Built-In Functions
Rounds
__a
to a 32-bit unsigned fractional value and produces an acc80
-typed result. This built-in function generates the
(UF)
variant of the
RND
instruction.
The following built-in functions may be used to extract a value from an acc80
operand to produce an integer result, with optional saturation or rounding. They are declared in builtins.h
:
• int sat_si(acc80 __a);
Saturates
__a
to a 32-bit signed integer. This built-in function generates the
(SI)
variant of the
SAT
instruction.
• unsigned int sat_ui(acc80 __a);
Saturates
__a
to a 32-bit unsigned integer. This built-in function generates the
(UI)
variant of the
SAT
instruction.
• int sat_sf(acc80 __a);
Saturates
__a
to a 32-bit signed fractional value. This built-in function generates the
(SF)
variant of the
SAT instruction.
• unsigned int sat_uf(acc80 __a);
Saturates
__a
to a 32-bit unsigned fractional value. This built-in function generates the
(UF)
variant of the
SAT
instruction.
• int rnd_sf(acc80 __a);
Rounds
__a
to a 32-bit signed fractional value. This built-in function generates the
(SF)
variant of the
RND instruction.
• unsigned int rnd_uf(acc80 __a);
Rounds
__a
to a 32-bit unsigned fractional value. This built-in function generates the
(UF)
variant of the
RND instruction.
• unsigned int mr0(acc80 __a);
Returns the bottom 32 bits of __a. This built-in function generates a copy from either the
MR0F
or
MR0B register.
• unsigned int mr1(acc80 __a);
Returns bits 32 to 63 of __a. This built-in function generates a copy from either the
MR1F
or
MR1B
register.
• int mr2(acc80 __a);
Returns bits 64 to 79 of __a. This built-in function generates a copy from either the
MR2F
or
MR2B
register.
Exclusive Transaction Built-In Functions
Processors that support byte-addressing (shown in
Processors Supporting Byte-Addressing table) also support instructions that perform exclusive transactions to memory. Built-in functions are provided to
2–160
CrossCore Embedded Studio 2.2.0
Compiler Built-In Functions
allow use of these accesses from C/C++ code. These are given in the
Built-in Functions for Exclusive Memory Access-
es table.
Table 2-38: Built-in Functions for Exclusive Memory Accesses
Built-in function Description
unsigned char load_exclusive_8(volatile unsigned char *, int *) unsigned short load_exclusive_16(volatile unsigned short *, int *) unsigned int load_exclusive_32(volatile unsigned int *, int *)
Load and return 8-bit value from address given by first argument using exclusive access.
Set memory pointed to by second parameter to zero on success, one on failure. Not available when using the
-char-size-32
compiler switch.
Load and return 16-bit value from address given by first argument using exclusive access.
Set memory pointed to by second parameter to zero on success, one on failure. Not available when using the
-char-size-32
compiler switch.
Load and return 32-bit value from address given by first argument using exclusive access.
Set memory pointed to by second parameter to zero on success, one on failure.
unsigned long long load_exclusive_64(volatile unsigned long long *, int *)
Load and return 64-bit value from address given by first argument using exclusive access.
Set memory pointed to by second parameter to zero on success, one on failure.
int store_exclusive_8(unsigned char, volatile unsigned char *) int store_exclusive_16(unsigned short, volatile unsigned short *) int store_exclusive_32(unsigned int, volatile unsigned int *) int store_exclusive_64(unsigned long long, volatile unsigned long long *)
Store 8-bit value (first argument) to address (second argument) using exclusive access.
Zero return value indicates success. Not available when using the
-char-size-32 compiler switch.
Store 16-bit value (first argument) to address (second argument) using exclusive access.
Zero return value indicates success. Not available when using the
-char-size-32 compiler switch.
Store 32-bit value (first argument) to address (second argument) using exclusive access.
Zero return value indicates success.
Store 64-bit value (first argument) to address (second argument) using exclusive access.
Zero return value indicates success.
The exclusive load built-in functions return the value held in the memory location indicated by their first parameter.
A second parameter is used so that the load can return whether the address being accessed supports exclusive accesses. A value of zero indicates success, while one indicates that exclusive accesses are not supported to the given address.
The exclusive store built-in functions attempt to store the value given by the first argument to the memory address given by the second argument. If the memory has not been written to since the preceding exclusive load of the same address, the value is written to the memory location and the function returns zero. Otherwise the memory is not updated and the function returns a non-zero result. An example of using these built-in functions to test and obtain a spin lock is given in the following example.
Example. Acquiring a spin lock using the exclusive transaction built-in functions.
#include <stdlib.h>
#include <builtins.h> void acquire_spin_lock(volatile unsigned int *lock) {
CrossCore Embedded Studio 2.2.0
2–161
Compiler Built-In Functions
while (1) {
int err;
int locked = load_exclusive_32(lock, &err);
if (err != 0)
abort();
if (locked == 0) {
err = store_exclusive_32(1, lock);
if (err == 0)
break;
}
}
}
Miscellaneous Built-In Functions
• unsigned int iop_read(volatile void * __a);
The iop_read
built-in function performs a volatile read from the address given by the argument, returning an unsigned int
compiler switch is used. In this
case, the built-in function can be used to access a memory-mapped IOP register (and have any necessary anomaly workarounds applied) while other volatile accesses are still treated by the compiler as non-IOP accesses.
• void iop_write(volatile void * __a, unsigned int __b);
The iop_write
built-in function performs a volatile write of the second argument to the address given by the
first argument. It can be used when the
compiler switch is used. In this case, the built-in function can be used to access a memory-mapped IOP register (and have any necessary anomaly workarounds applied) while other volatile accesses are still treated by the compiler as non-IOP accesses.
• void NOP(void);
The
NOP
built-in function inserts a
NOP
instruction.
• int funcsize(const void *func);
The funcsize
built-in function returns the size of function in instruction words. The result is calculated from the difference between the start and end labels for the function operand. The compiler creates these labels for all C/C++ functions.
The start label is the mangled name of the function. The end label used is a dot ("
.
") followed by the start label followed by "
.end
". For example, for C function foo
compiled with
-char-size-32
, these labels are
"
_foo:
" and "
._foo.end:
".
When using the funcsize
built-in for assembly functions, the start and end labels need to be correctly defined for it to work.
NOTE: The funcsize
built-in does not work for functions defined in different modules than it is used, because end labels are not usually externally visible.
2–162
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Language Extensions
Pragmas
The compiler supports a number of pragmas. Pragmas are implementation-specific directives that modify the compiler's behavior. There are two types of pragma usage: pragma directives and pragma operators.
Pragma directives have the following syntax:
#pragma pragma-directive pragma-directive-operands new-line
Pragma operators have the following syntax:
_Pragma (string-literal)
When processing a pragma operator, the compiler effectively turns it into a pragma directive using a non-string version of
string-literal
. This means that the following pragma directive
#pragma linkage_name mylinkname can also be equivalently be expressed using the following pragma operator
_Pragma ("linkage_name mylinkname")
The examples in this manual use the directive form.
The following sections describe the pragmas supported by the compiler:
•
•
•
•
•
•
Class Conversion Optimization Pragmas
•
Template Instantiation Pragmas
•
•
Fixed-Point Arithmetic Pragmas
•
•
•
•
•
•
CrossCore Embedded Studio 2.2.0
2–163
Pragmas
•
Mixed Char-Size Interface Pragmas
The compiler issues a warning when it encounters an unrecognized pragma directive or pragma operator. Refer to
the Achieving Optimal Performance From C/C++ Source Code
chapter on how to use pragmas for code optimization.
Data Declaration Pragmas
The data declaration pragmas include align
, alignment_region
, pack
, pad
, and no_partial_initialization
pragmas.
Alignments specified using these pragmas must be a power of two. The compiler rejects uses of those pragmas that specify alignments that are not powers of 2.
#pragma align alignopt
The alignalignopt
pragma can be used before variable and field declarations. It applies to the variable or field declaration that immediately follows the pragma.
The pragma's effect is that the next variable or field declaration should be forced to be aligned on a boundary specified by
alignopt
. The
alignopt
parameter is specified in addressable units, so when using the
-char-size-8 switch on processors that support byte-addressing, it should be specified in bytes, otherwise it should be specified in words.
• If the pragma is being applied to a local variable (since local variables are stored on the stack), the variable will only be changed when
alignopt
is not greater than the greatest possible stack alignment. If
alignopt
is greater than 2 words or 8 bytes, then a warning is given that the pragma is being ignored. Note that the variable is aligned relative to the stack pointer, and therefore for the pragma to be honored you must always make sure the stack pointer is kept double-word aligned. Therefore do not use the
-no-aligned-stack
switch and make sure interrupt handlers and assembly functions preserve double-word alignment of the stack pointer.
For more details, see
.
• If
alignopt
is greater than the alignment normally required by the following variable or field declaration, then the variable or field declaration's alignment is changed to
alignopt
.
• If
alignopt
is less than the alignment normally required, then the variable or field declaration's alignment is changed to
alignopt
, and a warning is given that the alignment has been reduced.
For example, typedef struct {
#pragma align 8
int foo;
int bar;
#pragma align 8
int baz;
} aligned_ints;
2–164
CrossCore Embedded Studio 2.2.0
Data Declaration Pragmas
The pragma also allows the following keywords as allowable alignment specifications:
_WORD
- specifies a 32-bit alignment
_LONG
- specifies a 64-bit alignment
_QUAD
- specifies a 128-bit alignment
NOTE: The align
pragma only applies to the immediately-following definition, even if that definition is part of a list. For example,
#pragma align _LONG
int i1, i2, i3; // pragma only applies to i1
#pragma alignment_region (
alignopt
)
Sometimes it is desirable to specify an alignment for a number of consecutive data items rather than individually.
This can be done using the alignment_region
and alignment_region_end
pragmas:
•
#pragma alignment_region
sets the alignment for all following data symbols up to the corresponding alignment_region_end
pragma.
•
#pragma alignment_region_end
removes the effect of the active alignment region and restores the default alignment rules for data symbols.
The
alignopt
parameter is specified in addressable units, so when using the
-char-size-8
switch on processors that support byte-addressing, it should be specified in bytes, otherwise it should be specified in words. The rules concerning the argument are the same as for
#pragma align
. The compiler faults an invalid alignment (such as an alignment that is not a power of two). The compiler warns if the alignment of a data symbol within the control of an alignment_region
is reduced below its natural alignment (as for
#pragma align
).
Use of the align
pragma overrides the region alignment specified by the currently active alignment_region pragma (if there is one). The currently active alignment_region
does not affect the alignment of fields.
Example:
/* For processor without byte-addressing support or compiled
with the -char-size-32 switch */
#pragma align 4 int aa; /* alignment 4 */ int bb; /* alignment 1 */
#pragma alignment_region (2) int cc; /* alignment 2 */ int dd; /* alignment 2 */ int ee; /* alignment 2 */
#pragma align 4
CrossCore Embedded Studio 2.2.0
2–165
Data Declaration Pragmas
int ff; /* alignment 4 */ int gg; /* alignment 2 */ int hh; /* alignment 2 */
#pragma alignment_region_end int ii; /* alignment 1 */
#pragma alignment_region (3) long double kk; /* the compiler faults this, alignment is not
a power of two */
#pragma alignment_region_end
#pragma pack (
alignopt
)
The pack
alignopt
pragma may be applied to struct definitions. It applies to all struct
definitions that follow, until the default alignment is restored by omitting
alignopt
; for example, by
#pragma pack()
with empty parentheses.
The
alignopt
parameter is specified in addressable units, so when using the
-char-size-8
switch on processors that support byte-addressing, it should be specified in bytes, otherwise it should be specified in words. The pragma is used to reduce the default alignment of the struct
to be aligned. If there are fields within the struct
that have a default alignment greater than align
, their alignment is reduced to be alignopt
. If there are fields within the struct that have alignment less than align
, their alignment is unchanged.
If alignopt
is specified, it is illegal to invoke
#pragma pad
until the default alignment is restored. The compiler generates an error if the pad
and pack
pragmas are used in a manner that conflicts.
#pragma pad (
alignopt
)
The padalignopt
pragma can be applied to struct
definitions. It applies to all struct definitions that follow, until the default alignment is restored by omitting
alignopt
. The pragma is effectively shorthand for placing
#pragma align
before every field within the struct definition. Like the
#pragma pack
, it reduces the alignment of fields that default to an alignment greater than
alignopt
. However, unlike the
#pragma pack
, it also increases the alignment of fields that default to an alignment less than
alignopt
. Specify the
alignopt
parameter in addressable units. When using the
-char-size-8
switch for processors supporting byte-addressing, specify
alignopt
in bytes; otherwise, specify
alignopt
in words.
If
alignopt
is specified, it is illegal to invoke
#pragma pad
until default alignment is restored.
NOTE: While
#pragma packalignopt
generates a warning if a field alignment is reduced,
#pragma padalignopt
does not. If
alignopt
is specified, it is illegal to invoke
#pragma pack
until default alignment is restored.
The following example shows how to use
#pragma pad()
.
2–166
CrossCore Embedded Studio 2.2.0
Data Declaration Pragmas
#pragma pad(8) struct {
int i;
int j;
} s = {1,2};
#pragma pad()
#pragma no_partial_initialization
The no_partial_initialization
pragma indicates that the compiler should raise a diagnostic if the following structure declaration does not provide an initialization value for all members of the structure. The pragma is useful when a structure declaration is extended between revisions of the software.
The following example shows how to use
#pragma no_partial_initialization
: struct no_err {
int x;
int y;
};
#pragma no_partial_initialization struct with_err {
int x;
int y;
}; struct no_err s1 = { 5 }; // no diagnostic struct with_err s2 = { 5 }; // diagnostic reported
Interrupt Handler Pragmas
The interrupt pragmas provide a method by which you can write interrupt service routines in C and install them directly into the interrupt vector table, bypassing the dispatcher provided with the C run-time library.
NOTE: It will not normally be necessary to use these pragmas when writing interrupt handlers; the standard interrupt dispatcher will be a more convenient approach. For details on writing and installing interrupt handlers, refer to the System Run-Time Documentation in the CCES online help.
NOTE: These pragmas are not supported for SHARC+ processors. Use of the pragmas on SHARC+ processors will result in a compile-time error.
CAU-
TION:
When using these interrupt pragmas, you must use the correct pragmas for your application's context.
Do not use these pragmas unless you know which registers require saving, are comfortable with modifying and rebuilding your startup code, and can ensure that your interrupt handlers will comply with the requirements of any RTOS you are using.
CrossCore Embedded Studio 2.2.0
2–167
Interrupt Handler Pragmas
#pragma flush_restore_loop_stack
When the flush_restore_loop_stack
pragma is applied to an interrupt handler, the compiler generates code to save the current loop status registers (
CURLCTR
and
LADDR
) to the stack, and to empty the loop stack, and to restore the loop stack at the end of the handler.
The pragma takes effect only when it is used in conjunction with one of
#pragma interrupt_complete
or
#pragma interrupt_complete_nesting
, otherwise an error message is issued.
#pragma implicit_push_sts_handler
When the implicit_push_sts_handler
pragma is applied to an interrupt handler, the compiler does not generate an explicit
PUSH
and
POP
of
STS
.
The pragma takes effect only when it is used with
#pragma interrupt_complete
or
#pragma interrupt_complete_nesting
; otherwise, an error message is issued.
The compiler cannot determine whether the handler for the pragma is applied as a handler for the
VIRPT
,
IRQ
, or timer interrupts. It is your responsibility to determine whether to use the pragma or not.
#pragma interrupt_complete
The interrupt_complete
pragma is similar to the
#pragma interrupt_complete_nesting
pragma, except that it does not re-enable interrupts. (It is for non-nested interrupt handlers.) This is done by not modifying the
MODE1
register.
By default, the pragma saves and restores only the top 32 bits of each data register. See
NOTE: It will not normally be necessary to use this pragma when writing interrupt handlers; the standard interrupt dispatcher will be a more convenient approach. For details on writing and installing interrupt handlers, refer to the System Run-Time Documentation in the CCES online help.
NOTE: This pragma are not supported for SHARC+ processors. Use of the pragma on SHARC+ processors will result in a compile-time error.
CAU-
TION:
Do not use this pragma unless you know which registers require saving, are comfortable with modifying and rebuilding your startup code, and can ensure that your interrupt handlers will comply with the requirements of any RTOS you are using.
#pragma interrupt_complete_nesting
The interrupt_complete_nesting
pragma is used before a function definition. Use a function definition as an interrupt handler that can be called directly from the interrupt vector table. It produces a function that terminates with a "return from interrupt" sequence. Similar to
#pragma interrupt
, it saves and restores all registers used by the function. It also performs a
PUSH STS
instruction at the start of the function to save the
MODE1
registers.
2–168
CrossCore Embedded Studio 2.2.0
Interrupt Handler Pragmas
Since
PUSH STS
disables nested interrupts, and the pragma can be used with nested interrupts, it re-enables interrupts by the
BIT SET MODE1 0x1000;
instruction. At the end of the function, it performs a
POP STS
instruction to restore the status and
MODE1
registers.
By default, the pragma saves and restores only the top 32 bits of each data register. See
NOTE: It will not normally be necessary to use this pragma when writing interrupt handlers; the standard interrupt dispatcher will be a more convenient approach. For details on writing and installing interrupt handlers, refer to the System Run-Time Documentation in the CCES online help.
NOTE: This pragma are not supported for SHARC+ processors. Use of the pragma on SHARC+ processors will result in a compile-time error.
CAU-
TION:
Do not use this pragma unless you know which registers require saving, are comfortable with modifying and rebuilding your startup code, and can ensure that your interrupt handlers will comply with the requirements of any RTOS you are using.
#pragma interrupt_dispatched_handler
The interrupt_dispatched_handler
pragma can be used before a function declaration or definition. It applies to the function declaration or definition that immediately follows the pragma.
The interrupt_dispatched_handler
pragma is used for functions that will be called by an interrupt dispatcher. It indicates that the compiler should ensure that all used registers (including scratch registers) are restored at the end of the function. The compiler also ensures that, if an I register is used in the function, the corresponding L register is set to zero, so that circular buffering is not inadvertently invoked. The generated function uses the normal function return sequence; it does not use a "return from interrupt" sequence.
NOTE: It will not normally be necessary to use this pragma when writing interrupt handlers; the standard interrupt dispatcher will be a more convenient approach. For details on writing and installing interrupt handlers, refer to the System Run-Time Documentation in the CCES online help.
CAU-
TION:
Do not use this pragma unless you know which registers require saving, are comfortable with modifying and rebuilding your startup code, and can ensure that your interrupt handlers will comply with the requirements of any RTOS you are using. The standard interrupt dispatcher provided by adi_int.h
must be used if you use the uCOS2 or uCOS3 RTOS.
#pragma interrupt_reentrant
The interrupt_reentrant
pragma is used in conjunction with
#pragma interrupt_dispatched_handler
, to generate a function that re-enables interrupts, so that the low-level dispatched handler can be interrupted by higher-priority interrupts.
CrossCore Embedded Studio 2.2.0
2–169
Interrupt Handler Pragmas
#pragma save_restore_40_bits
The save_restore_40_bits
pragma is used along with
#pragma interrupt_complete
or
#pragma interrupt_complete_nesting
to save and restore all 40 bits of the data registers (
Dregs
) used by the handler.
This ensures that any routines using 40-bit arithmetic that are interrupted do not suffer accuracy problems. For leaf routines (that is, routines that do not call any other functions), the compiler saves and restores only the registers that are used. For non-leaf routines, the compiler saves and restores 40 bits of all
Dregs
. Note that saving and restoring each
Dregs
requires six instructions.
#pragma save_restore_simd_40_bits
The save_restore_simd_40_bits
pragma is used with
#pragma interrupt_complete
and
#pragma interrupt_complete_nesting
to save and restore all 40 bits of any
PEx
data registers (
Dregs
) and
PEy
data registers (
Sregs
) used by the handler. It ensures that any routines using 40-bit arithmetic and SIMD mode that are interrupted do not suffer accuracy problems. For leaf routines (that is, routines that do not call any other functions), the compiler saves and restores only the registers that are used. For non-leaf routines, the compiler saves and restores
40 bits of all
Dregs
and
Sregs
. Saving and restoring each
Dregs
and
Sregs
requires six instructions.
NOTE: Only one run-time library routine ( cfft_mag()
) uses 40-bit arithmetic and SIMD mode.
Loop Optimization Pragmas
Loop optimization pragmas give the compiler additional information about the properties of the code within a particular loop, which allows the compiler to perform more aggressive optimization. The pragmas are placed before the loop statement and apply to the statement that immediately follows, which must be a for
, while
or do
statement.
In general, it is most effective to apply loop pragmas to inner-most loops, since the compiler can achieve the most savings there.
The optimizer always attempts to vectorize loops when it is safe to do so. The optimizer exploits the information generated by the interprocedural analysis to increase the cases where it knows that it is safe to do so. See
for more information.
#pragma SIMD_for
The
SIMD_for
pragma must precede a for
, while
, or do.. while
loop construct, and informs the compiler that the loop fulfills these conditions:
• Memory accesses are suitably aligned where necessary for SIMD mode.
• There are no memory accesses that rely on data stored during the previous iteration of the loop.
• There are no memory accesses within consecutive iterations that alias each other.
• Special constraints hold for any circular buffers used in the loop. The circular buffer length must consist of an even number of elements, and the initial value of the pointer must be aligned to point to an even element.
for more details.
2–170
CrossCore Embedded Studio 2.2.0
Loop Optimization Pragmas
#pragma all_aligned
The all_aligned
pragma applies to the subsequent loop. The pragma tells the compiler that all pointer-induction variables in the loop are initially double-word aligned.
The variable takes an optional argument ( n
) which can specify that the pointers are aligned after n
iterations. Therefore,
#pragma all_aligned(1)
says that after one iteration, all the pointer induction variables of the loop are so aligned. In other words, the default argument is zero.
#pragma no_vectorization
When the no_vectorization
pragma is specified on a loop, it ensures the compiler does not generate vectorized
SIMD code for the loop.
The pragma may also be specified on a function definition. For more information, see
#pragma loop_count (
min, max, modulo
)
The loop_count
pragma appears just before the loop it describes. It asserts that the loop iterates at least min times, no more than max
times, and a multiple of modulo
times. This information enables the optimizer to omit loop guards and to decide whether the loop is worth completely unrolling and whether code needs to be generated for odd iterations. Any of the parameters of the pragma that are unknown may be left blank.
For example, int i;
#pragma loop_count(24, 48, 8) for (i=0; i < n; i++)
#pragma loop_unroll N
The loop_unroll
pragma can be used only before a for
, while
or do.. while
loop. The pragma takes exactly one positive integer argument,
N
, and it instructs the compiler to unroll the loop
N
times prior to further transforming the code.
In the most general case, the effect of:
#pragma loop_unroll N for (init statements; condition; increment code) {
loop_body
} is equivalent to transforming the loop to: for (init statements; condition; increment code) {
loop_body /* copy 1 */
increment_code
if (
!
condition)
break;
loop_body /* copy 2 */
CrossCore Embedded Studio 2.2.0
2–171
Loop Optimization Pragmas
increment_code
if (
!
condition)
break;
...
loop_body /* copy N-1 */
increment_code
if (
!
condition)
break;
loop_body /* copy N */
Similarly, the effect of:
#pragma loop_unroll N while (condition) {
loop_body
} is equivalent to transforming the loop to: while (condition) {
loop_body /* copy 1 */
if (
!
condition)
break;
loop_body /* copy 2 */
if (
!
condition)
break;
...
loop_body /* copy N-1 */
if (
!
condition)
break;
loop_body /* copy N */
} and the effect of:
#pragma loop_unroll N do {
loop_body
} while (condition) is equivalent to transforming the loop to: do {
loop_body /* copy 1 */
if (!condition)
break;
2–172
CrossCore Embedded Studio 2.2.0
Loop Optimization Pragmas
loop_body /* copy 2 */
if (!condition)
break;
...
loop_body /* copy N-1 */
if (!condition)
break;
loop_body /* copy N */
} while (condition)
#pragma no_alias
Use the no_alias
pragma to inform the compiler that the following loop has no loads or stores that reference the same memory as each other. When the compiler finds memory accesses that potentially refer to the same location through different pointers, known as "aliases", the compiler is restricted in how it can reorder or vectorize the loop, because all the accesses from earlier iterations must be complete before the compiler can arrange for the next iteration to start.
In the example, void vadd(int *a, int *b, int *out, int n) {
int i;
#pragma no_alias
for (i = 0; i < n; i++)
out[i] = a[i] + b[i];
} the use of no_alias
pragma just before the loop informs the compiler that the pointers a
, b
, and out
point to different arrays, so no load from b
or a
is using the same address as any store to out
. Therefore, a[i]
or b[i]
is never an alias for out[i]
.
Using the no_alias
pragma can lead to better code because it allows the loads and stores to be reordered and any number of iterations to be performed concurrently (rather than just two at a time), thus providing better software pipelining by the optimizer.
#pragma vector_for
The vector_for
pragma tells the compiler that all iterations of the loop can run in parallel with each other, and data accessed in the loop are aligned suitably for SIMD operation. The vector_for
pragma does not force the compiler to vectorize the loop. The optimizer checks various properties of the loop and does not vectorize it if it believes it is unsafe or if it cannot deduce that it has the various properties necessary for the vectorization transformation.
The pragma has two effects:
CrossCore Embedded Studio 2.2.0
2–173
Pragmas
• It asserts to the compiler that data accesses are suitably aligned for SIMD operation.
• It disables checking for loop-carried dependencies.
void copy(short *a, short *b) {
int i;
#pragma vector_for
for (i = 0; i < 100; i++)
a[i] = b[i];
}
In cases where vectorization is impossible (for example, if one of the memory accesses were to have a non-unit stride through the array), the information given in the assertion made by vector_for
may still be put to good use in aiding other optimizations.
General Optimization Pragmas
The compiler supports several pragmas which can change the optimization level while a given module is being compiled. These pragmas must be used globally, immediately prior to a function definition. The pragmas do not just apply to the immediately following function; they remain in effect until the end of the compilation, or until superseded by one of the following optimize_
pragmas.
•
#pragma optimize_off
The pragma turns off the optimizer, if it was enabled, meaning it has the same effect as compiling with no optimization enabled.
•
#pragma optimize_for_space
The pragma turns the optimizer back on, if it was disabled, or sets the focus to give reduced code size a higher priority than high performance, where these conflict.
•
#pragma optimize_for_speed
The pragma turns the optimizer back on, if it was disabled, or sets the focus to give high performance a higher priority than reduced code size, where these conflict.
•
#pragma optimize_as_cmd_line
The pragma resets the optimization settings to be those specified on the cc21k
command line when the compiler was invoked.
These are code examples for the optimize_
pragmas.
#pragma optimize_off void non_op() { /* non-optimized code */ }
#pragma optimize_for_space void op_for_si() { /* code optimized for size */ }
#pragma optimize_for_speed void op_for_sp() { /* code optimized for speed */ }
2–174
CrossCore Embedded Studio 2.2.0
Pragmas
/* subsequent functions declarations optimized for speed */
Function Side-Effect Pragmas
The function side-effect pragmas ( alloc
, compatible_pm_dm_params
, pure
, const
, regs_clobbered
, misra_func
, no_vectorization
, noreturn
, overlay
, pgo_ignore
and result_alignment
) are used before a function declaration to give the compiler additional information about the function in order to enable it to improve the code surrounding the function call. These pragmas should be placed before a function declaration and should apply to that function. For example,
#pragma pure long dot(short*, short*, int);
The regs_clobbered_call
pragma is used before function call statement to give information regarding that particular call site.
#pragma alloc
The alloc
pragma tells the compiler that the function behaves like the library function " malloc
", returning a pointer to a newly allocated object. An important property of these functions is that the pointer returned by the function does not point at any other object in the context of the call. In the example,
#define N 100
#pragma alloc int *new_buf(void); int *vmul(int *a, int *b) {
int i;
int *out = new_buf();
for (i = 0; i < N; ++i)
out[i] = a[i] * b[i];
return out;
} the compiler can reorder the iterations of the loop because the
#pragma alloc
tells it that a
and b
cannot overlap out
.
The GNU attribute malloc
is also supported with the same meaning.
#pragma compatible_pm_dm_params
The compatible_pm_dm_params
pragma tells the compiler to treat pm
- and dm
-qualified pointers as assignmentcompatible within the following function declaration. The pragma is ignored if it does not immediately precede a function declaration.
.
CrossCore Embedded Studio 2.2.0
2–175
Function Side-Effect Pragmas
#pragma const
The const
pragma is a more restrictive form of the pure
pragma. The pragma tells the compiler that the function does not read from global variables as well as not writing to them or reading or writing volatile variables. The result of the function is therefore solely a function of its parameter values - two invocations with identical parameters will always return the same result. If any of the parameters are pointers, the function may not read the data they point at.
#pragma misra_func(arg)
The misra_func(arg)
pragma is placed before a function prototype. It is used to support MISRA-C rules 20.4,
20.7, 20.8, 20.9, 20.10, 20.11, and 20.12. The arg
indicates the type of function with respect to the MISRA-C
Rule. Functions following Rule 20.4 would take arg heap
, 20.7 arg jmp
, 20.8 arg handler
, 20.9 arg io
, 20.10 arg string_conv
, 20.11 arg system
and 20.12 arg time
.
#pragma no_vectorization
When the no_vectorization
pragma is specified immediately before a function definition, it ensures the compiler does not generate vectorized SIMD code for any loop in the function on which it is specified.
The pragma may also be specified on individual loops.
#pragma noreturn
The noreturn
pragma can be placed before a function prototype or definition. The pragma tells the compiler that the function to which it applies will never return to its caller. For example, a function such as the standard C function exit
never returns.
The pragma allows the compiler to treat all code following a call to a function declared with the pragma as unreachable and hence removable.
#pragma noreturn void func() {
while(1);
} main() {
func();
/* any code here will be removed */
}
#pragma overlay
When compiling code which involves one function calling another in the same source file, the compiler optimizer can propagate register information between the functions. This means that it can record which scratch registers are clobbered over the function call. This can cause problems when compiling overlaid functions, as the compiler may assume that certain scratch registers are not clobbered over the function call, but they are clobbered by the overlay manager. The
#pragma overlay
, when placed on the definition of a function, will disable this propagation of register information to the function's callers.
2–176
CrossCore Embedded Studio 2.2.0
Function Side-Effect Pragmas
For example,
#pragma overlay int add(int a, int b)
{
// callers of function add() assume it clobbers
// all scratch registers
return a+b;
}
#pragma pgo_ignore
The pgo_ignore
pragma tells the compiler that no profile should be generated for this function, when using profile-guided optimization. This is useful when the function is concerned with error checking or diagnostics. extern const short *x, *y; int dotprod(void) {
int i, sum = 0;
for (i = 0; i < 100; i++)
sum += x[i] * y[i];
return sum;
}
#pragma pgo_ignore int check_dotprod(void) {
/* The compiler will not profile this comparison */
return dotprod() == 100;
}
#pragma pure
The pragma tells the compiler that the function does not write to any global variables, and does not read or write any volatile variables. Its result, therefore, is a function of its parameters or of global variables. If any of the parameters are pointers, the function may read the data they point at but it may not write it.
As this means the function call has the same effect every time it is called, between assignments to global variables, the compiler does not need to generate the code for every call.
Therefore, in this example,
#pragma pure long sdot(short *, short *, int); long tendots(short *a, short *b, int n) {
int i;
long s = 0;
for (i = 0; i < 10; ++i)
s += sdot(a, b, n); // call can get hoisted out of loop
return s;
}
CrossCore Embedded Studio 2.2.0
2–177
Function Side-Effect Pragmas
the compiler can replace the ten calls to sdot
with a single call made before the loop.
#pragma regs_clobbered
string
The regs_clobbered
pragma may be used with a function declaration or definition to specify which registers are modified (or clobbered) by that function. The
string
contains a list of registers and is case-insensitive.
When used with an external function declaration, the pragma acts as an assertion telling the compiler something it would not be able to discover for itself. In the example,
#pragma regs_clobbered "r4 r8 i4" void f(void); the compiler knows that only registers r4
, r8
and i4
may be modified by the call to f
, so it may keep local variables in other registers across that call.
The regs_clobbered
pragma may also be used with a function definition, or a declaration preceding a definition
(when it acts as a command to the compiler to generate register saves, and restores on entry and exit from the function) to ensure it only modifies the registers in string.
For example,
#pragma regs_clobbered "r3 m4 r5 i12"
// Function "g" will only clobber r3, m4, r5, and i12 int g(int a) {
return a+3;
}
NOTE: The regs_clobbered
pragma may not be used in conjunction with
#pragma interrupt
. If both are specified, a warning is issued and the regs_clobbered
pragma is ignored.
To obtain optimum results with the pragma, it is best to restrict the clobbered set to be a subset of the default scratch registers. When considering when to apply the regs_clobbered
pragma, it may be useful to look at the output of the compiler to see how many scratch registers were used. Restricting the volatile set to these registers will produce no impact on the code produced for the function but may free up registers for the caller to allocate across the call site.
NOTE: Care must be taken when using the regs_clobbered
pragma with pointers to functions. A function pointer cannot be declared to have a customized clobber set, so if you take the address of a function which has a customized clobber set then you must also add
#pragma regs_clobbered_call
to every call through that the function pointer. The compiler raises a warning if you take the address of a function with a regs_clobbered
pragma.
String Syntax
A regs_clobbered
string consists of a list of registers, register ranges, or register sets that are clobbered. The list is separated by spaces, commas, or semicolons.
A
register
is a single register name, which is the same as that which may be used in an assembly file.
2–178
CrossCore Embedded Studio 2.2.0
Function Side-Effect Pragmas
A
register range
consists of start
and end
registers which both reside in the same register class, separated by a hyphen. All registers between the two (inclusive) are clobbered.
A
register set
is a name for a specific set of commonly clobbered registers that is predefined by the compiler.
The
Clobbered Register Sets table shows defined clobbered register sets.
Table 2-39: Clobbered Register Sets
Set
CCset
SHADOWset
MRset
DAG1scratch
DAG2scratch
DAGscratch
Dscratch
ALLscratch everything
Registers
ASTATx, ASTATy
All S regs, all Shadow MR regs, ASTATy
MRF, MRB; shadow MRF, shadow MRB
Members of DAG1 I, M, B and L-registers that are scratch by default
Members of DAG2 I, M, B and L-registers that are scratch by default
All members of
DAG1scratch
and
DAG2scratch
All D-registers that are scratch by default, ASTAT
Entire default scratch register set
All registers, apart from those that are user-reserved or unclobberable
When the compiler detects an illegal string, a warning is issued and the default volatile set as defined in this compiler manual is used instead.
Unclobberable and Must Clobber Registers
There are certain caveats as to what registers may or must be placed in the clobbered set. See the
Clobbered Register
Sets table. On SHARC processors, certain registers may not be specified in the clobbered set, as the correct operation of the function call requires their value to be preserved.
If the user specifies these registers in the clobbered set, a warning is issued and they are removed from the specified clobbered set.
I6, I7, B6, B7, L6, L7
Registers from these classes,
D, I, B, LCNTR, PX, MR may be specified in the clobbered set and code is generated to save them as necessary.
The L-registers are required to be set to zero on entry and exit from a function. A user may specify that a function clobbers the L-registers. If it is a compiler-generated function, then it leaves the L-registers at zero at the end of the function. If it is an assembly function, then it may clobber the L-registers. In that case, the L-registers are re-zeroed
CrossCore Embedded Studio 2.2.0
2–179
Function Side-Effect Pragmas
after any call to that function. The soft-wired registers
M5,M6,M7
and
M13,M14,M15
are reset in an analogous manner.
The registers
R2
and
I12
are always clobbered. If the user specifies a function definition with the regs_clobbered
pragma that does not contain these registers, a warning is issued and these registers are added to the clobbered set.
User-Reserved Registers
Registers in the
USTAT
class and user-reserved registers, which are indicated via the
-reserve
), are never preserved in the function wrappers whether in the clobbered set or not.
Function Parameters
Function calling conventions are visible to the caller and do not affect the clobbered set that may be used on a function. For example,
#pragma regs_clobbered "" // clobbers nothing void f(int a, int b); void g() {
f(2,3);
}
The parameters a and b are passed in registers
R4
and
R8
, respectively. No matter what happens in function f
, after the call returns, the values of
R4
and
R8
are still set to 2 and 3, respectively.
Function Results
The registers in which a function returns its result must always be clobbered by the callee and retain their new value in the caller. They may appear in the clobbered set of the callee but it makes no difference to the generated code-the return registers are not saved and restored. Only the return registers used by the particular function return type are special. Return registers used by different return types are treated in the clobbered list in the conventional way.
For example, typedef struct { int x; int y; } Point; typedef struct { int x[10]; } Big; int f(); // Result in R0. R1 may be preserved across call
Point g();// Result in R0 and R1
Big f(); // Result pointer in R0, R1 may be preserved
// across call.
#pragma regs_clobbered_call string
The regs_clobbered_call
pragma may be applied to a statement to indicate that the call within the statement uses a modified volatile register set. The pragma is closely related to
#pragma regs_clobbered
, but avoids some of the restrictions that relate to that pragma.
These restrictions arise because the regs_clobbered
pragma applies to a function's declaration-when the call is made, the clobber set is retrieved from the declaration automatically. This is not possible when the declaration is not available, because the function being called is not directly tied to a declaration of a specific function. This affects:
2–180
CrossCore Embedded Studio 2.2.0
Function Side-Effect Pragmas
• pointers to functions
• class methods
• pointers to class methods
• virtual functions
In such cases, the regs_clobbered_call
pragma can be used at the call site to inform the compiler directly of the volatile register set to be used during the call.
The pragma's syntax is as follows:
#pragma regs_clobbered_call clobber_string
statement
where
clobber_string
follows the same format as for the regs_clobbered
pragma and
statement
is the C statement containing the call expression.
There must be only a single call within the statement; otherwise, the statement is ambiguous. For example,
#pragma regs_clobbered "r0 r1 r2 i12" int func(int arg) { /* some code */ } int (*fnptr)(int) = func; int caller(int value) {
int r;
#pragma regs_clobbered_call "r0 r1 r2 i12"
r = (*fnptr)(value);
return r;
}
NOTE: When you use the regs_clobbered_call
pragma, you must ensure that the called function does indeed only modify the registers listed in the clobber set for the call - the compiler does not check this for you. It is valid for the callee to clobber less than is listed in the call’s clobber set. It is also valid for the callee to modify registers outside of the call’s clobber set, as long as the callee saves the values first and restores them before returning to the caller.
The following examples show this.
Example 1.
#pragma regs_clobbered "r0 r1 r2 i12" void callee(void) { ... }
#pragma regs_clobbered_call "r0 r1 r2 i12" callee(); // Okay - clobber sets match
Example 2.
#pragma regs_clobbered "r0 r2 i12"
CrossCore Embedded Studio 2.2.0
2–181
Function Side-Effect Pragmas
void callee(void) { ... }
#pragma regs_clobbered_call "r0 r1 r2 i12" callee(); // Okay - callee clobber set is a subset
// of call's set
Example 3.
#pragma regs_clobbered "r0 r1 r2 i12" void callee(void) { ... }
#pragma regs_clobbered_call "r0 r2 i12" callee(); // Error - callee clobbers more than
// indicated by call.
Example 4.
void callee(void) { ... }
#pragma regs_clobbered_call "r0 r1 r2 i12" callee(); // Error - callee uses default set larger
// than indicated by call.
Limitations
Pragma regs_clobbered_call
may not be used on constructors or destructors of C++ classes.
The pragma only applies to the call in the immediately-following statement. If the immediately-following line contains more than one statement, the pragma only applies to the first statement on the line:
#pragma regs_clobbered "r0 r1 r2 i12" x = foo(); y = bar(); // only "x = foo();" is affected by
// the pragma.
Similarly, if the immediately-following line is a sequence of declarations that use calls to initialize the variables, then only the first declaration is affected:
#pragma regs_clobbered "r0 r1 r2 i12" int x = foo(), y = bar();// only "x = foo()" is affected
// by the pragma.
Moreover, if the declaration with the call-based initializer is not the first in the declaration list, the pragma will have no effect:
#pragma regs_clobbered "r0 r1 r2 i12" int w = 4, x = foo(); y = bar();// pragma has no effect
// on "w = 4".
The pragma has no effect on function calls that get inlined. Once a function call is inlined, the inlined code obeys the clobber set of the function into which it has been inlined. It does not continue to obey the clobber set that will be used if an out-of-line copy is required.
2–182
CrossCore Embedded Studio 2.2.0
Function Side-Effect Pragmas
#pragma result_alignment (n)
The result_alignment
pragma asserts that the pointer or integer returned by the function has a value that is a multiple of
n
.
The pragma is often used in conjunction with the
#pragma alloc
of custom-allocation functions that return pointers that are more strictly aligned than could be deduced from their type. The following example shows a use of the pragma. Note that the pragma will not change the alignment of data returned by the declared function. It is a guideline to the compiler.
#pragma result_alignment(8) int * alloc_align8_data(unsigned long size);
Class Conversion Optimization Pragmas
The class conversion optimization pragmas ( param_never_null
, suppress_null_check
) allow the compiler to generate more efficient code when converting class pointers from a pointer-to-derived-class to a pointer-to-baseclass, by asserting that the pointer to be converted will never be a null pointer. This allows the compiler to omit the null check during conversion.
#pragma param_never_null
param_name [ ... ]
This pragma must immediately precede a function definition. It specifies a name or a list of space-separated names, which must correspond to the parameter names declared in the function definition. It checks that the named parameter is a class pointer type. Using this information it will generate more efficient code for a conversion from a pointer to a derived class to a pointer to a base class. It removes the need to check for the null pointer during the conversion.
For example,
#include <iostream> using namespace std; class A {
int a;
}; class B {
int b;
}; class C: public A, public B {
int c;
};
C obj;
B *bpart = &obj; bool fail = false;
#pragma param_never_null pc void func(C *pc)
{
B *pb;
pb = pc; /* without pragma the code generated has to
CrossCore Embedded Studio 2.2.0
2–183
Class Conversion Optimization Pragmas
check for NULL */
if (pb != bpart)
fail = true;
} int main(void)
{
func(&obj);
if (fail)
cout << "Test failed" << endl;
else
cout << "Test passed" << endl;
return 0;
}
#pragma suppress_null_check
This pragma must immediately precede an assignment of two pointers or a declaration list. If the pragma precedes an assignment it indicates that the second operand pointer is not null and generates more efficient code for a conversion from a pointer to a derived class to a pointer to a base class. It removes the need to check for the null pointer before assignment. On a declaration list it marks all variables as not being the null pointer. If the declaration contains an initialization expression, that expression is not checked for null.
For example,
#include <iostream> using namespace std; class A {
int a;
}; class B {
int b;
}; class C: public A, public B {
int c;
};
C obj;
B *bpart = &obj; bool fail = false; void func(C *pc)
{
B *pb;
#pragma suppress_null_check
pb = pc; /* without pragma the code generated has to
check for NULL */
if (pb != bpart)
fail = true;
}
2–184
CrossCore Embedded Studio 2.2.0
Pragmas
void func2(C *pc)
{
#pragma suppress_null_check
B *pb = pc, *pb2 = pc; /* pragma means these initializations
need not check for NULL. It also marks pb and pb2
as never being NULL, so the compiler will not
generate NULL checks in class conversions using
these pointers. */
if (pb != bpart || pb2 != bpart)
fail = true;
} int main(void)
{
func(&obj);
func2(&obj);
if (fail)
cout << "Test failed" << endl;
else
cout << "Test passed" << endl;
return 0;
}
Template Instantiation Pragmas
The template instantiation pragmas ( instantiate
, do_not_instantiate
and can_instantiate
) give finegrain control over where (that is, in which object file) the individual instances of template functions, and member functions and static members of template classes are created. The creation of these instances from a template is called instantiation. As templates are a feature of C++, these pragmas are allowed only in
-c++
mode.
Refer to Compiler C++ Template Support for more information on how the compiler handles templates.
These pragmas take the name of an instance as a parameter, as shown in the Instance Names table.
Table 2-40: Instance Names
Name
a template class name a template class declaration a member function name a static data member name a static data declaration a member function declaration a template function declaration
Parameter
A<int> class A<int>
A<int>::f
A<int>::I int A<int>::I void A<int>::f(int, char) char* f(int, float)
CrossCore Embedded Studio 2.2.0
2–185
Template Instantiation Pragmas
If instantiation pragmas are not used, the compiler chooses object files in which to instantiate all required instances automatically during the pre-linking process.
#pragma instantiate instance
The instantiate
pragma requests the compiler to instantiate
instance
in the current compilation.
For example,
#pragma instantiate class Stack<int> causes all static members and member functions for the int
instance of a template class
Stack
to be instantiated, whether they are required in this compilation or not. The example,
#pragma instantiate void Stack<int>::push(int) causes only the individual member function
Stack<int>::push(int)
to be instantiated.
#pragma do_not_instantiate instance
The pragma directs the compiler not to instantiate
instance
in the current compilation. For example,
#pragma do_not_instantiate int Stack<float>::use_count prevents the compiler from instantiating the static data member
Stack<float>::use_count
in the current compilation.
#pragma can_instantiate instance
The can_instantiate
pragma tells the compiler that if
instance
is required anywhere in the program, it should be instantiated in this compilation.
NOTE: Currently, the can_instantiate
pragma forces the instantiation even if it is not required anywhere in the program. Therefore, it has the same effect as
#pragma instantiate
.
Header File Control Pragmas
The header file control pragmas ( no_implicit_inclusion
, once
, and system_header
) help the compiler to handle header files.
#pragma no_implicit_inclusion
switch is used for each included
.h
file, the compiler attempts to include the corresponding
.c
or
.cpp
file. This is called implicit inclusion.
If
#pragma no_implicit_inclusion
is placed in an
.h
file, the compiler does not implicitly include the corresponding
.c
or
.cpp
file with the
-c++
switch. This behavior only affects the
.h
file with
#pragma no_implicit_inclusion
within it and the corresponding
.c
or
.cpp
files.
For example, if there are the following files
2–186
CrossCore Embedded Studio 2.2.0
Header File Control Pragmas
t.c
which contains
#include "m.h" and m.h and m.c
are both empty, then cc21k -c++ t.c -M shows the following dependencies for t.c
: t.doj: t.c
t.doj: m.h
t.doj: m.c
If the following line is added to m.h
,
#pragma no_implicit_inclusion running the compiler as before would not show m.c
in the dependencies list, such as: t.doj: t.c
t.doj: m.h
#pragma once
The once
pragma, which should appear at the beginning of a header file, tells the compiler that the header is written in such a way that including it several times has the same effect as including it once. For example,
#pragma once
#ifndef FILE_H
#define FILE_H
... contents of header file ...
#endif
NOTE: In this example, the
#pragma once
is actually optional because the compiler recognizes the
#ifndef/
#define/#endif
idiom and does not reopen a header that uses it.
#pragma system_header
The system_header
pragma identifies an include
file as a file supplied with CCES. The compiler makes use of this information to help optimize uses of the supplied library functions and inline functions that these files define.
The pragma should not be used in user application source.
Fixed-Point Arithmetic Pragmas
The compiler supports several pragmas which can change the semantics of arithmetic on the native fixed-point type, fract
. These are
#pragma FX_CONTRACT {ON|OFF}
and
#pragma FX_ROUNDING_MODE {TRUNCATION|
BIASED|UNBIASED}
. In addition,
#pragma STDC FX_FULL_PRECISION {ON|OFF|DEFAULT}
and
#pragma STDC FX_FRACT_OVERFLOW {SAT|DEFAULT}
are accepted by the compiler but have no effect on generated code.
CrossCore Embedded Studio 2.2.0
2–187
Fixed-Point Arithmetic Pragmas
These pragmas may be used at file scope, in which case they apply to all following functions until another pragma is respecified to change the pragma state. Alternatively, they may be specified in a
{ }
delimited scope (or compound statement), where they will temporarily override the current setting of the pragma's state until the end of the scope.
For more information, see
Using Native Fixed-Point Types
.
#pragma FX_CONTRACT {ON|OFF}
The
FX_CONTRACT {ON|OFF}
pragma may be used to control the precision of intermediate results of calculations on the native fixed-point type fract
. If
FX_CONTRACT
is
ON
, where an intermediate result is not stored back to a named variable, the compiler may choose to keep the intermediate result in greater precision than that mandated by the ISO/IEC C Technical Report 18037. It will do this where maintaining the higher precision allows more efficient code to be generated.
When
FX_CONTRACT
is
OFF
, the compiler will adhere strictly to the ISO/IEC Technical Report 18037 and will convert all intermediate results to the type dictated in this standard before use.
The following example shows the use of the pragma.
fract mulsu(fract f1, unsigned fract f2) {
#pragma FX_CONTRACT ON
return f1 * f2; /* compiler creates signed-unsigned multiply */
}
The default state of the
FX_CONTRACT
pragma is
ON
.
#pragma FX_ROUNDING_MODE {TRUNCATION|BIASED|UNBIASED}
The
FX_ROUNDING_MODE {TRUNCATION|BIASED|UNBIASED}
pragma may be used to control the rounding mode used during calculations on the native fixed-point type fract
.
When
FX_ROUNDING_MODE
is set to
TRUNCATION
, the exact mathematical result of a computation is rounded by truncating the least significant bits beyond the precision of the result type. This is equivalent to rounding towards negative infinity.
When
FX_ROUNDING_MODE
is set to
BIASED
, the exact mathematical result of a computation is rounded to the nearest value that fits in the result type. If the exact result lies exactly half-way between two consecutive values in the result type, the result is rounded up to the higher one.
When
FX_ROUNDING_MODE
is set to
UNBIASED
, the exact mathematical result of a computation is rounded to the nearest value that fits in the result type. If the exact result lies exactly half-way between two consecutive values in the result type, the result is rounded to the even value.
The following example shows the use of the pragma.
fract divide_biased(fract f1, fract f2) {
#pragma FX_ROUNDING_MODE BIASED
return f1 / f2; /* compiler creates divide with biased rounding */
}
The default state of the
FX_ROUNDING_MODE
pragma is
TRUNCATION
.
2–188
CrossCore Embedded Studio 2.2.0
Fixed-Point Arithmetic Pragmas
#pragma STDC FX_FULL_PRECISION {ON|OFF|DEFAULT}
The
STDC FX_FULL_PRECISION {ON|OFF|DEFAULT}
pragma is used by the ISO/IEC Technical Report 18037 to permit an implementation to generate faster code for fixed-point arithmetic, but produce lower-accuracy results.
The compiler always produces full-accuracy results. Therefore, although the pragma is accepted by the compiler, the code generated will be the same regardless of the state of
FX_FULL_PRECISION
.
#pragma STDC FX_FRACT_OVERFLOW {SAT|DEFAULT}
The
STDC FX_FRACT_OVERFLOW {SAT|DEFAULT}
pragma is used by the ISO/IEC Technical Report 18037 to permit an implementation to generate code that does not saturate fract
-typed results on overflow.
The fract
arithmetic with the compiler always saturates on overflow. Therefore, although the pragma is accepted by the compiler, the code generated will be the same regardless of the state of
FX_FRACT_OVERFLOW
.
Inline Control Pragmas
The compiler supports two pragmas to control the inlining of code. These pragmas are
#pragma always_inline
,
#pragma inline
, and
#pragma never_inline
.
#pragma always_inline
This always_inline
pragma may be applied to a function definition to indicate to the compiler that the function should always be inlined, and never called "out of line". The pragma may only be applied to function definitions with the inline
qualifier, and may not be used on functions with variable-length argument lists. It is invalid for function definitions that have interrupt-related pragmas associated with them.
If the function in question has its address taken, the compiler cannot guarantee that all calls are inlined, so a warning is issued.
for details of pragma precedence during inlining.
The following are examples of the always_inline
pragma.
int func1(int a) { // only consider inlining
return a + 1; // if -Oa switch is on
} inline int func2(int b) {// probably inlined, if optimizing
return b + 2;
}
#pragma always_inline inline int func3(int c) {// always inline, even unoptimized
return c + 3;
}
#pragma always_inline int func4(int d) {// error: not an inline function
CrossCore Embedded Studio 2.2.0
2–189
Inline Control Pragmas
return d + 4;
}
#pragma inline
The pragma instructs the compiler to inline the function if it is considered desirable. The pragma is equivalent to specifying the inline
keyword, but may be applied when the inline
keyword is not allowed (such as when
compiling in MISRA-C mode). For more information, see MISRA-C Compiler
.
#pragma inline int func5(int a, int b) { /* can be inlined */
return a / b;
}
#pragma never_inline
This pragma may be applied to a function definition to indicate to the compiler that function should always be called "out of line", and that the function's body should never be inlined.
The pragma may not be used on function definitions that have the inline
qualifier. See Function Inlining for de-
tails of pragma precedence during inlining.
These are code examples for the never_inline
pragma.
#pragma never_inline int func5(int e) {// never inlined, even with -Oa switch
return e + 5;
}
#pragma never_inline inline int func5(int f) {// error: inline function
return f + 6;
}
Linking Control Pragmas
Linking pragmas ( linkage_name
, function_name
, core
, retain_name
, section
, file_attr
and weak_entry
) change how a given global function or variable is viewed during the linking stage.
#pragma linkage_name identifier
The pragma associates the identifier with the next external function declaration. It ensures that the identifier is used as the external reference, instead of following the compiler's usual conventions. For example,
_Pragma("linkage_name __realfuncname") void funcname ();
See also #pragma function_name identifier
.
2–190
CrossCore Embedded Studio 2.2.0
Linking Control Pragmas
#pragma function_name identifier
The pragma associates the identifier with the next external function declaration. The compiler uses the given identifier instead of the one used in the function declaration for any external references. However, unlike
#pragma linkage_name
, the compiler follows its usual conventions to mangle the new name, for example by prefixing a leading underscore. For example,
_Pragma("function_name realfuncname") void funcname ();
See also #pragma linkage_name identifier .
#pragma core
When building a project that targets multiple processors or multiple cores on a processor, it is possible to create a link stage that produces executables for more than one core or processor. The interprocedural analysis (IPA) framework requires that some conventions be adhered to in order to successfully perform its analyses for such projects.
Because the IPA framework collects information about the whole program, including information on references which may be to definitions outside the current translation unit, the IPA framework must be able to distinguish these definitions and their references without ambiguity.
If any confusion were allowed about which definition a reference refers to, then the IPA framework could potentially cause bad code to be generated, or could cause translation units in the project to be continually recompiled ad infinitum. It is the global symbols that are really relevant in this respect. The IPA framework will correctly handle locals and static symbols because multiple definitions are not possible within the same file, so there can be no ambiguity.
In order to disambiguate all references and the definitions to which they refer, it is necessary to have a unique name for each definition within a given project. It is illegal to define two different functions or variables with the same name. This is illegal in single-core projects because this would lead to multiple definitions of a symbol and the link would fail. In multi-core projects, however, it may be possible to link a project with multiple definitions because one definition could be linked into each link project, resulting in a valid link. Without detailed knowledge of what actions the linker had performed, however, the IPA framework would not be able to disambiguate such multiple definitions. For this reason, to use the IPA framework, it is up to you to ensure unique names even in projects targeting multiple cores or processors.
There are a few cases for which it is not possible to ensure unique names in multi-core or multi-processor projects.
One such case is main
. Each processor or core will have its own main
function, and these need to be disambiguated for the IPA framework to be able to function correctly. Another case is where a library (or the C run-time startup) references a symbol which the user may wish to define differently for each core.
For this reason, CCES supports the
#pragma core(corename)
.
The pragma can be provided immediately prior to a definition or a declaration. The pragma allows you to give a unique identifier to each definition. It also allows you to indicate to which definition each reference refers. The IPA framework will use this core identifier to distinguish all instances of symbols with the same name and will therefore be able to carry out its analyses correctly.
CrossCore Embedded Studio 2.2.0
2–191
Linking Control Pragmas
NOTE: The corename
specified should only consist of alphanumeric characters. The core name is case sensitive.
The pragma should be used:
• On every definition (not in a library) for which there needs to be a distinct definition for each core.
• On every declaration of a symbol (not in a library) for which the relevant definition includes the use of
#pragma core
. The core specified for a declaration must agree with the core specified for the definition.
It should be noted that the IPA framework will not need to be informed of any distinction if there are two identical copies of the same function or data with the same name. Functions or data that come from objects and that are duplicated in memory local to each core, for example, will not need to be distinguished. The IPA framework does not need to know exactly which instance each reference will get linked to because the information processed by the framework is identical for each copy. Essentially, the pragma only needs to be specified on items where there will be different functions or data with the same name incorporated into the executable for each core.
Here is an example of
#pragma core
usage to distinguish two different main functions:
/* foo.c */
#pragma core("coreA") int main(void) {
/* Code to be executed by core A */
}
/* bar.c */
#pragma core("coreB") int main(void) {
/* Code to be executed by core B */
}
Omitting either instance of the pragma will cause the IPA framework to issue a fatal error indicating that the pragma has been omitted on at least one definition.
Here is an example that will cause an error to be issued because the name contains a non-alphanumeric character:
#pragma core("core/A") int main(void) {
/* Code to executed on core A */
}
Here is an example where the pragma needs to be specified on a declaration as well as the definitions. There is a library which contains a reference to a symbol which is expected to be defined for each core. Two more modules define the main
functions for the two cores. Two further modules, each only used by one of the cores, makes a reference to this symbol, and therefore requires use of the pragma. For example,
/* libc.c */
#include <stdio.h> extern int core_number; void print_core_number(void) {
printf("Core %d\n", core_number);
}
/* maina.c */
2–192
CrossCore Embedded Studio 2.2.0
Linking Control Pragmas
extern void fooa(void)
#pragma core("coreA") int core_number = 1;
#pragma core("coreA") int main(void) {
/* Code to be executed by core A */
print_core_number();
fooa();
}
/* mainb.c */ extern void foob(void)
#pragma core("coreB") int core_number = 2;
#pragma core("coreB") int main(void) {
/* Code to be executed by core B */
print_core_number();
foob();
}
/* fooa.c */
#include <stdio.h>
#pragma core("coreA") extern int core_number; void fooa(void) {
printf("Core: is core%c\n", `A' - 1 + core_number);
}
/* foob.c */
#include <stdio.h>
#pragma core("coreB") extern int core_number; void fooa(void) {
printf("Core: is core%c\n", `A' - 1 + core_number);
}
In general, it will only be necessary to use
#pragma core
in this manner when there is a reference from outside the application (in a library, for example) where there is expected to be a distinct definition provided for each core, and where there are other modules that also require access to their respective definition. Notice also that the declaration of core_number
in lib.c
does not require use of the pragma because it is part of a translation unit to be included in a library.
A project that includes more than one definition of main
will undergo some extra checking to catch problems that would otherwise occur in the IPA framework. For any non-template symbol that has more than one definition, the tool chain will fault any definitions that are outside libraries that do not specify a core name with the pragma. This check does not affect the normal behavior of the prelinker with respect to templates and in particular the resolution of multiple template instantiations.
To clarify:
CrossCore Embedded Studio 2.2.0
2–193
Linking Control Pragmas
Inside a library,
#pragma core
is not required on declarations or definitions of symbols that are defined more than once. However, a library can be responsible for forcing the application to define a symbol more than once (that is, once for each core). In this case, the definitions and declarations require the pragma to be used outside the library to distinguish the multiple instances.
It should be noted that the tool chain cannot check that uses of
#pragma core
are consistent. If you use the pragma inconsistently or ambiguously then the IPA framework may end up causing incorrect code to be generated or causing continual recompilation of the application's files.
It is also important to note that the pragma does not change the linkage name of the symbol it is applied to in any way.
For more information on IPA, see
#pragma retain_name
The retain_name
pragma indicates that the function or variable declaration that follows the pragma is not removed even though it apparently has no uses. Normally, when Interprocedural Analysis or linker elimination are enabled, the CCES tools will identify unused functions and variables, and will eliminate them from the resulting executable to reduce memory requirements. The retain_name
pragma instructs the tools to retain the specified symbol, regardless.
The following example shows how to use the pragma.
int delete_me(int x) {
return x-2;
}
#pragma retain_name int keep_me(int y) {
return y+2;
} int main(void) {
return 0;
}
Since the program has no uses for either delete_me()
or keep_me()
, the compiler removes delete_me()
, but keeps keep_me()
because of the pragma. You do not need to specify retain_name
for main()
.
The pragma is only valid for global symbols. It is not valid for the following kinds of symbols:
• Symbols with static
storage class
• Function parameters
• Symbols with auto
storage class (locals). These are allocated on the stack at runtime.
• Members/fields within a struct
/ union
/ class
• Type declarations
2–194
CrossCore Embedded Studio 2.2.0
Linking Control Pragmas
For more information on IPA, see
#pragma section/#pragma default_section
The section pragmas provide greater control over the sections in which the compiler places symbols.
The section(SECTSTRING[, QUALIFIER, ...])
pragma is used to override the target section for any global or static symbol immediately following it. The pragma allows greater control over section qualifiers compared to the section
keyword.
The default_section(SECTKIND[, SECTSTRING [, QUALIFIER, ...]])
pragma is used to override the default sections in which the compiler is placing its symbols.
The default sections fall into the categories listed under
SECTKIND
. Except for the
STI
category, the pragma remains in force for a section category until its next use with that particular category, or the end of the file. The
STI
is an exception, in that only one
STIdefault_section
can be specified and its scope is the entire file scope, not just the part following the use of
STI
. A warning is issued if several
STI
sections are specified in the same file.
The omission of a section name results in the default section being reset to be the section that was in use at the start of the file, which can be either a compiler default value, or a value set by the user through the
-section
command line switch (for example,
-section SECTKIND=SECTSTRING)
.
In all cases (including STI), the default_section
pragma overwrites the value specified with the
-section command-line switch.
#pragma default_section(DATA, "NEW_DATA1") int x;
#pragma default_section(DATA, "NEW_DATA2") int y=5;
#pragma default_section(DATA, "NEW_DATA3") int z;
In this case y
is placed in
NEW_DATA2
, because the definition of y
is within its scope.
A default_section
pragma can only be used at global scope, where global variables are allowed.
SECTKIND
can be one of the following keywords found in the SECTKIND Keywords table.
Table 2-41: SECTKIND Keywords
Keyword
CODE
ALLDATA
DATA
CONSTDATA
BSZ
SWITCH
Description
Section is used to contain procedures and functions
Shorthand notation for
DATA
,
CONSTDATA
,
BSS
,
STRINGS
and
AUTOINIT
Section is used to contain "normal data"
Section is used to contain read-only data
Section is used to contain zero-filled data
Section is used to contain jump-tables to implement C/C++ switch statements
CrossCore Embedded Studio 2.2.0
2–195
Linking Control Pragmas
Table 2-41: SECTKIND Keywords (Continued)
Keyword Description
VTABLE
STI
STRINGS
AUTOINIT
PM_DATA
PM_CONSTDATA
Section is used to contain C++ virtual-function tables
Section that contains code required to be executed by C++ initializations. For more information,
see Constructors and Destructors of Global Class Instances
.
Section that stores string literals
Contains data used to initialize aggregate autos.
Section is used to contain normal data declared with pm
keyword
Section is used to contain read-only data declared with pm
keyword
SECTSTRING
is the double-quoted string containing the section name, exactly as it appears in the assembler file.
Changing one section kind has no effect on other section kinds. For instance, even though
STRINGS
and
CONSTDATA
are by default placed by the compiler in the same section, if
CONSTDATA default_section
is changed, the change has no effect on the
STRINGS
data.
Please note that
ALLDATA
is not a real section, but a rather pseudo-kind that stands for
DATA
,
CONSTDATA
,
STRINGS
,
AUTOINIT
and
BSZ
, and changing
ALLDATA
is equivalent to changing all of these section kinds. Therefore,
#pragma default_section(ALLDATA, params) is equivalent to the sequence:
#pragma default_section(DATA, params)
#pragma default_section(CONSTDATA, params)
#pragma default_section(STRINGS, params)
#pragma default_section(AUTOINIT, params)
#pragma default_section(BSZ, params)
QUALIFIER
can be one of the following keywords found in the QUALIFIER Keywords table.
Table 2-42: QUALIFIER Keywords
Keyword
PM
DM
ZERO_INIT
NO_INIT
RUNTIME_INIT
DOUBLE32
DOUBLE64
Description
Section is located in program memory
Section is located in data memory
Section is zero-initialized at program startup
Section is not initialized at program startup
Section is user-initialized at program startup
Section may contain 32-bit but not 64-bit doubles
Section may contain 64-bit but not 32-bit doubles
2–196
CrossCore Embedded Studio 2.2.0
Linking Control Pragmas
Table 2-42: QUALIFIER Keywords (Continued)
Keyword Description
DOUBLEANY
SW
NW
DMAONLY
Section may contain either 32-bit or 64-bit doubles
Code is short-word (processors that support VISA execution only).
Code is normal-word (processors that support VISA execution only).
Section is located in memory that can only be accessed by DMA. On ADSP-2126x and certain
ADSP-2136x processors, this keyword applies to external memory.
There may be any number of comma-separated section qualifiers within such pragmas, but they must not conflict with one another. Qualifiers must also be consistent across pragmas for identical section names, and omission of qualifiers is not allowed even if at least one such qualifier has appeared in a previous pragma for the same section. If any qualifiers have not been specified for a particular section by the end of the translation unit, the compiler uses default qualifiers appropriate for the target processor. The following specifies that f()
should be placed in a section
"foo", which is
DOUBLEANY
qualified:
#pragma section("foo", DOUBLEANY) void f() {}
The compiler always tries to honor the section
pragma as its highest priority, and the default_section
pragma is always the lowest priority.
For example, the following code results in function f
being placed in section foo
:
#pragma default_section(CODE, "bar")
#pragma section("foo") void f() {}
The following code results in x
being placed in section zeromem
:
#pragma default_section(BSZ, "zeromem") int x;
However, the following example does not result in the variable a
being placed in section onion
because it was declared with the pm
keyword and therefore is placed in the
PM
data section:
#pragma default_section(DATA, "onion") int pm a = 4;
If the PM data is explicitly set as in this example,
#pragma default_section(PM_DATA, "pm_onion")
#pragma default_section(DATA, "onion") int pm a = 4; then the variable a
gets placed in the pm_onion
section.
The following code results in code in section "foo" being compiled as short-word code (processors that support VI-
SA execution only):
#pragma section("foo", SW)
CrossCore Embedded Studio 2.2.0
2–197
Linking Control Pragmas
The following results in code in section "foo2" being compiled as normal word code (on processors that support
VISA execution only):
#pragma default_section(CODE, "foo2", NW)
NOTE: In cases where a C++ STL object must be placed in a specific memory section, using
#pragma section/default_section
will not work. Instead, a non-default heap must be used, as explained in
Allocating C++ STL Objects to a Non-Default Heap .
#pragma file_attr("name[=value]"[, "name[=value]" [...]])
This pragma directs the compiler to emit the specified attributes when it compiles a file containing the pragma.
Multiple
#pragma file_attr
directives are allowed in one file.
If "
=value
" is omitted, the default value of "1" will be used.
NOTE: The value of an attribute is all the characters after the
'='
symbol and before the closing
'”'
symbol, including spaces. A warning will be emitted by the compiler if you have a preceding or trailing space as an attribute value, as this is likely to be a mistake.
See File Attributes for more information on using attributes.
#pragma weak_entry
The weak_entry
pragma may be used before a static variable or function declaration or definition. It applies to the function/variable declaration or definition that immediately follows the pragma. Use of the pragma causes the compiler to generate the function or variable definition with weak linkage.
The following are example uses of the pragma weak_entry
directive.
#pragma weak_entry int w_var = 0;
#pragma weak_entry void w_func(){}
NOTE: When a symbol definition is weak, it may be discarded by the linker in favor of another definition of the same symbol. Therefore, if any modules in the application make use of the weak_entry
pragma, interprocedural analysis is disabled because it would be unsafe for the compiler to predict which definition will be selected by the linker. For more information, see
Diagnostic Control Pragmas
The compiler supports
#pragma diag
which allows selective modification of the severity of compiler diagnostic messages.
The directive has three forms:
• Modify the severity of specific diagnostics
2–198
CrossCore Embedded Studio 2.2.0
Diagnostic Control Pragmas
• Modify the behavior of an entire class of diagnostics
• Save or restore the current behavior of all diagnostics
Modifying the Severity of Specific Diagnostics
This form of the directive has the following syntax:
#pragma diag(ACTION: DIAG [, DIAG ...][: STRING])
The
action:
qualifier can be one of the following keywords.
Table 2-43: Keywords for Action Qualifier
Keyword Action
suppress
Suppresses all instances of the diagnostic remark annotation warning error restore
Changes the severity of the diagnostic to a remark.
Changes the severity of the diagnostic to an annotation.
Changes the severity of the diagnostic to a warning.
Changes the severity of the diagnostic to an error.
Restores the severity of the diagnostic to what it was originally at the start of compilation after all command-line options were processed.
If not in MISRA-C mode, the
diag
qualifier can be one or more comma-separated compiler diagnostic numbers without the preceding " cc
" or zeros. The choice of error numbers is limited to those that may have their severity overridden (such as those that are displayed with a "
{D}
" in the error message).
In addition, some diagnostics are global (for example, diagnostics emitted by the compiler back-end after lexical analysis and parsing, or before parsing begins), and these global diagnostics cannot have their severity overridden by the diagnostic control pragmas. To modify the severity of global diagnostics, use the diagnostic control switches. For more information, see
-W{annotation|error|remark|suppress|warn} number[, number ...]
.
In MISRA-C mode, the
diag
qualifier is a list of MISRA-C rule numbers in the form
misra_rule_number_6_3
and
misra_rule_number_19_4
for rules 6.3 and 19.4, and so on. Special cases are rules 10.1 and 10.2, which are both split into four distinct rule checks. For example, 10.1(c) should be stated as
misra_rule_10_1_c
. diag may also be the special token misra_rules_all, which specifies that the pragma applies to all MISRA-C rules.
The third optional argument is a string-literal to insert a comment regarding the use of the
#pragma diag
.
Modifying the Behavior of an Entire Class of Diagnostics
This form of the directive has the following syntax, which is not allowed when in MISRA-C mode:
#pragma diag(ACTION)
The effects are as follows:
•
#pragma diag(errors)
CrossCore Embedded Studio 2.2.0
2–199
Diagnostic Control Pragmas
The pragma can be used to inhibit all subsequent warnings and remarks (equivalent to the
-w
switch option).
•
#pragma diag(remarks)
The pragma can be used to enable all subsequent remarks, annotations and warnings (equivalent to the
-
Wremarks
switch option).
•
#pragma diag(annotations)
The pragma can be used to enable all subsequent annotations and warnings (equivalent to the
-
Wannotations
switch option).
•
#pragma diag(warnings)
The pragma can be used to restore the default behavior when the
-w
,
-Wremarks
and
-Wannotations switches are not specified, which is to display warnings but inhibit remarks and annotations.
Saving or Restoring the Current Behavior of All Diagnostics
This form has the following syntax:
#pragma diag(ACTION)
The effects are as follows:
•
#pragma diag(push)
The pragma may be used to store the current state of the severity of all diagnostic error messages.
•
#pragma diag(pop)
The pragma restores all diagnostic error messages that was previously saved with the most recent push
.
All
#pragma diag(push)
directives must be matched with the same number of
#pragma diag(pop)
directives in the overall translation unit, but need not be matched within individual source files, unless in MISRA-C mode. Note that the error threshold (set by the remarks
, annotations
, warnings
or errors
keywords) is also saved and restored with these directives.
The duration of such modifications to diagnostic severity are from the next line following the pragma to either the end of the translation unit, the next
#pragma diag(pop)
directive, or the next overriding
#pragma diag() directive with the same error number. These pragmas may be used anywhere and are not affected by normal scoping rules.
All command-line overrides to diagnostic severity are processed first and any subsequent
#pragma diag()
directives will take precedence, with the restore action changing the severity back to that at the start of compilation after processing the command-line switch overrides.
NOTE: Note that the directives to modify specific diagnostics are singular (for example,
“error”
), and the directives to modify classes of diagnostics are plural (for example,
“errors”
).
2–200
CrossCore Embedded Studio 2.2.0
Pragmas
Run-Time Checking Pragmas
Run-time checking pragmas allow you to control the compiler's generation of additional checking code. This code can test at runtime for common programming errors. The
command-line switch and its related switches control which common errors are tested for. Use the command-line switches to enable run-time checking; once runtime checking is enabled, the run-time checking pragmas can be used to disable and re-enable checking, for specific functions.
This section describes the following pragmas:
•
•
NOTE: Run-time checking causes the compiler to generate additional code to perform the checks. This code has space and performance overheads. Use of run-time checking should be restricted to application development, and should not be used on applications for release.
#pragma rtcheck(off)
The rtcheck(off)
pragma disables any run-time check code generation that has been enabled via command-line switches, such as
. The pragma is only valid at file scope, and affects code generation for function defini-
tions that follow.
The pragma has no effect on checks for stack overflow, or checks of heap operations. This is because such checks are provided by selecting alternative library support at link-time, and so apply to the whole application.
#pragma rtcheck(on)
The rtcheck(on)
pragma re-enables any run-time check code generation that was enabled via command-line switches, such as
. The pragma is only valid at file scope, and affects code generation for function defini-
tions that follow. If no run-time checking was enabled by command-line switches, the pragma has no effect.
Memory Bank Pragmas
The memory bank pragmas provide additional performance characteristics for the memory areas used to hold code and data for the function.
By default, the compiler assumes that there are no external costs associated with memory accesses. This strategy allows optimal performance when the code and data are placed into high-performance internal memory. In cases where the performance characteristics of memory are known in advance, the compiler can exploit this knowledge to improve the scheduling of generated code.
#pragma code_bank(
bankname
)
The code_bank
pragma informs the compiler that the instructions for the immediately-following function are placed in a memory bank called
bankname
. Without the pragma, the compiler assumes that instructions are placed into the default bank, if one has been specified; see
for details. When optimizing the
CrossCore Embedded Studio 2.2.0
2–201
Memory Bank Pragmas
function, the compiler is aware of attributes of memory bank
bankname
, and determines how long it takes to fetch each instruction from the memory bank.
If
bankname
is omitted, the instructions for the function are not considered to be placed into any particular bank.
In the following example, the add_slowly()
function is placed into the " slowmem
" bank, which may have different performance characteristics from the default code bank, into which add_quickly()
is placed.
#pragma code_bank(slowmem) int add_slowly (int x, int y) { return x + y; } int add_quickly(int a, int b) { return a + b; }
#pragma data_bank(
bankname
)
The data_bank
pragma informs the compiler that the immediately-following function uses the memory bank
bankname
as the model for memory accesses for non-local data that does not otherwise specify a memory bank; see
for details. Without the pragma, the compiler assumes that non-local data should use the default bank, if any has been specified, for behavioral characteristics.
If
bankname
is omitted, the non-local data for the function is not considered to be placed into any specific bank.
In both green_func()
and blue_func()
of the following example, i
is associated with the memory bank
" blue
", and the retrieval and update of i
are optimized to use the performance characteristics associated with memory bank " blue
".
#pragma data_bank(green) int green_func(void)
{
extern int arr1[32];
extern int bank("blue") i;
i &= 31;
return arr1[i++];
} int blue_func(void)
{
extern int arr2[32];
extern int bank("blue") i;
i &= 31;
return arr2[i++];
}
The array arr1
does not have an explicit memory bank in its declaration. Therefore, it is associated with the memory bank " green
", because green_func()
has a specific default data bank. In contrast, arr2
is associated with the default data memory bank (if any), because blue_func()
does not have a
#pragma data_bank
preceding it.
2–202
CrossCore Embedded Studio 2.2.0
Memory Bank Pragmas
#pragma stack_bank(
bankname
)
The stack_bank
pragma informs the compiler that all locals for the immediately-following function are to be associated with memory bank
bankname
, unless they explicitly identify a different memory bank. Without the prag-
for details.
If
bankname
is omitted, locals for the function are not considered to be placed into any particular bank.
In the following example, the dotprod()
function places the sum
and i
values into memory bank " mystack
", while fib()
places r
, a
, and b
into the default stack memory bank (if any), because there is no stack_bank pragma. The count_ticks()
function does not declare any local data, but any compiler-generated local storage uses the " sysstack
" memory bank's performance characteristics.
#pragma stack_bank(mystack) short dotprod(int n, const short *x, const short *y)
{
int sum = 0;
int i = 0;
for (i = 0; i < n; i++)
sum += *x++ * *y++;
return sum;
} int fib(int n)
{
int r;
if (n < 2) {
r = 1;
} else {
int a = fib(n-1);
int b = fib(n-2);
r = a + b;
}
return r;
}
#pragma stack_bank(sysstack) void count_ticks(void)
{
extern int ticks;
ticks++;
}
#pragma default_code_bank(
bankname
)
The default_code_bank
pragma informs the compiler that
bankname
should be considered the default memory bank for the instructions generated for any following functions that do not explicitly use
#pragma code_bank
.
If
bankname
is omitted, the pragma sets the compiler's default back to not specifying a particular bank for generated code.
CrossCore Embedded Studio 2.2.0
2–203
Memory Bank Pragmas
For more information, see
.
#pragma default_data_bank(
bankname
)
The default_data_bank
pragma informs the compiler that
bankname
should be considered the default memory bank for non-local data accesses in any following functions that do not explicitly use
#pragma data_bank
.
If
bankname
is omitted, the pragma sets the compiler's default back to not specifying a particular bank for nonlocal data.
For more information, see
.
#pragma default_stack_bank(
bankname
)
The default_stack_bank
pragma informs the compiler that
bankname
should be considered the default memory bank for local data in any following functions that do not explicitly use
#pragma stack_bank
.
If
bankname
is omitted, the pragma sets the compiler's default back to not specifying a particular bank for local data.
For more information, see
.
#pragma bank_memory_kind(
bankname, kind
)
The bank_memory_kind
pragma informs the compiler of what
kind
of memory the memory bank
bankname
is.
for the kinds supported by the compiler.
The pragma must appear at global scope, outside any function definitions, but need not immediately precede a function definition.
In the following example, the compiler knows that all accesses to the data[]
array are to the " blue
" memory bank, and hence to internal, in-core memory.
#pragma bank_memory_kind(blue, internal) int sum_list(const int bank("blue") *data, int n)
{
int sum = 0;
while (n--)
sum += data[n];
return sum;
}
#pragma bank_read_cycles(
bankname, cycles[, bits]
)
The bank_read_cycles
pragma tells the compiler that each read operation on the memory bank
bankname
requires
cycles
cycles before the resulting data is available. This allows the compiler to generate more efficient code.
2–204
CrossCore Embedded Studio 2.2.0
Memory Bank Pragmas
If the bits
parameter is specified, it indicates that a read of
bits
bits will take
cycles
cycles. If the
bits
parameter is omitted, the pragma indicates that reads of all widths will require
cycles
cycles.
bits
may be one of
32
or
64
.
In the following example, the compiler assumes that a read from
*x
takes a single cycle, as this is the default read time, but that a read from
*y
takes twenty cycles, because of the pragma.
#pragma bank_read_cycles(slowmem, 20) int dotprod(int n, const int *x, bank("slowmem") const int *y)
{
int i, sum;
for (i=sum=0; i < n; i++)
sum += *x++ * *y++;
return sum;
}
The pragma must appear at global scope, outside any function definitions, but need not immediately precede a function definition.
#pragma bank_write_cycles(
bankname, cycles[, bits]
)
The bank_write_cycles
pragma tells the compiler that each write operation on memory bank
bankname
requires
cycles
cycles before it completes. This allows the compiler to generate more efficient code.
If the bits
parameter is specified, it indicates that a write of
bits
bits will take
cycles
cycles. If the
bits
parameter is omitted, the pragma indicates that writes of all widths will require
cycles
cycles.
bits
may be one of
32
or
64
.
In the following example, the compiler knows that each write through ptr
to the " output
" memory bank takes six cycles to complete.
#pragma bank_write_cycles(output, 6) void write_buf(int n, const char *buf)
{
volatile bank("output") char *ptr = REG_ADDR;
while (n--)
*ptr = *buf++;
}
The pragma must appear at global scope, outside any function definitions, but need not immediately precede a function definition. This is shown in the preceding example.
#pragma bank_maximum_width(
bankname, width
)
The bank_maximum_width
pragma informs the compiler that
width
is the maximum number of bits to transfer to/from memory bank
bankname
in a single access. On SHARC processors, the
width
parameter may only be
32 or
64
.
The pragma must appear at global scope, outside any function definitions, but need not immediately precede a function definition.
CrossCore Embedded Studio 2.2.0
2–205
Pragmas
Code Generation Pragmas
The code generation pragmas are described below in the following sections.
#pragma avoid_anomaly_45 {on | off}
When executing code from external SDRAM on the ADSP-21161 processor, conditional instructions containing a
DAG1
data access may not be performed correctly.
These pragmas,
#pragma avoid_anomaly_45 on
and
#pragma avoid_anomaly_45 off
, allow you to initiate (or avoid) the generation of such instructions on a function-by-function basis. The pragmas should be used before a function definition and remain in effect until another variant of the pragma is seen.
#pragma no_db_return
The pragma is used immediately before a function definition and will cause the compiler to ensure that non-delayed-branch instructions are used to return from the function. The pragma may be applied to both interrupt and non-interrupt function definitions. Applying the pragma to an interrupt function can be used as a workaround for
ADSP-213xx silicon anomalies 02000069, 04000068, 06000028, 07000021, 08000026, and 09000015, "Incorrect
Popping of stacks possible when exiting IRQx/Timer interrupts with DB modifiers."
If the pragma does not appear immediately before a function definition then a compiler error message is issued.
The following examples show uses of the pragma:
Example 1.
#pragma no_db_return int max(int x, int y)
{
if (y > x)
return y;
else
return x;
}
Example 2.
#pragma no_db_return
#pragma interrupt_complete_nesting void foo(void) {
...
}
Example 3.
#pragma no_db_return int i; /* INVALID - not a function definition, causes compiler error cc1943 */
2–206
CrossCore Embedded Studio 2.2.0
Pragmas
Exceptions Table Pragma
The generate_exceptions_tables
pragma allows you to generate code that permits exceptions to be thrown through C functions. It is described in the following section.
#pragma generate_exceptions_tables
The pragma may be applied to a C function definition to request the compiler to generate tables which enable C++ exceptions to be thrown through executions of this function.
The following example consists of two source files. The first is a C file which contains the pragma applied to the definition of function call_a_call_back
.
#pragma generate_exceptions_tables void call_a_call_back(void pfn(void)) {
pfn();/* without pragma program terminates when
throw_an_int throws an exception */
}
The second source file contains C++ code. The function main
calls call_a_call_back
, from the C file listed above, which in turn calls throw_an_int
. The exception thrown by throw_an_int
will be caught by the catch handler in main
because use of the pragma ensured the compiler generated an exceptions table for call_a_call_back
.
#include <iostream> extern "C" void call_a_call_back(void pfn()); static void throw_an_int() {
throw 3;
} int main() {
try {
call_a_call_back(throw_an_int);
} catch (int i) {
if (i == 3) std::cout << "Test passed\n";
}
}
An alternative to using
#pragma generate_exceptions_tables
is to compile C files with the
exception handling) switch, which, for C files, is equivalent to using the pragma before every function definition.
Mixed Char-Size Interface Pragmas
This section describes pragmas that help with generating code that interfaces between code built with the
-charsize-8
compiler switch and code built with the
-char-size-32
compiler switch.
•
#pragma byte_addressed [(push)|(pop)]
•
#pragma word_addressed [(push)|(pop)]
CrossCore Embedded Studio 2.2.0
2–207
Mixed Char-Size Interface Pragmas
•
#pragma default_addressed [(push)|(pop)]
•
#pragma byte_addressed [(push)|(pop)]
#pragma byte_addressed
is used to specify that the following symbol(s) are defined in a source file built with the
-char-size-8
switch. It is generally used when creating an interface between a current source file, being compiled with the
-char-size-32
switch, and other source files built with the
-char-size-8
switch. The pragma may only be used on processors that support byte-addressing, shown in the
Processors Supporting Byte-Addressing
).
For a discussion of how to create interfaces between byte-addressed and word-addressed code using
#pragma byte_addressed
, see
.
#pragma word_addressed [(push)|(pop)]
#pragma word_addressed
is used to specify that the following symbol(s) are defined in a source file built with the
-char-size-32
switch. It is generally used when creating an interface between a current source file, being compiled with the
-char-size-8
switch, and other source files built with the
-char-size-32
switch. The pragma only has an effect on processors that support byte-addressing, shown in the
Processors Supporting Byte-Ad-
dressing table (see Processor Features
).
For a discussion of how to create interfaces between byte-addressed and word-addressed code using
#pragma word_addressed
, see
.
#pragma default_addressed [(push)|(pop)]
#pragma default_addressed
is used to specify that the following symbol(s) are defined in a source file built with the same
-char-size
switch as the current source file. It is generally used when creating a header file designed to be incorporated into either byte-addressed or word-addressed source files, where both word-addressed and byte-addressed versions of the declared symbols exist in a library. For example, the standard headers included in
CCES, such as stdio.h
, declare their symbols as default_addressed
as both word-addressed and byte-addressed versions of standard library functions are included in the CCES standard libraries. The pragma overrides any
#pragma byte_addressed
or
#pragma word_addressed
that is currently in force. The pragma only has an effect on processors that support byte-addressing, shown in the
Processors Supporting Byte-Addressing table (see
).
#pragma no_stack_translation
#pragma no_stack_translation
is used to specify that the following function declaration declares a function written in assembly code that will work correctly when the frame and stack pointers i6, i7, b6 and b7 are either in the word-addressed address space or in the byte-addressed address space. Therefore when calling this function from
C/C++ code, the compiler does not need to translate the frame and stack pointers prior to and on return from the
2–208
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Language Extensions
call. Using the pragma when it is valid to do so results in more efficient and smaller code. The pragma only has an effect on processors that support byte-addressing, shown in the Processors Supporting Byte-Addressing table (see
).
For a discussion of how to create interfaces between byte-addressed and word-addressed code using
#pragma no_stack_translation
, see
.
GCC Compatibility Extensions
The compiler provides compatibility with many features of the C dialect accepted by version 4.6 of the GNU C
Compiler. Many of these features are available in the ISO/IEC 9899:1999 standard. A brief description of the extensions is included in this section. For more information, refer to the following URL: http://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/index.html#toc_C-Extensions .
NOTE: The GCC compatibility extensions are only available in C89 and C99 modes. They are not accepted in C
++ dialect mode, unless enabled with the
switch.
Statement Expressions
A statement expression is a compound statement enclosed in parentheses. A compound statement itself is enclosed in braces
{}
, so this construct is enclosed in parentheses-brace pairs (
{}
).
The value computed by a statement expression is the value of the last statement which should be an expression statement. The statement expression may be used where expressions of its result type may be used. But they are not allowed in constant expressions.
Statement expressions are useful in the definition of macros as they allow the declaration of variables local to the macro. In the following example,
#define min(a,b) ({ \
short __x=(a),__y=(b),__res;\
if (__x > __y) \
__res = __y; \
else \
__res = __x; \
__res; \
}) int use_min() {
return min(foo(), thing()) + 2;
}
The foo()
and thing()
statements get called once each because they are assigned to the variables
__x
and
__y which are local to the statement expression that min
expands to and min()
can be used freely within a larger expression because it expands to an expression.
Labels local to a statement expression can be declared with the
__label__
keyword. For example,
#define checker(p) ({ \
CrossCore Embedded Studio 2.2.0
2–209
GCC Compatibility Extensions
__label__ exit; \
int i; \
for (i=0; p[i]; ++i) { \
int d = get(p[i]); \
if (!check(d)) goto exit; \
process(d); \
} \
exit: \
i; \
}) extern int g_p[100]; int checkit() {
int local_i = checker(g_p);
return local_i;
}
NOTE: Statement expressions are not supported in C++ mode. Also, statement expressions are an extension to C originally implemented in the GCC compiler. Analog Devices support the extension primarily to aid porting code written for that compiler. When writing new code, consider using inline functions, which are compatible with ANSI/ISO standard C++ and C99, and are as efficient as macros when optimization is enabled.
Type Reference Support Keyword (Typeof)
The typeof( expression )
construct can be used as a name for the type of expression without actually knowing what that type is. It is useful for making source code that is interpreted more than once such as macros or include files more generic.
The typeof
keyword may be used where ever a typedef
name is permitted such as in declarations and in casts.
For example,
#define abs(a) ({ \
typeof(a) __a = a; \
if (__a < 0) __a = - __a; \
__a; \
}) shows typeof
used in conjunction with a statement expression to define a "generic" macro with a local variable declaration.
The argument to typeof
may also be a type name. Because typeof
itself is a type name, it may be used in another typeof(type-name)
construct. This can be used to restructure the C type declaration syntax.
For example,
#define pointer(T) typeof(T *)
#define array(T, N) typeof(T [N]) array (pointer (char), 4) y;
2–210
CrossCore Embedded Studio 2.2.0
GCC Compatibility Extensions
declares y
to be an array of four pointers to char
.
NOTE: The typeof
keyword is not supported in C++ mode. The typeof
keyword is an extension to C originally implemented in the GCC compiler. It should be used with caution because it is not compatible with other dialects of C/C++ and has not been adopted by the more recent C99 standard.
Generalized Lvalues
Lvalues are expressions that may appear on the left-hand side of an assignment. GCC provides several lvalue-related extensions to C, which are supported by the compiler for GCC compatibility:
• A cast is an lvalue if its operand is an lvalue. This C-mode extension is not allowed in C++ mode.
• A comma operator is an lvalue if its right operand is an lvalue. This C-mode extension is a standard feature of
C++.
• A conditional operator is an lvalue if its last two operands are lvalues of the same type. This C-mode extension is a standard feature of C++.
Conditional Expressions With Missing Operands
The middle operand of a conditional operator can be left out. If the condition is non-zero (true), then the condition itself is the result of the expression. This can be used for testing and substituting a different value when a pointer is
NULL
. The condition is only evaluated once; therefore, repeated side effects can be avoided. For example,
printf("name = %s\n", lookup(key)?:"-"); calls lookup()
once, and substitutes the string "-" if it returns
NULL
. This is an extension to C, provided for compatibility with GCC. It is not allowed in C++ mode.
Zero-Length Arrays
Arrays may be declared with zero length. This is an anachronism supported to provide compatibility with GCC. Use variable length array members instead.
GCC Variable Argument Macros
The final parameter in a macro declaration may be followed by an ellipsis (
...
) to indicate the parameter stands for a variable number of arguments.
#define tracegcc(file,line,msg ...) \ logmsg(file,line, ## msg) can be used with differing numbers of arguments: the following statements: tracegcc("a.c", 999, "one", "two", "three"); tracegcc("a.c", 999, "one", "two"); tracegcc("a.c", 999, "one"); tracegcc("a.c", 999); expand to the following code:
CrossCore Embedded Studio 2.2.0
2–211
GCC Compatibility Extensions
logmsg("a.c", 999,"one", "two", "three"); logmsg("a.c", 999,"one", "two"); logmsg("a.c", 999,"one"); logmsg("a.c", 999);
The
##
operator has a special meaning when used in a macro definition before the parameter that expands the variable number of arguments: if the parameter expands to nothing, then it removes the preceding comma.
NOTE: This variable argument macro syntax comes from GCC. The compiler supports GCC variable argument
macro formats in C89 and C99 modes. In C++ mode the -g++
switch needs to be enabled to support
.
Line Breaks in String Literals
String literals may span many lines. The line breaks do not need to be escaped in any way. They are replaced by the character
\n
in the generated string. This extension is not supported in C++ mode. The extension is not compatible with many dialects of C, including ANSI/ISO C89 and C99. However, it is useful in asm
statements, which are intrinsically non-portable.
This extension may be disabled via the
switch.
Arithmetic on Pointers to Void and Pointers to Functions
Addition and subtraction is allowed on pointers to void
and pointers to functions. The result is as if the operands had been cast to pointers to char
. The sizeof()
operator returns one for void
and function types.
Cast to Union
A type cast can be used to create a value of a union type, by casting a value of one of the union's member types to the union type.
Ranges in Case Labels
A consecutive range of values can be specified in a single case by separating the first and last values of the range with the three-period token
...
.
For example, case 200 ... 300:
Escape Character Constant
The character escape '
\e
' may be used in character and string literals and maps to the ASCII Escape code, 27.
Alignment Inquiry Keyword (__alignof__)
The
__alignof__ (type-name)
construct evaluates to the alignment required for an object of a type. The
__alignof__ expression
construct can also be used to give the alignment required for an object of the
expression
type.
2–212
CrossCore Embedded Studio 2.2.0
GCC Compatibility Extensions
If expression is an lvalue
(may appear on the left hand side of an assignment), the alignment returned takes into account alignment requested by pragmas and the default variable allocation rules.
Keyword for Specifying Names in Generated Assembler (asm)
The asm
keyword can be used to direct the compiler to use a different name for a global variable or function. See also
#pragma linkage_name identifier
.
For example, int N asm("C11045"); tells the compiler to use the label C11045 in the assembly code it generates wherever it needs to access the source level variable
N
. By default, the compiler would use the label
_N
, or the label
N.
when compiling with the
-charsize-8
switch on processors that support byte-addressing.
The asm
keyword can also be used in function declarations but not function definitions. However, a definition preceded by a declaration has the desired effect.
For example, extern int f(int, int) asm("func"); int f(int a, int b) {
...
}
Function, Variable and Type Attribute Keyword (__attribute__)
The
__attribute__
keyword can be used to specify attributes of functions, variables and types, as in these examples,
void func(void) __attribute__ ((section("fred")));
int a __attribute__ ((aligned (8)));
typedef struct {int a[4];} __attribute__((aligned (4))) Q;
The
__attribute__
keyword is supported, and therefore code, written for GCC, can be ported. The
Keywords
for __attribute__ table lists the accepted keywords.
Table 2-44: Keywords for __attribute__
Attribute Keyword
alias("name") aligned(N) always_inline const constructor
Behavior
Accepted on functions declarations. Declares the function to be an alias for name.
Accepted on variables, where it is equivalent to
#pragma align(N)
. Accepted
(but ignored) on typedefs.
Accepted on function declarations. Equivalent to the pragma of the same name.
Accepted on function declarations. Equivalent to the pragma of the same name.
Accepted (but ignored) on function declarations.
CrossCore Embedded Studio 2.2.0
2–213
GCC Compatibility Extensions
Table 2-44: Keywords for __attribute__ (Continued)
Attribute Keyword Behavior
deprecated destructor format(kind,str,args) format_arg(kind, str) malloc naked no_instrument_function nocommon noinline nonnull noreturn nothrow packed pure section("name") sentinel transparent_union unused used
Accepted on function, variable and type declarations. Causes the compiler to emit a warning if the entity with the attribute is referenced within the source code.
Accepted (but ignored) on function declarations.
Accepted on function declarations. Indicates that the function accepts a formatting argument string of type kind, e.g. printf
. str and args are integer values; the strth parameter of the function is the formatting string, while the argsth parameter of the function is the first parameter processed by the formatting string.
Accepted on function declarations. Indicates that the function accepts and returns a formatting argument string of type kind. str is an integer value; the strth parameter of the function is the formatting string.
Accepted on function declarations. Equivalent to using
#pragma alloc
.
Accepted (but ignored) on function declarations.
Accepted (but ignored) on function declarations.
Accepted on variable declarations. Causes the compiler to treat the declaration as a definition.
Accepted on function declarations. Equivalent to
#pragma never_inline
.
Accepted on function declarations. Causes the compiler to emit a warning if the function is invoked with any NULL parameters.
Accepted on function declarations. Equivalent to using the pragma of the same name.
Accepted (but ignored) on function declarations.
Accepted (but ignored) on typedefs. When used on variable declarations, this is equivalent to using the pragma of the same name.
Accepted on function declarations. Equivalent to using the pragma of the same name.
Accepted on function declarations. Equivalent to using the pragma of the same name.
Accepted on function declarations. Directs the compiler to emit a warning for any calls to the function which do not provide a null pointer literal as the last parameter. Accepts an optional integer position P (default 0) to indicate that the Pth parameter from the end is the sentinel instead.
Accepted on union definitions. When the union type is used for a function's parameter, the parameter can accept values which match any of the union's types.
Accepted on declarations of functions, variables and types. Indicates that the entity is known not to be used, so the compiler should not emit diagnostics complaining that there are no uses of the entity.
Accepted on declarations of functions and variables. Indicates that the compiler should emit the entity even when the compiler cannot detect uses. Similar to
2–214
CrossCore Embedded Studio 2.2.0
GCC Compatibility Extensions
Table 2-44: Keywords for __attribute__ (Continued)
Attribute Keyword
warn_unused_result weak
Behavior
#pragma retain_name
, but this attribute can be applied to static entities that will not be visible outside the module. Conversely, this attribute will not prevent linker elimination from deleting the entity.
Accepted (but ignored) on function declarations.
Accepted on function and variable declarations. Equivalent to using
#pragma weak_entry
Unnamed struct/union Fields Within struct/unions
The compiler allows you to define a structure or union that contains, as fields, structures and unions without names.
For example: struct {
int field1;
union {
int field2;
int field3;
};
int field4;
} myvar;
This allows the user to access the members of the unnamed union as though they were members of the enclosing struct; for example, myvar.field2
.
Support for 40-Bit Arithmetic
The SHARC family of processors support 40-bit, floating-point arithmetic. Although this feature is not supported by the compiler, it is used by some run-time library functions and compiler support functions. This section provides information on the following topics:
• The implications of using 40-bit arithmetic in C/C++ code
• Library functions that use 40-bit arithmetic (directly or indirectly)
Using 40-Bit Arithmetic in Compiled Code
Although 40-bit arithmetic can be enabled in C/C++ code (by clearing the
RND32
bit on the MODE1 register), there are a number of factors that mean that arithmetic operations can produce inconsistent results:
• Where possible, the compiler will attempt to perform constant folding (the simplification of constant expressions at compilation time). The results of floating-point constant folding may be different from the results generated by performing the same calculation using the SHARC processor's 40-bit arithmetic.
• The compiler will sometimes use the integer
PASS
instruction (
"Rx = PASS Ry;"
) to copy a floating-point value from one register to another. This operation will result in a 40-bit value being truncated to a 32-bit value.
It is not normally possible to predict whether the compiler will use this instruction-it depends on many factors,
CrossCore Embedded Studio 2.2.0
2–215
Support for 40-Bit Arithmetic
such as the code sequence being compiled and whether optimization has been enabled. However, using the
extra-precision
switch prevents use of the integer
PASS
instruction for floating-point values. For more information, see
.
• By default, data memory (including the stack) is configured to access 32-bit words, so any data stored to memory will be truncated from 40 bits to 32 bits. It is not possible to anticipate exactly when the compiler will place data in memory (especially when the optimizer has been enabled), meaning that it is not possible to guarantee that all 40 bits of a calculation will be preserved. For example, when preserving the value of a local variable across a function call, the compiler can either store the variable on the stack (which truncates it) or store it in a preserved register (for example,
R3
which will preserve all 40 bits). As before, the behavior depends on many factors such as the code sequence and optimization.
For these reasons, it is recommended that 40-bit arithmetic is not used in C/C++ code.
Run-Time Library Functions That Use 40-Bit Arithmetic
The following run-time library functions use 40-bit arithmetic: cfft_mag cosf div fir
(the scalar-valued version from the header file filters.h
) rfft_mag iir
(the scalar-valued version from the header file filters.h
) rsqrtf ldiv fmodf sinf sqrtf
The compiler support functions for the following operations use 40-bit arithmetic: modulus operator floating point division integer division
A number of library functions do not themselves use 40-bit arithmetic but they invoke one or more of the above functions and may therefore generate less accurate results if they are interrupted: acosf cexpf gen_hamming rmsf asinf normf gen_hanning twidfftf cabsf polarf gen_harris twiffft cartesianf gen_blackman gen_kaiser
If the
-double-size-64
switch has not been specified, then the following functions are also affected: acos cexp asin cos cabs fmod cartesian norm
2–216
CrossCore Embedded Studio 2.2.0
C/C++ Compiler Language Extensions
polar sqrt rms rsqrt sin
SIMD Support
The SHARC processors supported by the compiler allow Single Instruction, Multiple Data (SIMD) execution.
When optimizing, the compiler can automatically exploit SIMD mode, subject to certain constraints being met. If the compiler is unable to automatically exploit SIMD mode, it will generate normal code (Single Instruction, Single
Data, "SISD"). You can also use pragmas and other facilities to inform the compiler when SIMD mode is appropriate.
This section contains:
•
A Brief Introduction to SIMD Mode
•
What the Compiler Can Do Automatically
•
What Prevents the Compiler From Automatically Exploiting SIMD Mode
•
How to Help the Compiler Exploit SIMD Mode
•
How to Prevent SIMD Code Generation
A Brief Introduction to SIMD Mode
This brief discussion is only concerned with aspects of SIMD architecture as they relate to the compiler. For full details on SIMD mode, refer to your processor's hardware reference manual.
In SIMD mode, the processor uses an additional computation unit operating in parallel with the first computation unit. This additional unit has its own register file. Whereas in SISD mode, only the first unit fetches values from memory, performs operations on them and stores the results back in memory, in SIMD mode, both units do this at once. The two units access adjacent memory locations, so that if the first unit accesses location M, the second unit will access location M+1. The operation performed in both units will be the same, but each unit will be performing the operation on its own data.
Because the processor is performing two operations in parallel, SIMD mode can provide double the computational throughput of SISD mode. However, because SIMD mode accesses adjacent memory locations, the compiler can only exploit SIMD mode when the source code being compiled supports such access patterns.
SIMD is a processor mode. For a given compute instruction I, if the processor is in SISD mode, the processor will execute I as a SISD instruction, on the first computation unit. In SIMD mode, the processor will execute the same instruction I as a SIMD instruction, executing it on both computation units. (Not all instructions behave differently in SISD and SIMD modes; for example, address arithmetic, which is not executed by the processor's computation unit, is not affected by the processor mode.)
What the Compiler Can Do Automatically
To exploit SIMD mode, you must enable the compiler optimizer.
CrossCore Embedded Studio 2.2.0
2–217
SIMD Support
No C/C++ language extensions are necessary for SIMD use; rather, the compiler can automatically generate SIMD code from standard C/C++ as long as there is sufficient information to indicate that the transformation does not alter the semantics of the source code.
There is a cost associated with switching between SISD and SIMD modes, so the compiler will generate code that exploits SIMD mode when it can determine that the source code meets the appropriate constraints and that the improvement in performance is likely to outweigh the cost of switching the modes.
Because of the cost of switching between modes, the compiler is most likely to generate SIMD code within loops, as the time spent within the loop in SIMD mode generally outweighs the mode-switching costs outside the loop. In contrast, SIMD mode is relatively rare in linear code, as the access patterns typically do not allow for many operations in a given mode before the mode must be switched again.
What Prevents the Compiler From Automatically Exploiting SIMD Mode
The compiler will verify that the source code is suitable for SIMD mode before transforming it. There are a number of reasons why a given piece of source code may not be suitable for SIMD mode, including:
• The memory access patterns are not suitable, e.g. they do not access adjacent memory locations.
• The number of consecutive operations that can exploit SIMD mode are insufficient to justify the cost of switch into SIMD and back to SISD.
• Some of the code is conditional, and the compiler cannot implement them with conditional instructions.
• The code contains function calls that cannot be inlined.
• The code is a loop that contains dependencies between successive iterations, i.e. there is an operation in iteration N+1 that depends on the result of an operation in iteration N.
• The code accesses memory locations that are not double-word aligned (ADSP-2116x processors only).
• The compiler cannot be certain that input and output buffer pointers do not point to the same array.
• The data being accessed might end up in external memory, and the target processor has external memory which does not support SIMD accesses, i.e. the target processor is one of ADSP-2116x, ADSP-2137x, ADSP-21367,
ADSP-21368, or ADSP-21369.
• The code contains asm
statements: the compiler has no knowledge of the instructions executed by asm
statements, so cannot automatically determine whether the instructions would be safe in SIMD mode, unless you let the compiler know that an asm
statement is safe for SIMD mode using the
-annotate-loop-instr
.
When the compiler detects such problems, it automatically avoids using SIMD mode, and generates normal SISD mode code. The compiler will not generate SIMD code for source code where the compiler can determine that the resulting SIMD code would not be a valid representation of the source.
If the compiler determines that the only reason why the code is not suitable for SIMD mode is because the data in question is not appropriately aligned, it also issues a warning to that effect; you may be able to modify your source code so that the compiler can see that the data is suitably aligned, in which case SIMD mode code will be possible.
2–218
CrossCore Embedded Studio 2.2.0
SIMD Support
For more information, see
How to Help the Compiler Exploit SIMD Mode .
If the compiler can neither prove that your code is suitable for SIMD nor prove that it is not, the compiler will take the conservative approach, and will generate SISD code.
How to Help the Compiler Exploit SIMD Mode
The
can write your source code so that the compiler can obtain the information it needs to verify that SIMD mode is safe for your application.
The compiler will not automatically attempt to generate SIMD code if:
• the optimizer is not enabled.
• SIMD generation has been explicitly disabled; see
How to Prevent SIMD Code Generation .
• the target processor supports external memory, and external memory does not support SIMD memory accesses.
Therefore, you should:
• Remove any switches that disable SIMD generation.
• Specify the
-loop-simd
switch or the
-linear-simd
switch, as required, to tell the compiler to attempt
SIMD generation, if your processor has external memory that is problematic for SIMD access.
This will mean that the compiler will attempt SIMD generation, but the compiler may still have difficulty if it cannot verify all the operations would be safe in SIMD mode.
In circumstances where your application is suitable for SIMD, but the compiler cannot prove this, the compiler will
construct, and tells the compiler that for all memory accesses, you have verified that:
• where necessary the accesses are suitably aligned for SIMD mode.
• there are no accesses that rely on data stored during the previous iteration of the loop.
• the accesses do not alias each other.
• the code is not accessing external memory (if that is problematic for SIMD operations on the target processor).
• if there are any circular buffers used in the loop, the circular buffer length consists of an even number of elements, and the initial value of the pointer is aligned to point to an even element.
The pragma will not force the compiler to generate SIMD code if the compiler can prove that the source code is not
tells the compiler that it is safe to proceed with SIMD mode code generation.
CrossCore Embedded Studio 2.2.0
2–219
SIMD Support
If your loop contains asm
statements, this is normally a barrier to SIMD generation, since the compiler cannot tell whether the instructions within the asm
statement would be valid in SIMD mode. However, you can specify the
switch to tell the compiler that, for loops with the SIMD_for pragma, asms should be considered safe.
NOTE: SIMD accesses are not supported to external memory on ADSP-2116x, ADSP-2137x, ADSP-21367,
ADSP-21368 or ADSP-21369 processors, so to obtain SIMD code for these processors, you must use
switch to enable SIMD code generation, and you must ensure the associated data is not mapped to external memory.
How to Prevent SIMD Code Generation
To guarantee that the compiler does not generate SIMD code even when possible, use one of the following:
•
Accessing External Memory on ADSP-2126x and ADSP-2136x Processors
On the ADSP-2126x and some ADSP-2136x processors, it is not possible to access external memory directly from the processor core. The compiler provides some facilities to allow access to variables in external memory from C/C+
+ code, and to reduce the possibility of errors due to incorrect data placement.
Link-Time Checking of Data Placement
Data which is placed in external memory on ADSP-2126x and 2136x processors must be defined using the
DMAONLY
qualifier of the section
or default_section
pragmas ( #pragma section/#pragma default_section ).
For example:
#pragma section("seg_extmem1", DMAONLY) int extmem1[100];
The linker will perform additional checks to ensure that data marked as
DMAONLY
is not placed in internal memory, and that "normal" data is not placed in external memory. If data is placed incorrectly, the linker will issue an error.
Refer to the Linker and Utilities Manual for information for more information.
Inline Functions for External Memory Access
Two inline functions, read_extmem
and write_extmem
, are provided to transfer data between internal and external memory. A full description of these functions is provided in the C/C++ Library Manual for SHARC Processors.
2–220
CrossCore Embedded Studio 2.2.0
Preprocessor Features
Preprocessor Features
C/C++ preprocessor are used by CCES to control the programming environment. The cc21k
compiler provides standard preprocessor functionality, as described in any C text. The following extensions to standard C are also supported including several features of the
•
Predefined Preprocessor Macros
•
Preprocessor Generated Warnings
•
NOTE: The compiler’s preprocessor is an integral part of the compiler; it is not the preprocessor described in the
Assembler and Preprocessor Manual.
This section contains:
•
Predefined Preprocessor Macros
•
Predefined Preprocessor Macros
The Predefined Preprocessor Macro Listing table describes the predefined preprocessor macros.
cc21k
defines a hierarchy of macros that can be used in code to identify the processor for which you are compiling.
These macros identify the processor by:
• Architecture, to identify the processor as ADSP-21xxx or ADSP-SCxxx (by the
__ADSP21000___
macro)
• The Instruction Set Architecture (ISA) revision supported (via the
__ADSPSHARC__
macro)
• Processor family (e.g. the
__ADSP214xx__
,
__ADSP2146x__
macros)
• Family that use a common library build (e.g. the
__ADSP21469_FAMILY__
macro)
• Processor itself (e.g. the
__ADSP21467__
macro)
Table 2-45: Predefined Preprocessor Macro Listing
Macro Function
__2116x__
__2126x__
When compiling for the ADSP-21160 or ADSP-21161 processors, cc21k
defines
__2116x__ as 1. This macro has been deprecated; please use the
__ADSP2116x__
macro instead.
When compiling for the ADSP-21261, ADSP-21262, or ADSP-21266 processors, cc21k
defines
__2126x__
as 1. This macro has been deprecated; please use the
__ADSP2126x__
macro instead.
CrossCore Embedded Studio 2.2.0
2–221
Preprocessor Features
Table 2-45: Predefined Preprocessor Macro Listing (Continued)
Macro Function
__213xx__
__2136x__
__2137x__
__214xx__
__2146x__
__2147x__
__2148x__
__ADSP21000__
__ADSP21160__
__ADSP21160_FAMILY__
__ADSP21161__
__ADSP21161_FAMILY__
__ADSP2116x__
When compiling for the ADSP-21362, ADSP-21363, ADSP-21364, ADSP-21365,
ADSP-21366, ADSP-21367, ADSP-21368, ADSP-21369, ADSP-21371 or ADSP-21375 processors, cc21k
defines
__2136x__
and
__213xx__
as 1. This macro has been deprecated; please use the
__ADSP213xx__
macro instead.
When compiling for the ADSP-21362, ADSP-21363, ADSP-21364, ADSP-21365,
ADSP-21366, ADSP-21367, ADSP-21368 or ADSP-21369 processors, cc21k
defines
__2136x__
as 1. This macro has been deprecated; please use the
__ADSP2136x__
macro instead.
When compiling for the ADSP-21371 and ADSP-21375 processors, cc21k
defines
__2137x__
as 1. This macro has been deprecated; please use the
__ADSP2137x__
macro instead.
When compiling for the ADSP-2146x, ADSP-2147x or ADSP-2148x processors, cc21k
defines
__214xx__
as 1. This macro has been deprecated; please use the
__ADSP214xx__
macro instead.
When compiling for the ADSP-21467 or ADSP-21469 processors, cc21k
defines
__2146x__ as 1. This macro has been deprecated; please use the
__ADSP2146x__
macro instead.
When compiling for the ADSP-21477, ADSP-21478, or ADSP-21479 processors, cc21k
defines
__2147x__
as 1. This macro has been deprecated; please use the
__ADSP2147x__
macro instead.
When compiling for the ADSP-21483, ADSP-21486, ADSP-21487, ADSP-21488, or
ADSP-21489 processors, cc21k
defines
__2148x__
as 1. This macro has been deprecated; please use the
__ADSP2148x__
macro instead.
cc21k
always defines
__ADSP21000__
as 1.
cc21k
defines
__ADSP21160__
as 1 when you compile with the
-proc ADSP-21160
command-line switch.
When compiling for the ADSP-21160 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21160_FAMILY__
,
__ADSP2116x__
,
__ADSP211xx__
and
__SIMDSHARC__
.
When compiling for the ADSP-21160 processor, cc21k
defines
__ADSP21160_FAMILY__
as
1.
cc21k
defines
__ADSP21161__
as 1 when you compile with the
-proc ADSP-21161
command-line switch.
When compiling for the ADSP-21161 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21161_FAMILY__
,
__ADSP2116x__
,
__ADSP211xx__
and
__SIMDSHARC__
.
When compiling for the ADSP-21161 processor, cc21k
defines
__ADSP21161_FAMILY__
as
1.
When compiling for the ADSP-21160 or ADSP-21161 processors, cc21k
defines
__ADSP2116x__
as 1.
2–222
CrossCore Embedded Studio 2.2.0
Preprocessor Features
Table 2-45: Predefined Preprocessor Macro Listing (Continued)
Macro Function
__ADSP211xx__
__ADSP21261__
__ADSP21262__
__ADSP21266__
__ADSP21266_FAMILY__
__ADSP2126x__
__ADSP212xx__
__ADSP21362__
__ADSP21362_FAMILY__
__ADSP21363__
__ADSP21364__
When compiling for the ADSP-21160 or ADSP-21161 processors, cc21k
defines
__ADSP211xx__
as 1.
cc21k
defines
__ADSP21261__
as 1 when you compile with the
-proc ADSP-21261
command-line switch.
When compiling for the ADSP-21261 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21266_FAMILY__
,
__ADSP2126x__
,
__ADSP212xx__
, and
__SIMDSHARC__
.
cc21k
defines
__ADSP21262__
as 1 when you compile with the
-proc ADSP-21262
command-line switch.
When compiling for the ADSP-21262 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21266_FAMILY__
,
__ADSP2126x__
,
__ADSP212xx__
, and
__SIMDSHARC__
.
cc21k
defines
__ADSP21266__
as 1 when you compile with the
-proc ADSP-21266
command-line switch.
When compiling for the ADSP-21266 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21266_FAMILY__
,
__ADSP2126x__
,
__ADSP212xx__
, and
__SIMDSHARC__
.
When compiling for the ADSP-21261, ADSP-21262 or ADSP-21266 processors, cc21k
defines
__ADSP21266_FAMILY__
as 1.
When compiling for the ADSP-21261, ADSP-21262 or ADSP-21266 processors, cc21k
defines
__ADSP2126x__
as 1.
When compiling for the ADSP-21261, ADSP-21262 or ADSP-21266 processors, cc21k
defines
__ADSP212xx__
as 1.
cc21k
defines
__ADSP21362__
as 1 when you compile with the
-proc ADSP-21362
command-line switch.
When compiling for the ADSP-21362 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21362_FAMILY__
,
__ADSP2136x__
,
__ADSP213xx__
and
__SIMDSHARC__
.
When compiling for the ADSP-21362, ADSP-21363, ADSP-21364, ADSP-21365 or
ADSP-21366 processors, cc21k
defines
__ADSP21362_FAMILY__
as 1.
cc21k
defines
__ADSP21363__
as 1 when you compile with the
-proc ADSP-21363
command-line switch.
When compiling for the ADSP-21363 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21362_FAMILY__
,
__ADSP2136x__
,
__ADSP213xx__
and
__SIMDSHARC__
.
cc21k
defines
__ADSP21364__
as 1 when you compile with the
-proc ADSP-21364
command-line switch.
CrossCore Embedded Studio 2.2.0
2–223
Preprocessor Features
Table 2-45: Predefined Preprocessor Macro Listing (Continued)
Macro
__ADSP21365__
__ADSP21366__
__ADSP21367__
__ADSP21367_FAMILY__
__ADSP21368__
__ADSP21369__
__ADSP2136x__
__ADSP21371__
__ADSP21371_FAMILY__
Function
When compiling for the ADSP-21364 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21362_FAMILY__
,
__ADSP2136x__
,
__ADSP213xx__
and
__SIMDSHARC__
.
cc21k
defines
__ADSP21365__
as 1 when you compile with the
-proc ADSP-21365
command-line switch.
When compiling for the ADSP-21365 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21362_FAMILY__
,
__ADSP2136x__
,
__ADSP213xx__
and
__SIMDSHARC__
.
cc21k
defines
__ADSP21366__
as 1 when you compile with the
-proc ADSP-21366
command-line switch.
When compiling for the ADSP-21366 processors, the following additional macros are defined as 1:
__ADSP21000__
,
__ADSP21362_FAMILY__
,
__ADSP2136x__
,
__ADSP213xx__
and
__SIMDSHARC__
.
cc21k
defines
__ADSP21367__
as 1 when you compile with the
-proc ADSP-21367
command-line switch.
When compiling for the ADSP-21367 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21367_FAMILY__
,
__ADSP2136x__
,
__ADSP213xx__
and
__SIMDSHARC__
.
When compiling for the ADSP-21367, ADSP-21368 or ADSP-21369 processors, cc21k
defines
__ADSP21367_FAMILY__
as 1.
cc21k
defines
__ADSP21368__
as 1 when you compile with the
-proc ADSP-21368
command-line switch.
When compiling for the ADSP-21368 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21367_FAMILY__
,
__ADSP2136x__
,
__ADSP213xx__
and
__SIMDSHARC__
.
cc21k
defines
__ADSP21369__
as 1 when you compile with the
-proc ADSP-21369
command-line switch.
When compiling for the ADSP-21369 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21367_FAMILY__
,
__ADSP2136x__
,
__ADSP213xx__
and
__SIMDSHARC__
.
When compiling for the ADSP-21362, ADSP-21363, ADSP-21364, ADSP-21365,
ADSP-21366, ADSP-21367, ADSP-21368 or ADSP-21369 processors, cc21k
defines
__ADSP2136x__
as 1.
cc21k
defines
__ADSP21371__
as 1 when you compile with the
-proc ADSP-21371
command-line switch.
When compiling for the ADSP-21371 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21371_FAMILY__
,
__ADSP2137x__
,
__ADSP213xx__
and
__SIMDSHARC__
.
When compiling for the ADSP-21371 or ADSP-21375 processors, cc21k
defines
__ADSP21371_FAMILY__
as 1.
2–224
CrossCore Embedded Studio 2.2.0
Preprocessor Features
Table 2-45: Predefined Preprocessor Macro Listing (Continued)
Macro Function
__ADSP21375__
__ADSP2137x__
__ADSP213xx__
__ADSP21467__
__ADSP21469__
__ADSP21469_FAMILY__
__ADSP2146x__
__ADSP21477__
__ADSP21478__
__ADSP21479__ cc21k
defines
__ADSP21375__
as 1 when you compile with the
-proc ADSP-21375
command-line switch.
When compiling for the ADSP-21375 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21371_FAMILY__
,
__ADSP2137x__
,
__ADSP213xx__
and
__SIMDSHARC__
.
When compiling for the ADSP-21371 or ADSP-21375 processors, cc21k
defines
__ADSP2137x__
as 1.
When compiling for the ADSP-21362, ADSP-21363, ADSP-21364, ADSP-21365,
ADSP-21366, ADSP-21367, ADSP-21368, ADSP-21369, ADSP-21371, or ADSP-21375 processors, cc21k
defines
__ADSP213xx__
as 1.
cc21k
defines
__ADSP21467__
as 1 when you compile with the
-proc ADSP-21467
command-line switch.
When compiling for the ADSP-21467 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21469_FAMILY__
,
__ADSP2146x__
,
__ADSP214xx__
and
__SIMDSHARC__
.
cc21k
defines
__ADSP21469__
as 1 when you compile with the
-proc ADSP-21469
command-line switch.
When compiling for the ADSP-21469 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21469_FAMILY__
,
__ADSP2146x__
,
__ADSP214xx__
and
__SIMDSHARC__
.
When compiling for the ADSP-21467 or ADSP-21469 processors, cc21k
defines
__ADSP21469_FAMILY__
as 1.
When compiling for the ADSP-21467 or ADSP-21469 processors, cc21k
defines
__ADSP2146x__
as 1.
cc21k
defines
__ADSP21477__
as 1 when you compile with the
-proc ADSP-21477
command-line switch.
When compiling for the ADSP-21477 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21479_FAMILY__
,
__ADSP2147x__
,
__ADSP214xx__
and
__SIMDSHARC__
.
cc21k
defines
__ADSP21478__
as 1 when you compile with the
-proc ADSP-21478
command-line switch.
When compiling for the ADSP-21478 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21479_FAMILY__
,
__ADSP2147x__
,
__ADSP214xx__
and
__SIMDSHARC__
.
cc21k
defines
__ADSP21479__
as 1 when you compile with the
-proc ADSP-21479
command-line switch.
When compiling for the ADSP-21479 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21479_FAMILY__
,
__ADSP2147x__
,
__ADSP214xx__
and
__SIMDSHARC__
.
CrossCore Embedded Studio 2.2.0
2–225
Preprocessor Features
Table 2-45: Predefined Preprocessor Macro Listing (Continued)
Macro Function
__ADSP21479_FAMILY__
__ADSP2147x__
__ADSP21483__
__ADSP21486__
__ADSP21487__
__ADSP21488__
__ADSP21489__
__ADSP2148x__
__ADSP214xx__
__ADSP21584__
When compiling for the ADSP-21477, ADSP-21478, ADSP-21479, ADSP-21483,
ADSP-21486, ADSP-21487, ADSP-21488 or ADSP-21489 processors, cc21k
defines
__ADSP21479_FAMILY__
as 1.
When compiling for the ADSP-21477. ADSP-21478 or ADSP-21479 processors, cc21k
defines
__ADSP2147x__
as 1.
cc21k
defines
__ADSP21483__
as 1 when you compile with the
-proc ADSP-21483
command-line switch.
When compiling for the ADSP-21483 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21479_FAMILY__
,
__ADSP2148x__
,
__ADSP214xx__
and
__SIMDSHARC__
.
cc21k
defines
__ADSP21486__
as 1 when you compile with the
-proc ADSP-21486
command-line switch.
When compiling for the ADSP-21486 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21479_FAMILY__
,
__ADSP2148x__
,
__ADSP214xx__
and
__SIMDSHARC__
.
cc21k
defines
__ADSP21487__
as 1 when you compile with the
-proc ADSP-21487
command-line switch.
When compiling for the ADSP-21487 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21479_FAMILY__
,
__ADSP2148x__
,
__ADSP214xx__
and
__SIMDSHARC__
.
cc21k
defines
__ADSP21488__
as 1 when you compile with the
-proc ADSP-21488
command-line switch.
When compiling for the ADSP-21488 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21479_FAMILY__
,
__ADSP2148x__
,
__ADSP214xx__
, and
__SIMDSHARC__
.
cc21k
defines
__ADSP21489__
as 1 when you compile with the
-proc ADSP-21489
command-line switch.
When compiling for the ADSP-21489 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSP21479_FAMILY__
,
__ADSP2148x__
,
__ADSP214xx__
and
__SIMDSHARC__
.
When compiling for the ADSP-21483, ADSP-21486, ADSP-21487, ADSP-21488 or
ADSP-21489 processors, cc21k
defines
__ADSP2148x__
as 1.
When compiling for the ADSP-21467, ADSP-21469, ADSP-21477, ADSP-21478,
ADSP-21479, ADSP-21483, ADSP-21486, ADSP-21487, ADSP-21488, or ADSP-21489 processors, cc21k
defines
__ADSP214xx__
as 1.
cc21k
defines
__ADSP21584__
as 1 when you compile with the
-proc ADSP-21584
command-line switch.
When compiling for the ADSP-21584 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSPSC589_FAMILY__
,
__ADSP2158x__
,
__ADSP215xx__
,
__SIMDSHARC__
and
__BA_SHARC__
.
2–226
CrossCore Embedded Studio 2.2.0
Preprocessor Features
Table 2-45: Predefined Preprocessor Macro Listing (Continued)
Macro Function
__ADSP21587__
__ADSP2158x__
__ADSP215xx__
__ADSPSC582__
__ADSPSC584__
__ADSPSC587__
__ADSPSC589__
__ADSPSC589_FAMILY__
__ADSPSC58x__
__ADSPSC5xx__
__ADSPSHARC__ cc21k
defines
__ADSP21587__
as 1 when you compile with the
-proc ADSP-21587
command-line switch.
When compiling for the ADSP-21587 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSPSC589_FAMILY__
,
__ADSP2158x__
,
__ADSP215xx__
,
__SIMDSHARC__
and
__BA_SHARC__
.
When compiling for the ADSP-21584, ADSP-21587, ADSP-SC582, ADSP-SC584, ADSP-
SC587 or ADSP-SC589 processors, cc21k
defines
__ADSP2158x__
as 1.
When compiling for the ADSP-21584, ADSP-21587, ADSP-SC582, ADSP-SC584, ADSP-
SC587 or ADSP-SC589 processors, cc21k
defines
__ADSP215xx__
as 1.
cc21k
defines
__ADSPSC582__
as 1 when you compile with the
-proc ADSP-SC582
command-line switch.
When compiling for the ADSP-SC582 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSPSC589_FAMILY__
,
__ADSP2158x__
,
__ADSP215xx__
,
__ADSPSC58x__
,
__ADSPSC5xx__
,
__SIMDSHARC__
and
__BA_SHARC__
.
cc21k
defines
__ADSPSC584__
as 1 when you compile with the
-proc ADSP-SC584
command-line switch.
When compiling for the ADSP-SC584 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSPSC589_FAMILY__
,
__ADSP2158x__
,
__ADSP215xx__
,
__ADSPSC58x__
,
__ADSPSC5xx__
,
__SIMDSHARC__
and
__BA_SHARC__
.
cc21k
defines
__ADSPSC587__
as 1 when you compile with the
-proc ADSP-SC587
command-line switch.
When compiling for the ADSP-SC587 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSPSC589_FAMILY__
,
__ADSP2158x__
,
__ADSP215xx__
,
__ADSPSC58x__
,
__ADSPSC5xx__
,
__SIMDSHARC__
and
__BA_SHARC__
.
cc21k
defines
__ADSPSC589__
as 1 when you compile with the
-proc ADSP-SC589
command-line switch.
When compiling for the ADSP-SC589 processor, the following additional macros are defined as
1:
__ADSP21000__
,
__ADSPSC589_FAMILY__
,
__ADSP2158x__
,
__ADSP215xx__
,
__ADSPSC58x__
,
__ADSPSC5xx__
,
__SIMDSHARC__
and
__BA_SHARC__
.
When compiling for the ADSP-21584, ADSP-21587, ADSP-SC582, ADSP-SC584, ADSP-
SC587 or ADSP-SC589 processors, cc21k
defines
__ADSPSC589_FAMILY__
as 1.
When compiling for the ADSP-SC582, ADSP-SC584, ADSP-SC587 or ADSP-SC589 processors, cc21k
defines
__ADSPSC58x__
as 1.
When compiling for the ADSP-SC582, ADSP-SC584, ADSP-SC587 or ADSP-SC589 processors, cc21k
defines
__ADSPSC5xx__
as 1.
cc21k
defines
__ADSPSHARC__
when compiling for any ADSP-21xxx or ADSP-SCxxx processor. The value represents the revision of the ISA supported by the processor, according to the following list:
CrossCore Embedded Studio 2.2.0
2–227
Preprocessor Features
Table 2-45: Predefined Preprocessor Macro Listing (Continued)
Macro
_ADI_COMPILER
__ANALOG_EXTENSIONS__
_ADI_THREADS
__BASE_FILE__
__BA_SHARC__
__BYTE_ADDRESSING__
__CCESVERSION__
__cplusplus
__DATE__
__DOUBLES_ARE_FLOATS__
__ECC__
__EDG__
__EDG_VERSION__
__EXCEPTIONS
__FILE__
Function
ADSP-211xx/212xx/213xx processors (ISA revision 1.1.0): 0x110 ADSP-2146x processors (ISA revision 1.4.0): 0x140 ADSP-2147x/2148x processors (ISA revision 1.4.7): 0x147
ADSP-215xx/SC5xx processors (ISA revision 2.0.0): 0x200
Always defined as 1.
cc21k
defines
__ANALOG_EXTENSIONS__
as 1 unless MISRA-C is enabled.
Defined as 1 when compiling with the
-threads
switch.
The preprocessor expands this macro to a string constant which is the current source file being compiled as seen on the compiler command-line.
cc21k
defines
__BA_SHARC__
as 1 when compiling for processors that support byte-addressing (see the
Processors Supporting Byte-Addressing table in Processor Features
). The
__BA_SHARC__
macro is used to identify processors that are capable of byte-addressing independent of whether the
-char-size-8
or
-char-size-32
switch is being used.
cc21k
defines
__BYTE_ADDRESSING__
as 1 when the
-char-size-8
switch is used on processors that support byte-addressing (see the
Processors Supporting Byte-Addressing table in Processor Features ). When the
-char-size-32
The preprocessor defines this macro to be an eight-digit hexadecimal representation of the
CCES release, in the form
0xMMmmUUPP
, where:-
MM
is the major release number- mm
is the minor release number-
UU
is the update number-
PP
is the patch release number
For example, CrossCore Embedded Studio 1.0.2.0 would define __CCESVERSION__ as
0x01000200
.
cc21k
defines
__cplusplus
as 199711L when compiling C++ source files. It also gets defined as 1 for use in LDFs.
The preprocessor expands this macro into the preprocessing date as a string constant. The date string constant takes the form
Mmm dd yyyy
. (ANSI standard).
cc21k
defines
__DOUBLES_ARE_FLOATS__
as 1 when the size of the double
type is the same as the single-precision float
type. When the
-double-size-64
compiler switch is
), the macro is not defined.
cc21k
always defines
__ECC__
as 1.
cc21k
always defines
__EDG__
as 1. This signifies that an Edison Design Group compiler front-end is being used.
cc21k
always defines
__EDG_VERSION__
as an integral value representing the version of the compiler's front-end.
cc21k
defines
__EXCEPTIONS
as 1 when C++ exception handling is enabled (using the
command-line switch).
The preprocessor expands this macro into the current input file name as a string constant. The string matches the name of the file specified on the compiler's command-line or in a preprocessor
#include
command (ANSI standard).
2–228
CrossCore Embedded Studio 2.2.0
Preprocessor Features
Table 2-45: Predefined Preprocessor Macro Listing (Continued)
Macro Function
__FIXED_POINT_ALLOWED
__FLT64_SHARC__
_HEAP_DEBUG
Defined as 1 unless MISRA-C is enabled. It is defined to indicate that the native fixed-point
types support may be used. For more information, see Using Native Fixed-Point Types
.
cc21k
defines
__FLT64_SHARC__
as 1 when compiling for processors that support native double-precision floating-point arithmetic (see the
Processors Supporting Native Double-Preci-
sion Floating-Point Arithmetic table in
cc21k
defines
_HEAP_DEBUG
as 1 when Heap Debugging support is enabled, otherwise
_HEAP_DEBUG
is undefined. For more information, see Heap Debugging .
__HETEROGENEOUS_PROCESSOR__ cc21k
defines
__HETEROGENEOUS_PROCESSOR__
as 1 when the target processor comprises both SHARC and ARM cores (specifically, the ADSP-SC5xx family of processors), otherwise
__HETEROGENEOUS_PROCESSOR__
is undefined.
__IDENT__
The preprocessor expands
__IDENT__ to a string normally set using
#ident
.
_INSTRUMENTED_PROFILING cc21k
defines
_INSTRUMENTED_PROFILING as 1 when instrumented profiling is enabled
switch).
_LANGUAGE_C
__LINE__
_LONG_LONG
_MISRA_RULES
__NOSIMD__
__NORMAL_WORD_CODE__
__NUM_ARM_CORES__
__NUM_CORES__
__NUM_SHARC_CORES__
_PGO_HW
__RTTI cc21k
always defines
_LANGUAGE_C
as 1 when building C or C++ sources.
The preprocessor expands this macro into the current input line number as a decimal integer constant (ANSI standard).
cc21k
always defines
_LONG_LONG
as 1 when compiling C and C++ sources to indicate that
64-bit double word integer types are supported.
cc21k
defines _MISRA_RULES as 1 when compiling in MISRA-C mode.
cc21k
defines
__NOSIMD__
as 1 when SIMD code generation is disabled (using the
command-line switch).
When compiling for ADSP-214xx processors, cc21k
defines
__NORMAL_WORD_CODE__
as 1, when compiling in normal-word mode. For more information, see
.
cc21k
defines
__NUM_ARM_CORES__
to the number of ARM cores on the target part. For
ADSP-211xx/212xx/213xx/214xx/215xx processors, this is undefined; for ADSP-SC5xx processors, this is defined to 1.
cc21k
defines
__NUM_CORES__
to the total number of cores on the target part. For
ADSP-211xx/212xx/213xx/214xx processors, this is always 1; for ADSP-215xx and ADSP-
SC5xx processors except ADSP-SC582, this is defined to 3; for ADSP-SC582 this is defined to
2.
cc21k
defines
__NUM_SHARC_CORES__
to the total number of SHARC cores on the target part. For ADSP-211xx/212xx/213xx/214xx processors, this macro is not defined; for
ADSP-215xx and ADSP-SC5xx processors except ADSP-SC582, this is defined to 2; for ADSP-
SC582 this is defined to 1.
cc21k
defines
_PGO_HW
as 1 when you compile with the
commandline switches.
cc21k
defines
__RTTI
as 1 when C++ run-time type information is enabled (using the
-rtti command-line switch
).
CrossCore Embedded Studio 2.2.0
2–229
Preprocessor Features
Table 2-45: Predefined Preprocessor Macro Listing (Continued)
Macro Function
__SHORT_WORD_CODE__
__SIGNED_CHARS__
__SILICON_REVISION__
__SIMDSHARC__
__STDC__
__STDC_VERSION__
__TIME__
__VERSION__
__VERSIONNUM__
__WORKAROUNDS_ENABLED
When compiling for ADSP-214xx processors, cc21k
defines
__SHORT_WORD_CODE__
as 1, when compiling in short-word mode. This is the default when compiling for ADSP-214xx processors.
cc21k
defines
__SIGNED_CHARS__
as 1 indicating that plain char type variables are signed rather than unsigned. The macro is defined by default.
cc21k
defines
__SILICON_REVISION__
to a hexadecimal constant corresponding to the tar-
get processor revision. For more information, see Using the
switch.
cc21k
defines
__SIMDSHARC__
as 1. The
__SIMDSHARC__
macro is used to identify processors that are capable of executing SIMD code.
cc21k
always defines
__STDC__
as 1.
cc21k
defines
__STDC_VERSION__
as 199409L when compiling in C89 mode, and as
199901L when compiling in C99 mode.
The preprocessor expands this macro into the preprocessing time as a string constant. The date string constant takes the form hh:mm:ss
(ANSI standard).
The preprocessor expands this macro into a string constant containing the current compiler version.
The preprocessor defines
__VERSIONNUM__
as a numeric variant of
__VERSION__
constructed from the version number of the compiler. Eight bits are used for each component in the version number and the most significant byte of the value represents the most significant version component. As an example, a compiler with version 7.1.0.0 defines
__VERSIONNUM__
as
0x07010000 and 7.1.1.10 would define
__VERSIONNUM__
to be 0x0701010A.
cc21k
defines this macro to be 1 if any hardware workarounds are implemented by the compiler. This macro is set if the
switch has a value other than "none" or
if any specific workaround is selected by means of the
-workaround
workaround workaround_id[, workaround_id…]
Writing Macros
A macro is a name standing for a block of text that the preprocessor substitutes. Use the
#define
preprocessor command to create a macro definition. When the macro definition has arguments, the block of text the preprocessor substitutes can vary with each new set of arguments.
Compound Macros
Whenever possible, use inline functions rather than compound macros. If compound macros are necessary, define such macros to allow invocation like function calls. This will make your source code easier to read and maintain. If you want your macro to extend over more than one line, you must escape the newlines with backslashes. If your
macro contains a string literal and you are using the
switch, then you must escape the newline
twice, once for the macro and once for the string.
The following two code segments define two versions of the macro
SKIP_SPACES
:
2–230
CrossCore Embedded Studio 2.2.0
Writing Macros
/* SKIP_SPACES, regular macro */
#define SKIP_SPACES (p,limit) { \
char *lim = (limit); \
while ((p) != lim) { \
if (*(p)++ != ' ') { \
(p)--; \
break; \
} \
} \
}
/* SKIP_SPACES, enclosed macro */
#define SKIP_SPACES (p,limit) \
do { \
char *lim = (limit); \
while ((p) != lim) { \
if (*(p)++ != ' ') { \
(p)--; \
break; \
} \
} \
} while (0)
Enclosing the first definition within the do {} while (0)
pair changes the macro from expanding to a compound statement to expanding to a single statement. With the macro expanding to a compound statement, you sometimes need to omit the semicolon after the macro call in order to have a legal program. This leads to a need to remember whether a function or macro is being invoked for each call and whether the macro needs a trailing semicolon or not. With the do {…} while (0)
construct, you can pretend that the macro is a function and always put the semicolon after it.
For example,
/* SKIP_SPACES, enclosed macro, ends without `;' */ if (*p != 0)
SKIP_SPACES (p, lim); else
This expands to: if (*p != 0)
do {
} while (0);/* semicolon from SKIP_SPACES (); */ else
Without the do {} while (0)
construct, the expansion would be: if (*p != 0)
{
}
CrossCore Embedded Studio 2.2.0
2–231
C/C++ Run-Time Model and Environment
;/* semicolon from SKIP_SPACES (); */ else
C/C++ Run-Time Model and Environment
This section describes the SHARC processor C/C++ run-time model and run-time environment. The C/C++ runtime model, which applies to compiler-generated code, includes descriptions of layout of the stack, data access, and call/entry sequence. The C/C++ run-time environment includes the conventions that C/C++ routines must follow to run on SHARC processors. Assembly routines linked to C/C++ routines must follow these conventions.
NOTE: Analog Devices recommends that assembly programmers maintain stack conventions.
The run-time environment issues include the following items:
•
•
•
•
•
•
•
Controlling System Heap Size and Placement
•
•
Registers
The compiler makes use of the processor's registers in a variety of ways, as shown in the
Processor Register Categori-
zation table. Some registers fulfill more than role, depending on context.
This section contains:
•
•
•
•
•
•
•
2–232
CrossCore Embedded Studio 2.2.0
C/C++ Run-Time Model and Environment
•
•
Table 2-46: Processor Register Categorization
Register
R0
R1
R2
R3
R4
R5, R6, R7
R8
R9, R10, R11
R12
R13, R14, R15
S0-S15
I0, I1, I2, I3
I4
I5
I6
I7
I8, I9, I10, I11
I12
I13
I14, I15
B0, B1, B2, B3
B4
B5
B6, B7
B8, B9, B10, B11
B12, B13
B14, B15
L0-L5
L6, L7
CrossCore Embedded Studio 2.2.0
Categorization
Scratch Register, Result Register
Scratch Register, Long Result Register, Aggregate Result Register
Scratch Register
Preserved Register
Scratch Register, Parameter Register
Preserved Registers
Scratch Register, Parameter Register
Preserved Registers
Scratch Register, Parameter Register
Preserved Registers
Scratch Registers
Preserved Registers
Scratch Register
Preserved Register
Frame Pointer
Stack Pointer
Preserved Registers
Return Address Register, Scratch Register
Scratch Register
Preserved Registers
Preserved Registers
Scratch Register
Preserved Register
Stack Base Register
Preserved Registers
Scratch Registers
Preserved Registers
Preserved Registers, 0
Stack Length Register
2–233
Registers
Table 2-46: Processor Register Categorization (Continued)
Register Categorization
L8-L15
M0, M1, M2, M3
M4
M5
M6
M7
M8, M9, M10, M11
M12
M13
M14
M15
USTAT1-USTAT4
MRF, MRB
MSF, MSB
ASTAT, ASTATy
STKY, STKYy
MODE1, MODE2
MMASK
Preserved Registers, 0
Preserved Registers
Scratch Register
Dedicated Register, 0
Dedicated Register, 1
Dedicated Register, -1
Preserved Registers
Scratch Register
Dedicated Register, 0
Dedicated Register, 1
Dedicated Register, -1
User Registers
When the -char-size-8 switch is enabled on processors with support for byte-addressing (see the
Processors Supporting Byte-Addressing table in
Processor Features ), these are Scratch Registers. Otherwise they are Preserved Registers
Scratch Registers
Scratch Register
Scratch Register
Preserved Registers
Dedicated Register, 0xE03003
Dedicated Registers
The C/C++ run-time environment specifies a set of registers whose contents should not be changed except in specific defined circumstances. If these registers are changed, their values must be saved and restored. The dedicated register values must always be valid:
• On entry to any compiled function.
• On return to any compiled function.
• On exit from asm
statements and interrupt handlers.
The dedicated registers are
I6
,
I7
,
B6
,
B7
,
M5-M7
,
M13-M15
,
MODE1
,
MODE2
,
MMASK
, and all
L
registers.
•
I7
and
I6
are the stack pointer and the frame pointer registers, respectively.
B7
and
B6
indicate the base of the stack, and
L7
and
L6
indicate the stack length.
2–234
CrossCore Embedded Studio 2.2.0
Registers
• The remaining
L
registers define the lengths of the DAG’s circular buffers. The compiler uses the DAG registers, both in linear mode and in circular buffering mode. The compiler assumes that the
Length
registers are zero, both on entry to functions and on return from functions, and ensures this is the case when it generates calls or returns. Your application may modify the Length registers and use the circular buffers, but you must ensure that the Length registers are appropriately reset when calling compiled functions, or returning to compiled functions. Interrupt handlers must save and restore the Length registers, if using DAG registers.
• Registers
M5-M7
and
M13-M15
contain the fixed values of 0, 1 and -1 (
M5
and
M13
contain 0,
M6
and
M14 contain 1 and
M7
and
M15
contain -1).
•
MODE1
and
MODE2
are set to control the overall state of the processor, such as saturation, truncation and circular buffering. The compiler may change specific bits in these registers temporarily, in order to perform opera-
tions with specific semantics. For more information, see Mode Registers
.
• The
MMASK
register ensures that
MODE1
is set to the correct value before the interrupt dispatcher code is executed. It ensures that the following bits are cleared:
BR0
,
BR8
,
IRPTEN
,
ALUSAT
,
PEYEN
,
BDCST1
,
BDCST9
.
Mode Registers
The C/C++ run-time environment:
• Uses default bit order for DAG operations (no bit reversal)
• Uses the primary register set (not background set)
• Uses
.PRECISION=32
(32-bit floating-point) and
.ROUND_NEAREST
(round-to-nearest value) assembly directives
• Disables
ALU
saturation (
MODE1
register,
ALUSAT
bit = 0)
• Uses default
FIX
instruction rounding to nearest (
MODE1
register,
TRUNCATE=0
)
• Enables circular buffering by setting
CBUFEN
on
MODE1
Preserved Registers
These registers are also known as callee-preserved registers, as it is the callee's responsibility to ensure that these registers have the same value upon function return as they did upon entry to the function, regardless of whether the registers changed value in the meantime.
The C/C++ run-time environment specifies a set of registers whose contents must be saved and restored. Your assembly function must save these registers during the function's prologue and restore the registers as part of the function's epilogue. The call-preserved registers must be saved and restored if they are modified within the assembly function; if a function does not change a particular register, it does not need to save and restore the register.
The Callee-Preserved Registers table lists the default set of preserved registers.
CrossCore Embedded Studio 2.2.0
2–235
Registers
Table 2-47: Callee-Preserved Registers
B0
B1
B9
I0
I9
MODE2
M3
R5
R13
B10
I1
I10
MRB
M8
R6
R14
B2
B11
I2
I11
MRF
2
M9
R7
R15
B3
B14
I3
I14
M0
M10
R9
B5
B15
I5
I15
M1
M11
R10
B8
I8
MODE1
M2
R3
R11
*1 If you use a callee-preserved I register in an assembler routine called from an assembler routine, you must save and zero (clear) the corresponding L register as part of the function prologue. Then, restore the L register as part of the function epilogue.
*2 These registers are scratch registers when compiling with the
-char-size-8
switch on processors that support byte-addressing (see the
Processors Supporting Byte-Addressing table in Processor Features
).
NOTE: Functions may declare a non-standard partitioning of preserved/scratch registers through mechanisms such as
, which any calling function must respect.
Scratch Registers
Scratch registers are also known as caller-preserved registers, as it is the caller's responsibility to ensure that the value of these registers is preserved across function calls, if required.
The C/C++ run-time environment specifies a set of fregisters whose contents need not be saved and restored. Note that the contents of these registers are not preserved across function calls.
The Scratch Registers table lists the default set of scratch registers.
Table 2-48: Scratch Registers
B4
I4
S0
S9
MRB
B12
I12
S1
S10
MRF
1
B13
I13
S2
S11
R0
M4
S3
S12
R1
M12
S4
S13
R2
PX
S5
S14
R4
ASTATy
S6
S15
R8
STKYy
S7
MSB
R12
S8
MSF
*1 These registers are only scratch registers when compiling with the
-char-size-8
switch on processors that support byte-addressing (see the Processors Supporting Byte-Addressing table in
NOTE: Functions may declare a non-standard partitioning of preserved/scratch registers through mechanisms such as
, which any calling function must respect.
2–236
CrossCore Embedded Studio 2.2.0
Registers
Stack Registers
The C/C++ run-time environment reserves a set of registers for controlling the run-time stack. These registers may be modified for stack management in assembly functions, but they must be saved and restored. Never modify the stack registers within compiled functions. The Stack Registers table lists these registers.
Table 2-49: Stack Registers
Register
I7
I6
B6, B7
L6, L7
Value
Stack pointer
Frame pointer
Stack base address
Stack length
Modification Rules
Modify for stack management, restore when done.
Modify for stack management, restore when done.
Do not modify.
Do not modify.
NOTE: The run-time environment makes use of the processor's circular-buffer mechanism for stack overflow detection: if the stack pointer (
I7
) advances beyond the limits defined by its corresponding base (
B7
) and length (
L7
) registers, a circular-buffer interrupt occurs (
CB7I
). Thus, it is essential that the
B7
and
L7 registers are set correctly.
Parameter Registers
When calling a function, the first three words of parameter data are passed to the callee in registers
R4
,
R8
, and
R12
.
Return Registers
When a function returns a value back to its caller, if the returned value is 64 bits or smaller in size, the value is returned in the
R0
register and, if necessary, in the
R1
register. In the case of 64-bit return values, when the char-size is set to 8 bits,
R0
contains the least significant word and
R1
contains the most significant word, else
R0
contains the most significant word and
R1
contains the least significant word.
Aggregate Return Register
When a function returns a value back to its caller, if the returned value is larger than 64 bits in size, the value is returned in space reserved on the stack. This stack space is allocated by the caller, and a pointer to the start of the space is passed to the callee by the caller in the
R1
register.
Reservable Registers
The
-reserve register[, register …]
command-line switch lets you reserve registers for your inline assembly code or assembly language routines. If reserving an L register, you must reserve the corresponding I register; reserving an L register without reserving the corresponding I register can result in execution problems.
CrossCore Embedded Studio 2.2.0
2–237
Registers
You must reserve the same list of registers in all linked files; the whole project must use the same
-reserve
option.
The Reservable Registers table lists the registers that can be reserved with this switch. Note that the C run-time library does not use these registers, unless explicitly noted in the function's description in the C/C++ Library Manual
for SHARC Processors.
NOTE: Reserving registers can negatively influence the efficiency of compiled C/C++ code; use this option sparingly.
Registers in the
USTAT
class are never used by the compiler, except when explicitly directed via asm
statements.
Although
USTAT
registers may be included in clobber sets and specified by the
-reserve
switch, the compiler will not generate code to save or restore them.
Table 2-50: Reservable Registers
Register
I0, B0, L0, M0, I1, B1, L1, M1, I8,
B8, L8, M8, I9, B9, L9, M9, MRB,
USTAT1, USTAT2, USTAT3, USTAT4
Value
User-defined
Modification Rule
If not reserved, modify for temporary use, restore when done
If reserved, usage is not limited
Alternate Registers
With the exception of the background multiplier register
MRB
, which is visible at the same time as the foreground register
MRF
, the C/C++ run-time environment model does not use - or have any knowledge of - the alternate registers. To use these registers, you must first understand several aspects of the C/C++ run-time model.
The C/C++ run-time model uses register
I6
as the frame pointer and register
I7
as the stack pointer. Setting the
DAG
register set that contains
I6
and
I7
from a background register set to an active register set will have a direct affect on any stack operations, so you must ensure that
I6
and
I7
are set correctly before any stack operations are performed:
• Before switching to the alternate register set, save
I6
,
B6
and
L6
, and
I7
,
B7
and
L7
.
• After switching, restore
I6
,
B6
and
L6
, and
I7
,
B7
and
L7
to the saved values.
If the background
I6
and
I7
registers are active and an interrupt occurs, the C/C++ run-time model will use
I6
and
I7
to update the stack. This will result in faulty stack handling if they are not set properly.
NOTE: If you do not intend to set
I6
and
I7
, ensure that interrupts are disabled.
Managing the Stack
The C/C++ run-time environment uses the run-time stack to store automatic variables and return addresses. The stack is managed by a frame pointer (
I6
) and a stack pointer (
I7
) and grows downward in memory, moving from higher to lower addresses.
The stack pointer points to the location where the next value to be pushed will be stored, i.e. push operations write a value, then move the stack pointer to the next free address.
2–238
CrossCore Embedded Studio 2.2.0
C/C++ Run-Time Model and Environment
Whenever storing data on the stack, you must always decrement the stack pointer first, so that any data on the stack has an address that is higher than the current stack pointer value. Otherwise, data may be corrupted by interrupt handlers as they will save and restore context onto the top of the stack.
A stack frame is a section of the stack used to hold information about the current context of the C/C++ program.
Information in the frame includes local variables, compiler temporaries, and parameters for the next function.
The frame pointer serves as a base for accessing memory in the stack frame. Routines refer to locals, temporaries, and parameters by their offset from the frame pointer.
The Example Run-Time Stack figure shows an example section of a run-time stack. In the figure, the currently executing routine,
Current()
, was called by
Previous()
, and
Current()
in turn calls
Next()
. The state of the stack is as if
Current()
has pushed all the arguments for
Next()
onto the stack and is just about to call
Next()
.
Previous()'s Frame
.
..
Second (and last) word of
Current()'s stack parameters; argument e to Current()
First word of Current()'s stack parameters; argument d to
Current()
Frame pointer (i6) saved from Previous()
Current()'s Frame
Next()'s Frame
Return address
.
..
Local variables and saved registers for Current()
Last word of Next()'s stack parameters; argument z to
Next()
Second-to-last word of
Next()'s stack parameters; argument y to Next()
Empty (will hold stack pointer (i7))
.
.
.
Figure 2-2: Example Run-Time Stack
NOTE: Stack usage for passing any or all of a function’s arguments depends on the number and types of parameters to the function.
The prototypes for the functions in the Example Run-Time Stack figure are as follows:
void Current(int a, int b, int c, int d, int e);
void Next(int v, int w, int x, int y, int z);
CrossCore Embedded Studio 2.2.0
2–239
C/C++ Run-Time Model and Environment
In generating code for a function call, the compiler produces the following operations to create the called function's new stack frame:
• Loads the
R2
register with the frame pointer (in the
I6
register)
• Sets the frame pointer (the
I6
register) equal to the stack pointer (in the
I7
register)
• Uses a delayed-branch instruction to pass control to the called function
• Pushes the old frame pointer (held in the
R2
register) onto the run-time stack during the first branch delay slot
• Pushes the return address minus 1 (
PC
) onto the run-time stack during the second delayed-branch slot
The following instructions create a new stack frame. Note how the two initial register moves are incorporated into the cjump
instruction.
cjump my_function (DB);
/* where my_function is the called function */ dm(i7, m7) = r2; dm(i7, m7) = pc;
NOTE: When generating short-word code for processors that support VISA execution,
.label-1
is used instead of
PC
.
As you write assembly routines, note that the operations to create a stack frame are the responsibility of the called function, and you can use the entry
or leaf_entry
macros to perform these operations. For more information on these macros, see
.
In generating code for a function return, the compiler uses the following operations to restore the calling function's stack frame.
• Pops the return address off the run-time stack and loads it into the i12
register
• Uses a delayed-branch instruction to pass control to the calling function and jumps to the return address ( i12
+ 1
)
• Restores the caller's stack pointer, i7
register, by setting it equal to the current frame pointer (the i6
register), during the first branch delay slot
• Restores the caller's frame pointer, i6
register, by popping the previously saved frame pointer off the run-time stack and loading the value into i6
during the second delay-branch slot
The following instructions return from the function and restore the stack and frame pointers. Note that the restoring of the frame and stack pointers is incorporated into the rframe
instruction.
i12 = dm(-1, i6); jump (m14, i12) (DB); nop; rframe;
2–240
CrossCore Embedded Studio 2.2.0
C/C++ Run-Time Model and Environment
As you write assembly routines, note that the operations to restore stack and frame pointers are the responsibility of the called function, and you can use the exit
or leaf_exit
macros to perform these operations. For more
information on these macros, see C/C++/Assembly Support Macros .
In the following code examples (
Stack Management Example, C Code, Stack Management, Example ADSP-2116x
Assembly Code and Stack Management, Example ADSP-SC589 Assembly Code compiled with -char-size-8), observe how the function calls in the C code translate to stack management tasks in the compiled (assembly) version of the code. The comments have been added to the compiled code to indicate the function prologue and function epilogue.
NOTE: Stack Management, Example ADSP-2116x Assembly Code and Stack Management, Example ADSP-
SC589 Assembly Code compiled with -char-size-8 show non-optimized assembly code, for clarity; in optimized assembly code, the function prologue and epilogue instructions are often inter-mixed with instructions from the body of the function, for performance reasons.
Example 1. Stack Management, Example C Code
/* Stack management - C code */ int my_func(int, int); int arg_a, return_c; void foo(void)
{
int arg_b = 0;
return_c = my_func(arg_a, arg_b);
} int my_func(int arg_1, int arg_2)
{
return (arg_1 + arg_2)/2;
}
Example 2. Stack Management, Example ADSP-2116x Assembly Code
/* Stack management - C code compiled for ADSP-21160 */
.section/pm/DOUBLE32 seg_pmco;
.epctext:
_my_func:
/* Reserve space for local copies of parameters */
modify(i7,-2);
/* Save incoming parameters on stack */
dm(-2,i6)=r8;
dm(-3,i6)=r4;
/* Compute result = (arg_1+arg_2)/2 */
/* i.e. r0 = (r4+r8)/2 */
r8=r4+r8;
CrossCore Embedded Studio 2.2.0
2–241
C/C++ Run-Time Model and Environment
r12=lshift r8 by -31;
r2=r8+r12;
r0=ashift r2 by -1;
/* Function return sequence */
i12=dm(m7,i6);
jump (m14,i12) (db); rframe; nop;
._my_func.end:
.global _my_func;
.type _my_func,STT_FUNC;
_foo:
/* Reserve stack space for local variable arg_b */
modify(i7,-2);
/* Initialise arg_b to zero */
dm(-2,i6)=m13;
/* set first param register, r4, to arg_a */
r4=dm(_arg_a);
/* set second param register, r8, to arg_b, i.e. zero */
r8=m5;
/* Function call sequence */
cjump _my_func (db); dm(i7,m7)=r2; dm(i7,m7)=pc;
/* Store function result register, r0, into return_c */
(_return_c)=r0;
/* Function return sequence */
i12=dm(m7,i6);
jump (m14,i12) (db); rframe; nop;
._foo.end:
.global _foo;
.type _foo,STT_FUNC;
Stack Management, Example ADSP-SC589 Assembly Code compiled with -char-size-8 shows the same example compiled for ADSP-21589 with the
-char-size-8
switch. The code shows the differences in the symbol name mangling scheme used (a dot is append to the C identifier, rather than an underscore prefix), the use of VISA execution, and the use of the
(nw)
qualifier on the modify
instruction to specify that the offset is in words.
Example 3. Stack Management, Example ADSP-SC589 Assembly Code compiled with -char-size-8
/* Stack management - C code compiled for ADSP-SC589 */
.section/sw/DOUBLE32 seg_swco;
.epctext: my_func.:
/* Reserve space for local copies of parameters */
modify(i7,-2) (nw);
2–242
CrossCore Embedded Studio 2.2.0
C/C++ Run-Time Model and Environment
/* Save incoming parameters on stack */
dm(-2,i6)=r8;
dm(-3,i6)=r4;
/* Compute result = (arg_1+arg_2)/2 */
/* i.e. r0 = (r4+r8)/2 */
r8=r4+r8;
r12=lshift r8 by -31;
r2=r8+r12;
r0=ashift r2 by -1;
/* Function return sequence */
i12=dm(m7,i6);
jump (m14,i12) (db); rframe; nop;
.my_func..end:
.global my_func.;
.type my_func.,STT_FUNC; foo.:
/* Reserve stack space for local variable arg_b */
modify(i7,-2) (nw);
/* Initialise arg_b to zero */
dm(-2,i6)=m13;
/* Set first param register, r4, to arg_a */
r4=dm(arg_a.);
/* set second param register, r8, to arg_b, i.e. zero */
r8=m5;
/* Function call sequence */
cjump my_func. (db); dm(i7,m7)=r2; dm(i7,m7)=.LCJ0-1;
.LCJ0:
/* Store function result register, r0, into return_c */
dm(return_c.)=r0;
/* Function return sequence */
i12=dm(m7,i6);
jump (m14,i12) (db); rframe; nop;
.foo..end:
.global foo.;
.type foo.,STT_FUNC;
When writing code for processors that support byte-addressing, care must be taken to ensure that the frame and stack pointers i6
and i7
, and their corresponding base registers, b6
and b7
, are in the correct address space on entry to the called function. If calling a byte-addressed function from byte-addressed code, or a word-addressed function from word-addressed code, no address space translation will be necessary. However if calling a word-addressed function from byte-addressed code, or vice versa, the frame and stack registers must be converted to the
CrossCore Embedded Studio 2.2.0
2–243
C/C++ Run-Time Model and Environment
opposite address space before the call, and converted back again after returning to the callee. This can be achieved by using the b2w
and w2b
instructions to convert the i7
, b6
and b7
registers (it is not necessary to explicitly convert i6
since it is set from the i7
register during the call sequence). For example, the following sequence may be used to call a byte-addressed function from word-addressed code:
i7=w2b(i7); // convert stack pointer
b7=w2b(b7); // convert stack base
b6=w2b(b6); // convert frame base
cjump f. (db); // call byte-addressed function
dm(i7,m7)=r2;
dm(i7,m7)=.LCJ0-1;
.LCJ0:
b6=b2w(b6); // convert frame base
b7=b2w(b7); // convert stack base
i7=b2w(i7); // convert stack pointer
The next two sections, Transferring Function Arguments and Return Value
and Using Macros to Manage the Stack
, provide additional detail on function call requirements.
Function Call and Return
The transfer of control from a calling function to a called function, and returning control back again, is the joint responsibility of the calling function and the called function. The calling function has to pass the appropriate parameters, in registers or upon the stack, and in some cases has to provide space for the return value too. The called function has to keep its own temporary workspace separate from that of its caller. Both are required to ensure the integrity of some parts of the register set.
From the caller's point of view, the sequence of actions looks like this:
• If the function being called clobbers registers that contain values being used by the caller, the caller must save those values on the stack prior to making the call. In the
Example Run-Time Stack figure (see
), this is the "saved registers".
• If the called function returns an aggregate value that is returned via the stack, the caller must allocate stack space for this returned value. See
.
• If the called function takes parameters, the caller must set up those parameters, either in registers or on the stack. In the Example Run-Time Stack figure, this is "Next()'s stack parameters".
• The caller can now call the function.
• After the function returns, the caller must reset the stack pointer, to dispose of "Next()'s stack parameters", and restore any needed registers that might have been clobbered by the called function.
From the callee's point of view, the sequence of actions looks like this:
• Upon entry to the callee, the stack pointer will point to the top of the "First word of Current()'s stack parameters" area of the
Example Run-Time Stack figure. Note that this figure is viewed differently by caller and callee:
"Next()'s stack parameters" of the caller are "Current()'s stack parameters" as far as the callee is concerned.
2–244
CrossCore Embedded Studio 2.2.0
Function Call and Return
• If the function will be calling any further functions, it will have to save the Frame Pointer,
I6
.
• If the function needs any space for temporaries, it must create the "local variables" space on the stack.
• If the function needs to modify any registers that are not considered scratch registers, the function must save their current values prior to changing them. In other words, the function must preserve the value of any calleepreserved registers.
• The function may now perform its main task.
• Upon completion, the function may need to return a value to the caller. To do this, it must either load the value into the Result Registers, or store it to the stack.
• Prior to returning, the function must restore the value of any callee-preserved registers it has modified.
• The function must pop the "local variables" space from the stack, restore the
RETS
value, restore the caller's
Frame Pointer value (if changed) and restore the Stack Pointer to the value it had on entry to the function.
These operations are all combined; restoring the stack pointer to its previous position discards the "local variables" space.
• The function must retrieve the return address from the stack, where it was placed by the caller.
• Finally, the function can return control back to the caller.
Further details are given in the following sections:
•
Transferring Function Arguments and Return Value
•
Calling Assembly Subroutines From C/C++ Programs
•
Calling C/C++ Functions From Assembly Programs
•
•
Symbol Names in C/C++ and Assembly
•
Implementing C++ Member Functions in Assembly Language
•
Writing C/C++-Callable SIMD Subroutines
•
Mixed C/C++/Assembly Programming Examples
•
Exceptions Tables in Assembly Routines
Transferring Function Arguments and Return Value
The C/C++ run-time environment uses a set of registers and the run-time stack to transfer function parameters to assembly routines. Your assembly language functions must follow these conventions when they call or when they are called by C/C++ functions.
This section covers:
•
CrossCore Embedded Studio 2.2.0
2–245
Transferring Function Arguments and Return Value
•
Passing Parameters for Variable Argument Lists
•
•
•
Parameter and Return Value Examples
Basic Argument Passing
Because it is most efficient to use registers for passing parameters, the run-time environment attempts to pass the first three parameters in a function call using registers; it then passes any remaining parameters on the run-time stack. When the
-char-size-8
switch is used on processors that support byte-addressing, any parameters whose size is less than 32 bits are sign- or zero-extended to 32 bits by the caller.
The convention is to pass the function's first parameter in
R4
, the second parameter in
R8
, and the third parameter in
R12
. The following exceptions apply to this convention:
• If any parameter is larger than a single 32-bit word, then that parameter and all subsequent parameters are passed on the stack.
• Functions with variable-length parameter lists. For more information, see
Passing Parameters for Variable Argument Lists .
The Parameter and Return Value Transfer Registers table lists the rules that cc21k
uses for passing parameters in registers to functions and the rules that your assembly code must use for returns.
Table 2-51: Parameter and Return Value Transfer Registers
Register Parameter Type Passed Or Returned
R4
R8
Pass first parameter of 32 bits or less.
Pass second parameter of 32 bits or less.
R12 stack
R0
R0
,
R1
R1
Pass third parameter of 32 bits or less.
Pass fourth and remaining parameters; see exceptions to this rule on this page.
Return int
, long
, char
, float
, short
, pointer
, and one-word structure parameters.
Return long double, long long, unsigned long long and two-word structure parameters. When using char-size 8, place MSW in
R1
and LSW in
R0
, else place MSW in
R0
and LSW in
R1.
Return the address of results that are longer than two words;
R1
contains the first location in the block of memory containing the results.
Passing Parameters for Variable Argument Lists
The details of argument passing change slightly for variable argument lists.
For example, a function declared as follows may receive two or more arguments.
int varying(char *fmt, int flag, ...) { /* ... */ }
2–246
CrossCore Embedded Studio 2.2.0
Transferring Function Arguments and Return Value
Variable argument lists are processed using the macros defined in the stdarg.h
header file. The va_start() function obtains a pointer to the list of arguments which may be passed to other functions, or which may be walked by the va_arg()
macro.
To support this, the compiler passes the last named argument - flag
, in this case - on the stack, even if it would normally have been passed in
R4
,
R8
or
R12
(in this case, it would have been passed in
R8
). Any following arguments after the last named argument are also passed on the stack.
The va_start()
function can then take the address of the last non-varying argument ( flag
, in the example above), and va_arg()
can walk through the complete argument list on the stack.
Any arguments before the last named parameter are passed as normal. In this case, the first argument, fmt
, is passed in
R4
.
Passing a C++ Class Instance
A C++ class instance function parameter is always passed by reference when a copy constructor has been defined for the C++ class. If a copy constructor has not been defined for the C++ class then the C++ class instance function parameter is passed by value.
Consider the following example: class fr
{
public:
int v;
fr () {}
fr (const fr& rc1) : v(rc1.v) {}
}; extern int fn(fr x); fr Y; int main() {
return fn (Y);
}
The function call fn (Y)
in main
will pass the C++ class instance
Y
by reference because a copy constructor for that C++ class has been defined by fr (const fr& rc1) : v(rc1.v) {}
. If this copy constructor were removed, then
Y
would be passed by value.
Return Values
Values are usually returned from a called function to the caller in register
R0
, or in the register pair
R0-R1
, if necessary. The details are as follows:
CrossCore Embedded Studio 2.2.0
2–247
Transferring Function Arguments and Return Value
• Arithmetic values of size 32 bits or less are returned in
R0
. Values of less than 32 bits in size (which are only used when compiling with the
-char-size-8
switch on processors that support byte-addressing) are sign- or zero-extended to 32 bits by the called function.
• 64-bit arithmetic types are returned in
R0
and
R1
. When the char-size is set to 8 bits, the least significant bits are held in
R0
, else they are held in
R1
.
• Pointer values are returned in
R0
.
• Aggregate types of 32 bits or less are returned in
R0
.
• Aggregate types of 64 bits in size are returned in
R0
and
R1
, with the lower-addressed bytes in
R0
.
• Aggregate values larger than 64 bits in size are returned on the stack. The caller must allocate sufficient space on the stack within the caller's own frame, and load the address of the lowest-addressed part of this storage into register
R1
before calling the function.
Parameter and Return Value Examples
Consider the following function prototype example.
pass(int a, float b, char c, float d);
The first three arguments, a
, b
, and c
are passed in registers
R4
,
R8
, and
R12
, respectively. If the
-char-size-8 switch is being used on processors that support byte-addressing (see the Processors Supporting Byte-Addressing table in
c
is sign-extended to 32 bits before being stored in register
R12
. The fourth argument, d
, is passed on the stack.
This next example illustrates the effects of passing long double
arguments.
count(int w, long double x, char y, float z);
The first argument, w
, is passed in
R4
. Because the second argument, x
, is a multi-word argument, x
is passed on the stack. As a result, the remaining arguments, y
and z
, are also passed on the stack.
The following example illustrates the effects of variable arguments on parameter passing.
compute(float k, int l, char m,);
Here, the first two arguments, k
and l
, are passed in registers
R4
and
R8
. Because m
is the last named argument, m
is passed on the stack, as are all remaining variable arguments.
When arguments are placed on the stack, they are pushed on from right to left. The right-most argument is at a higher address than the left-most argument passed on the stack.
The following example shows how to access parameters passed on the stack.
tab(int a, char b, float c, int d, int e, long double f);
Parameters a
, b
, and c
are passed in registers because each of these parameters fits in a single-word. The remaining parameters, d
, e
, and f
, are passed on the stack.
2–248
CrossCore Embedded Studio 2.2.0
Function Call and Return
All parameters passed on the stack are accessed relative to the frame pointer, register
I6
. The first parameter passed on the stack, d
, is at address
I6 + 1
. To access it, you could use this assembly language statement.
r3=dm(1,i6);
The second parameter passed on the stack, e
, is at
I6 + 2
and can be accessed by the statement
r3=dm(2,i6);
The third parameter passed on the stack, f
, is a long double
. For processors that do not support byte-addressing, or when the
-char-size-32
switch is used, it is passed in big-endian format. In this case, the most significant word is at
I6 + 3
and the least significant word is at
I6 + 4
. If the
-char-size-8
switch is being used, the value is passed in little-endian format. In this case, the least significant word is at
I6 + 3
and the most significant word is at
I6 + 4
. For more information on the endianness of 64-bit types, see Endianness .
Calling Assembly Subroutines From C/C++ Programs
Before calling an assembly language subroutine from a C/C++ program, create a prototype to define the arguments for the assembly language subroutine and the interface from the C/C++ program to the assembly language subroutine. Even though it is legal to use a function without a prototype in C/C++, prototypes are a strongly-recommended practice for good software engineering. When the prototype is omitted, the compiler cannot perform argumenttype checking and assumes that the return value is of type integer and uses K&R promotion rules instead of ANSI promotion rules.
for ways to specify an assembly routine from C/C++.
The compiler will assume that the called assembly function will obey the run-time model's rules on register usage.
Refer to Registers for details.
NOTE: Functions may declare a non-standard partitioning of preserved/scratch registers through mechanisms such as
, which any calling function must respect. If the assembly function being called from C/C++ uses a non-standard clobber set, declare this in the prototype.
The compiler also assumes that the machine state does not change during execution of the assembly language subroutine. If you change modes within your assembly routine-for example, enabling saturation via the
MODE
registersensure that you restore them to their previous value before returning.
Calling C/C++ Functions From Assembly Programs
C/C++ functions can be called from assembly code. The situation is similar to that described in
Subroutines From C/C++ Programs
:
• The namespaces for C/C++ and assembly code are different; refer to
Symbol Names in C/C++ and Assembly
for details on how to specify a C/C++ function that can be referenced from assembly.
• The C/C++ function will obey the run-time model's rules described in
Registers , so your calling assembly code
must respect this, by not expecting caller-preserved registers to maintain their values over the call.
CrossCore Embedded Studio 2.2.0
2–249
Function Call and Return
• If your assembly code is passing parameters to the C/C++ function or receiving a return value from it, you must
follow the rules described in Transferring Function Arguments and Return Value
.
There are additional requirements you must fulfil when calling C/C++ code from assembly code, however:
• You must ensure that the system stack is valid, as described in
.
• You must ensure that
Dedicated Registers have their correct values.
• You must ensure that a system heap is set up. This is done for you if you are using the default or generated startup code and
.ldf
files. For more information, see Startup and Termination .
C/C++/Assembly Support Macros
This section describes macros defined in the asm_sprt.h
system header file. Use these macros in your assembly language modules, to simplify interfacing with C/C++ functions.
NOTE: Although the syntax for each macro does not change, the listing of asm_sprt.h
in this section may not be the most recent version. To see the current version, check the asm_sprt.h
file that came with your software package.
The following macros are available.
entry
The entry
macro expands into the function prologue for non-leaf functions. This macro should be the first line of any non-leaf assembly routine. Note that this macro is currently null, but it should be used for future compatibility.
exit
The exit
macro expands into the function epilogue for non-leaf functions. This macro should be the last line of any non-leaf assembly routine. Exit is responsible for restoring the caller's stack and frame pointers and jumping to the return address.
leaf_entry
The leaf_entry
macro expands into the function prologue for leaf functions. This macro should be the first line of any non-leaf assembly routine. Note that this macro is currently null, but it should be used for future compatibility.
leaf_exit
The leaf_exit
macro expands into the function epilogue for leaf functions. This macro should be the last line of any leaf assembly routine. leaf_exit
is responsible for restoring the caller's stack and frame pointers and jumping to the return address.
ccall(x)
The ccall
macro expands into a series of instructions that save the caller's stack and frame pointers and then jump to function x()
.
2–250
CrossCore Embedded Studio 2.2.0
Function Call and Return
reads(x)
The reads
macro expands into an instruction that reads a value off the stack and puts the value in the indicated register.
puts=x
The puts
macro expands into an instruction that pushes the value in register x
onto the stack.
gets(x)
The gets
macro expands into an instruction that reads a value off the stack and puts the value in the indicated register.
register = gets(x);
The value is located at an offset x
from the stack pointer.
alter(x)
The alter
macro expands into an instruction that adjusts the stack pointer by adding the immediate value x
. With a positive value for x
, alter
removes x words from the top of the stack. You could use alter
to clear x
number of parameters off the stack after a call.
save_reg
The save_reg
macro expands into a series of instructions that push the register file registers ( r0-r15
) onto the run-time stack.
restore_reg
The restore_reg
macro expands into a series of instructions that pop the register file registers ( r0-r15
) off the run-time stack.
Symbol Names in C/C++ and Assembly
You can use C/C++ symbols (function or variable names) in assembly routines and use assembly symbols in C/C++ code. This section describes how to name and use C/C++ and assembly symbols.
Only global C/C++ symbols can be referenced from assembly source.
To use a C/C++ function or variable in an assembly routine, declare it as global in the C program. Import the symbol into the assembly routine by declaring the symbol with the
.EXTERN
assembler directive.
To use an assembly function or variable in your C/C++ program, declare the symbol with the
.GLOBAL
assembler directive in the assembly routine and import the symbol by declaring the symbol as extern
in the C program.
The
C/C++ Naming Conventions for Symbols table shows several examples of the C/C++ and assembly interface naming conventions.
CrossCore Embedded Studio 2.2.0
2–251
Symbol Names in C/C++ and Assembly
Table 2-52: Parameter and Return Value Transfer Registers
In the C/C++ Program In the Assembly Subroutine
int c_var; /*declared global*/ void c_func(void); extern int asm_var; extern void asm_func(void);
.extern _c_var;
.type _c_var,STT_OBJECT; or
.extern c_var.;
.type c_var.,STT_OBJECT;
.global _c_func;
.type _c_func,STT_FUNC; or
.global c_func.;
.type c_func.,STT_FUNC;
.global _asm_var;
.type _asm_var,STT_OBJECT;
.byte = 0x00,0x00,0x00,0x00 or
.global asm_var.;
.type asm_var.,STT_OBJECT;
.byte = 0x00,0x00,0x00,0x00
.global _asm_func;
.type _asm_func,STT_FUNC;
_asm_func: or
.global asm_func.;
.type asm_func.,STT_FUNC; asm_func.:
The following sections cover different approaches:
•
C/C++ and Assembly: Extern Linkage
•
C and Assembly: Underscore Prefix or Dot Suffix
•
C/C++ and Assembly: Extern Linkage
The compiler supports the use of extern to declare symbol names in the different C, C++ and assembly namespaces.
For example:
extern int def_fn(void); // “_def_fn”, “def_fn.”, “__Z6def_fnv” or “_Z6def_fnv.”
extern “asm” int asm_fn(void); // “asm_fn” in assembly
extern “C” int c_fn(void); // “_c_fn” or “c_fn.” in assembly
When compiling your source in C or C++ mode, you can use extern
“asm”
or extern “C”
to specify which namespace you want your external symbols to use. Without the external linkage specifier, your symbol will use C namespace when compiling in C mode, and C++ namespace (mangled) when compiling in C++ mode.
C and Assembly: Underscore Prefix or Dot Suffix
As can be seen in C/C++ and Assembly: Extern Linkage
, when the compiler generates the assembly version of a Cnamespace symbol, it either prepends an underscore or appends a dot to the symbol name. When compiling with
2–252
CrossCore Embedded Studio 2.2.0
Symbol Names in C/C++ and Assembly
the
-char-size-8
switch on processors that support byte-addressing, the symbol name will have a dot suffix, otherwise it will have an underscore prefix. You can take advantage of this in your assembly source when referring to Cmode symbols, by adding the underscore or dot yourself.
Other Approaches
following approaches in your C/C++ source:
• When declaring functions, you can provide an alternative linkage name, using
#pragma linkage_name identifier
or
#pragma function_name identifier .
• When declaring variables in C, you can provide an alternative linkage name, using
Names in Generated Assembler (asm)
.
• When declaring functions in C, you can use
Function, Variable and Type Attribute Keyword (__attribute__) to
specify aliases of functions.
Implementing C++ Member Functions in Assembly Language
If an assembly language implementation is desired for a C++ member function, the simplest way is to use C++ to provide the proper interface between C++ and assembly.
In the class definition, write a simple member function to call the assembly-implemented function (subroutine).
This call can establish any interface between C++ and assembly, including passing a pointer to the class instance.
Since the call to the assembly subroutine resides in the class definition, the compiler inlines the call (inlining adds no overhead to compiler performance). From an efficiency point of view, the assembly language function is called directly from the user code.
As for any C++ function, ensure that a prototype for the assembly-implemented function is included in your program. As discussed in
Symbol Names in C/C++ and Assembly , you declare your assembly language subroutine's
name with the
.GLOBAL
directive in the assembly portion and import the symbol by declaring it as extern "C"
in the C++ portion of the code.
Note that using this method you avoid name mangling-you choose your own identifier for the external function.
Access to internal class information can be done either in the C++ portion or in the assembly portion. If the assembly subroutine needs only limited access to the class members, it is easier to select those in the C++ code and pass them as explicit arguments. This way the assembly code does not need to know how data is allocated within a class instance.
#include <stdio.h>
/* Prototype for external assembly routine: */
/* C linkage does not have name mangling */ extern "C" int cc_array(int); class CC {
private:
int av;
public:
CrossCore Embedded Studio 2.2.0
2–253
Function Call and Return
CC(){};
CC(int v) : av(v){};
int a() {return av;};
/* Assembly routine call: */
int array() {return cc_array(av);};
}; int main(void)
{
CC samples(11);
CC points;
points = CC(22);
int j, k;
j = samples.a();
k = points.array(); // Test asm call
printf ( "Val is %d\n", j );
printf ( "Array is %d\n", k );
return 1;
}
/* In a separate assembly file: */
.section /pm seg_pmco;
.global _cc_array;
_cc_array:
r0=r4+r4;
i12=dm(m7,i6);
jump(m14,i12) (db);
rframe;
nop;
Note that the above example is for processors that do not support byte-addressing, or when using the
-charsize-32
switch. When using the
-char-size-8
switch, the function cc_array
should appear in the assembly file as cc_array
.
Writing C/C++-Callable SIMD Subroutines
You can write assembly subroutines that use SIMD mode and call them from your C programs. The routine may use SIMD mode (
PEYEN
bit=1) for all code between the function prologue and epilogue, placing the chip in SISD mode (
PEYEN
bit =0) before the function epilogue or returning from the function.
NOTE: While it is possible to write subroutines that can be called in SIMD mode (i.e., the processor is in SIMD mode before the call and after the return), the compiler does not support a SIMD call interface. For example, trying to call a subroutine from a
#pragma SIMD_for loop
prevents the compiler from executing the loop in SIMD mode because the compiler does not support SIMD mode calls. For more informa-
.
2–254
CrossCore Embedded Studio 2.2.0
Function Call and Return
Because transfers between memory and data registers are doubled in SIMD mode (each explicit transfer has a matching implicit transfer), it is recommended that you access the stack in SISD mode to prevent corrupting the stack.
For more information on SIMD mode memory accesses, see the Memory chapter in the hardware reference manual for the appropriate processor.
Mixed C/C++/Assembly Programming Examples
This section shows examples of types of mixed C/C++/assembly programming in order of increasing complexity.
The examples in this section are as follows:
•
•
Using Macros to Manage the Stack
•
•
•
•
•
Using Registers for Arguments and Return
•
Using Non-Leaf Routines That Make Calls
•
Using Call Preserved Registers
Leaf routines are routines that return without making any calls. Non-leaf routines call other routines before returning to the caller.
You should use cc21k
to compile your C/C++ program and assemble your assembly language modules. cc21k
will invoke the compiler for C and C++ files, and will invoke the assembler for assembly files, defining common sets of macros, passing processor-specific switches and so on.
For example, the following cc21k
command line
cc21k my_prog.c my_sub1.asm runs cc21k
with the following modules listed in the
Modules for Running cc21k table. For more information, see
Table 2-53: Modules for Running cc21k
Module Description
my_prog.c
my_sub1.asm
Selects a C language source file for your program
Selects an assembly language module to be assembled and linked with your program
CrossCore Embedded Studio 2.2.0
2–255
Mixed C/C++/Assembly Programming Examples
Using Inline Assembly
The following example shows how to write a simple routine in ADSP-21xxx/SCxxx assembly code that properly interfaces to the C/C++ environment. It uses the asm()
construct to pass inline assembly code to the compiler.
int add(int x, int y, int z)
{
int res;
asm("%0=%2+%1; %0=%0+%3;":
"=d"(res):"d"(x),"d"(y),"d"(z):"astat");
return res;
}
For more information, see
Keyword for Specifying Names in Generated Assembler (asm)
.
Using Macros to Manage the Stack
The Subroutine Return Address Example - C Code and Subroutine Return Address Example - Assembly Code listings show how macros can simplify function calls between C, C++, and assembly functions. The assembly function uses the entry
, exit
, and ccall
macros to keep track of return addresses and manage the run-time stack.
For more information, see
C/C++/Assembly Support Macros .
Example 1. Subroutine Return Address Example - C Code
/* Subroutine Return Address Example-C code: */
/* assembly and c functions prototyped here */ void asm_func(void); void c_func(void);
/* c_var defined here as a global */
/* used in .asm file as _c_var */ int c_var=10;
/* asm_var defined in .asm file as _asm_var */ extern int asm_var; int main(void)
{
asm_func(); /* call to assembly function */
}
/* this function gets called from asm file */ void c_func(void)
{
if (c_var != asm_var)
exit(1);
else
exit(0);
}
Example 2. Subroutine Return Address Example - Assembly Code
2–256
CrossCore Embedded Studio 2.2.0
Mixed C/C++/Assembly Programming Examples
/* Subroutine Return Address Example-Assembly code: */
#include <asm_sprt.h>
.section/dm seg_dmda;
.var _asm_var=0; /* asm_var is defined here */
.global _asm_var; /* global for the C function */
.section/pm seg_pmco;
.global _asm_func; /* _asm_func is defined here */
.extern _c_func; /* c_func from the C file */
.extern _c_var; /* c_var from the C file */
_asm_func:
entry; /* entry macro from asm_sprt */
r8=dm(_c_var); /* access the global C var */
dm(_asm_var)=r8; /* set _asm_var to _c_var) */
ccall(_c_func); /* call the C function */
exit; /* exit macro from asm_sprt */
_asm_func.end:
The above example shows code that works with processors that do not support byte-addressing, or when using the
char-size-32
switch. When the
-char-size-8
switch is being used on processors that support byte-addressing, symbol names will be mangled according to the rules in
C and Assembly: Underscore Prefix or Dot Suffix .
Stack Alignment
By default the compiler will always create stack frame sizes that are an even numbers of words, so that the stack pointer preserves its alignment if it is double-word aligned. However calling a function from an interrupt handler or assembly routine that does not preserve stack alignment will result in a misaligned stack.
As a result, the compiler does not attempt to take advantage of the double-word alignment of the stack unless you tell the compiler that the stack always remains double-word aligned by using additional alignment assertions in your source code. Therefore there is no requirement to maintain double-word alignment in assembly code routines unless you want to keep aligned data on the stack. If you align local variables using
#pragma align
, for example, you must keep the stack pointer aligned to a double-word boundary in assembly routines that call into C code.
Using Scratch Registers
To write assembly functions that can be called from a C/C++ program, your assembly code must follow the conventions of the C/C++ run-time environment and use the conventions for naming functions. The dot()
assembly function described below demonstrates how to comply with these specifications. For more information, see
.
This function computes the dot product of two vectors. The two vectors and their lengths are passed as arguments.
Because the function uses only scratch registers (as defined by the run-time environment) for intermediate values and takes advantage of indirect addressing, the function does not need to save or restore any registers.
CrossCore Embedded Studio 2.2.0
2–257
Mixed C/C++/Assembly Programming Examples
/* dot(int n, dm float *x, pm float *y);
Computes the dot product of two floating-point vectors of length n. One is stored in dm and the other in pm. Length n must be greater than 2.*/
#include <asm_sprt.h>
.section/pm seg_pmco;
/* By convention, the assembly function name is the C function name with a leading underscore; "dot()" in C becomes "_dot" in assembly */
.global _dot;
_dot: leaf_entry; r0=r4-1,i4=r8;
/* Load first vector address into I register, and load r0 with length-1 */ r0=r0-1,i12=r12;
/* Load second vector address into I register and load r0 with length-2 (because the 2 iterations outside feed and drain the pipe */ f12=f12-f12,f2=dm(i4,m6),f4=pm(i12,m14);
/* Zero the register that will hold the result and start feeding pipe */ f8=f2*f4,f2=dm(i4,m6),f4=pm(i12,m14);
/* Second data set into pipeline, also do first multiply */ lcntr=r0, do dot_loop until lce;
/* Loop length-2 times, three-stage pipeline: read, mult, add */ dot_loop:
f8=f2*f4,f12=f8+f12,f2=dm(i4,m6),f4=pm(i12,m14);
f8=f2*f4,f12=f8+f12;
f0=f8+f12;
/* drain the pipe and end with the result in r0, where it'll be returned */ leaf_exit;
/* restore the old frame pointer and return */
_dot.end:
The above example shows code that works with processors that do not support byte-addressing, or when using the
char-size-32
switch. When the
-char-size-8
switch is being used on processors that support byte-addressing, symbol names will be mangled according to the rules in
C and Assembly: Underscore Prefix or Dot Suffix .
2–258
CrossCore Embedded Studio 2.2.0
Mixed C/C++/Assembly Programming Examples
Using Void Functions
The simplest kind of assembly routine is one with no arguments and no return value, which corresponds to C/C++ functions prototyped as void my_function(void)
. Such routines could be used to monitor an external event or used to perform an operation on a global variable.
It is important when writing such assembly routines to pay close attention to register usage. If the routine uses any call-preserved or compiler reserved registers (as defined in the run-time environment), the routine must save the register and restore it before returning. Because the following example does not need many registers, this routine uses only scratch registers (also defined in the run-time environment) that do not need to be saved. For more information, see
.
Note that in the example all symbols that need to be accessed from C/C++ contain a leading underscore. Because the assembly routine name
_delay
and the global variable
_del_cycle
must both be available to C and C++ programs, they contain a leading underscore in the assembly code. A different mangling scheme is used when using the
-char-size-8
switch on processors that support byte-addressing. For more information, see Symbol Names in
.
/* Simple Assembly Routines Example - _delay */
/* void delay (void);
An assembly language subroutine to delay N cycles, where N is the value of the global variable del_cycle */
#include <asm_sprt.h>;
.section/pm seg_pmco;
.extern _del_cycle;
.global _delay;
_delay:
leaf_entry; /* first line of any leaf func */
r4 = dm(_del_cycle);
/* Here, register r4 is used because it is a scratch register and does not need to be preserved */
lcntr = r4, do d_loop until lce;
d_loop:
nop;
leaf_exit; /* last line of any leaf func */
_delay.end:
Using the Stack for Arguments
A more complicated kind of routine is one that has parameters but no return values. The following example adds
and Symbol Names in C/C++ and Assembly
.
/* Assembly Routines With Parameters Example - _add5 */
/* void add5 (int a, int b, int c, int d, int e);
An assembly language subroutine that adds 5 numbers */
CrossCore Embedded Studio 2.2.0
2–259
Mixed C/C++/Assembly Programming Examples
#include <asm_sprt.h>
.section/pm seg_pmco;
.extern _sum_of_5; /* variable where sum will be stored */
.global _add5;
_add5: leaf_entry;
/* the calling routine passes the first three parameters in registers r4, r8, r12 */ r4 = r4 + r8; /* add the first and second parameter */ r4 = r4 + r12; /* adds the third parameter */
/* the calling routine places the remaining parameters (fourth/fifth) on the run-time stack; these parameters can be accessed using the reads() macro */ r8 = reads(1); /* put the fourth parameter in r8 */ r4 = r4 + r8; /* adds the fourth parameter */ r8 = reads(2); /* put the fifth parameter in r8 */ r4 = r4 + r8; /* adds the fifth parameter */ dm(_sum_of_5) = r4;
/* place the answer in the global variable */ leaf_exit;
_add5.end:
The above example shows code that works with processors that do not support byte-addressing, or when using the
char-size-32
switch. When the
-char-size-8
switch is being used on processors that support byte-addressing, symbol names will be mangled according to the rules in
C and Assembly: Underscore Prefix or Dot Suffix .
Using Registers for Arguments and Return
There is another class of assembly routines in which the routines have both parameters and return values. The following example of such a routine adds two numbers and returns the sum. Note that this routine follows the runtime environment specification for passing function parameters (in registers
R4
and
R8
) and passing the return value
(in register
R0
).
For more information, see
and Symbol Names in C/C++ and Assembly .
/* Routine With Parameters & Return Value - _add2 */
/* int add2 (int a, int b);
An assembly language subroutine that adds two numbers and returns the sum */
#include <asm_sprt.h>
.section/pm seg_pmco;
.global _add2;
_add2:
leaf_entry;
2–260
CrossCore Embedded Studio 2.2.0
Mixed C/C++/Assembly Programming Examples
/* per the run-time environment, the calling routine passes the first two parameters passed in registers r4 and r8; the return value goes in register r0 */
r0 = r4 + r8;
/* add the first and second parameter, store in r0 */
leaf_exit;
_add2.end:
Using Non-Leaf Routines That Make Calls
A more complicated example, which calls another routine, computes the length of the hypotenuse of a right-angled triangle: z = x
2
+ y
2
Although it is straight-forward to develop your own function that calculates a square-root in ADSP-21xxx assembly language, the following example demonstrates how to call the square root function from the C/C++ run-time library, sqrtf
. In addition to demonstrating a C run-time library call, this example shows some useful calling mac-
ros. For more information, see Function Call and Return
. Function symbol names are discussed in Symbol Names in C/C++ and Assembly .
/* Non-Leaf Assembly Routines Example - _hypot */
/* float hypot(float x, float y); An assembly language subroutine to return z = (x^2 + y^2)^(1/2) */
#include <asm_sprt.h>
.section/pm seg_pmco;
.extern _sqrtf;
.global _hypot;
_hypot:
entry; /* first line of non-leaf routine */
f4 = f4 * f4;
f8 = f8 * f8;
f4 = f4 + f8;
/* f4 contains argument to be passed to sqrtf function */
ccall (_sqrtf);
/* use the ccall() macro to make a function call in a C environment; f0 contains the result returned by the _sqrtf function. In turn, _hypot returns the result to its caller in f0 (and it is already there) */
exit; /* last line of non-leaf routine */
_hypot.end:
CrossCore Embedded Studio 2.2.0
2–261
Mixed C/C++/Assembly Programming Examples
If a called function takes more than three single word parameters, the remaining parameters must be pushed onto the stack and popped off the stack after the function call. The following function could call the
_add5
routine
shown in Using the Stack for Arguments
. Note that the last parameter must be pushed on the stack first.
/* Non-Leaf Assembly Routines Example - _calladd5 */
/* int calladd5 (void);
An assembly language subroutine that
calls another routine with more than 3 parameters.
This example adds the numbers 1, 2, 3, 4, and 5. */
#include <asm_sprt.h>
.section/pm seg_pmco;
.extern _add5;
.extern _sum_of_5;
.global _calladd5;
_calladd5: entry;
r4 = 5;
/* the fifth parameter is stored in r4 for pushing onto stack */
puts = r4; /* put fifth parameter in stack */
r4 = 4;
/* the fourth parameter is stored in r4 for pushing onto stack */
puts = r4; /* put fourth parameter in stack */
r4 = 1; /* the first parameter is sent in r4 */
r8 = 2; /* the second parameter is sent in r8 */
r12 = 3; /* the third parameter is sent in r12 */
ccall (_add5);
/* use the ccall macro to make a function call in a C environment */
alter(2);
/* call the alter() macro to remove the two arguments from the stack */
r0 = dm(_sum_of_5);
/* _sum_of_5 is where add5 stored its result */
exit;
_calladd5.end:
Using Call Preserved Registers
Some functions need to make use of registers that the run-time environment defines as call preserved registers. These registers, whose contents are preserved across function calls, are useful for variables whose lifetime spans a function call. The following example performs an operation on the elements of a C array using call preserved registers. For more information, see
. Function and data symbol names are discussed in
.
/* Non-Leaf Assembly Routines Example - _pass_array */
/* void pass_array(float function(float),
float *array,
int length);
2–262
CrossCore Embedded Studio 2.2.0
Mixed C/C++/Assembly Programming Examples
An assembly language routine that operates on a C array */
#include <asm_sprt.h>
.section/pm seg_pmco;
.global _pass_array;
_pass_array:
entry;
puts=i8;
/* This function uses a call preserved register, i8, because
it could be used by multiple functions, and this way it does
not have to be stored for every function call */
r0=i1;
puts=r0; /* i1 is also call preserved */
i8=r4;
/* read the first argument, the address of the function to call */
i1=r8;
/* read the second argument, the C array containing the data to be processed */
r0=r12;
/* read third argument, the number of data points in the array */
lcntr=r0, do pass_array_loop until lce;
/* loop through data points */
f4=dm(i1,m5);
/* get data point from array, store it in f4 as a parameter for the function call */
r2=i6; i6=i7;
jump (m13,i8) (db);
dm(i7,m7)=r2;
dm(i7,m7)=pc; pass_array_loop:
dm(i1,m6)=f0;
/* store the return value back in the array */
i1=gets(1); /* restore the value of i1 */
i8=gets(2); /* restore the value of i8 */
exit;
_pass_array.end:
CrossCore Embedded Studio 2.2.0
2–263
Function Call and Return
Exceptions Tables in Assembly Routines
C++ functions can throw C++ exceptions, which must be caught by another function earlier in the call-stack. Part of this catching process involves unwinding the stack of intervening, still-active function calls. The C++ exception support library uses additional function details to perform this unwinding. The exception support gets this information from different places:
• When C++ modules are compiled with exceptions enabled by the
switch, the compiler generates the neces-
sary unwinding tables.
• When C modules are compiled, exceptions information is not usually necessary, but the compiler will generate unwinding information if the
-eh
switch is specified.
• Assembly modules are not compiled, so unwinding information must be supplied manually, if necessary.
Assembly functions rarely need to provide exception-unwinding information. It is only necessary when all of the following conditions apply:
• The assembly routine may be called by a C or C++ function.
• The assembly routine calls a C++ function (or a C function that may lead to a C++ function being called, while the assembly routine is still active).
• The called C++ function may throw an exception.
The assembly routine must allocate a stack frame as described in
Managing the Stack . On entry to the assembly
tiguous region within the stack frame, called the save area. Registers are saved at ascending addresses in the save area in the order given in the Function Exception Table Register Numbers table.
A word in the
.gdt
section must be initialized with the address of the function exceptions table. This word must be marked with the
.RETAIN_NAME
directive to prevent it being removed by linker data elimination. The function exceptions table itself must be initialized as illustrated in the Function Exceptions Table table.
3
8
1
2
Table 2-54: Function Exceptions Table
Offset (words) Size (words)
0 1
4
1
1
1
Description
Start address of the routine
First address after end of routine
Signed offset from frame pointer of register save area
Bit set indicating which registers are saved
Always zero. Indicates this is not C++ code
The bit set field of the function exceptions table contains a bit for each register. The bits corresponding to registers saved in the save area must be set to one and the other bits set to zero. The bit numbers corresponding to each register are given in the Function Exception Table Register Numbers table, where bit 0 is the least significant bit of
2–264
CrossCore Embedded Studio 2.2.0
Function Call and Return
the lowest addressed word, bit 31 the most significant bit of that word, bit 32 the least significant bit of the second lowest addressed word and so on.
Bit numbering may best be explained by the C code to test bit number, int wrd = r/32; int bit = lu << (r%32); if (bitset[wrd] & bit)
/* register r was saved */
Table 2-55: Function Exception Table Register Numbers
Register Bit Number
ASTAT
ASTATY
0
1
R0 - R15
S0 - S15
M0 - M15
B0 - B15
I0 - I15
L0 - L15
MRF
MSF
MRB
MSB
PX1, PX2
USTAT1 - USTAT4
2 - 17
18 -33
34 - 49
50 - 65
66 - 81
82 - 97
98
99
100
101
102 - 103
104 - 107
3
3
3
3
1
1
1
1
1
1
1
1
Words Taken in Save Area if Saved
1
1
This example shows an assembly routine with function exceptions table, showing how to write code for processors that do not support byte-addressing, or when using the
-char-size-32
switch.
.section/pm seg_pmco;
_asmfunc:
.LN._asmfunc:
modify(i7,-6); // allocate stack frame
// save area at I6-7
dm(-7,i6)=r5; // save_area[0] = r5
dm(-6,i6)=r6; // save_area[1] = r6
dm(-5,i6)=r7; // save_area[2] = r7
r2=i0; dm(-4,i6)=r2; // save_area[3] = i0
r2=i1; dm(-3,i6)=r2; // save_area[4] = i1
r2=i2; dm(-2,i6)=r2; // save_area[5] = i2
// use R5,R6,R7,I0,I1,I2, call a C++ function
i0=dm(-4,i6);
CrossCore Embedded Studio 2.2.0
2–265
Function Call and Return
i1=dm(-3,i6);
i2=dm(-2,i6);
r5=dm(-7,i6);
r6=dm(-6,i6);
r7=dm(-5,i6);
i12=dm(m7,i6);
jump (m14,i12) (db); rframe; nop;
.LN._asmfunc.end:
._asmfunc.end:
.global _asmfunc;
.type _asmfunc, STT_FUNC;
.section/dm .edt; // conventionally function exceptions
// tables go in .edt
.var .function_exceptions_table[8] =
.LN._asmfunc, // first address of _asmfunc
.LN._asmfunc.end, // first address after _asmfunc
-7, // offset of save area from I6
0x00000380, 0, 0x0000001c, 0,
// bit set, bits 7=R5,8=R6,9=R7,66=I0,67=I1,68=I2
0; // always zero for non-c++
.section/dm .gdt;
.align 1;.fet_index:
.var = .function_exceptions_table;
// address of table in .gdt
.retain_name .fet_index;
When using the
-char-size-8
switch on processors that support byte-addressing, the example should be written to use byte-addressed data sections and use symbol names suitable for use with byte-addressed code:
.section/pm seg_pmco; asmfunc.:
.LN.asmfunc.:
modify(i7,-6) (nw); // allocate stack frame
// save area at I6-7
dm(-7,i6)=r5; // save_area[0] = r5
dm(-6,i6)=r6; // save_area[1] = r6
dm(-5,i6)=r7; // save_area[2] = r7
r2=i0; dm(-4,i6)=r2; // save_area[3] = i0
r2=i1; dm(-3,i6)=r2; // save_area[4] = i1
r2=i2; dm(-2,i6)=r2; // save_area[5] = i2
// use R5,R6,R7,I0,I1,I2, call a C++ function
i0=dm(-4,i6);
i1=dm(-3,i6);
i2=dm(-2,i6);
r5=dm(-7,i6);
r6=dm(-6,i6);
r7=dm(-5,i6);
i12=dm(m7,i6);
jump (m14,i12) (db); rframe; nop;
.LN.asmfunc..end:
2–266
CrossCore Embedded Studio 2.2.0
C/C++ Run-Time Model and Environment
.asmfunc..end:
.global asmfunc.;
.type asmfunc., STT_FUNC;
.section .edt; // conventionally function exceptions
// tables go in .edt
.var .function_exceptions_table[8] =
.LN._asmfunc, // first address of _asmfunc
.LN._asmfunc.end, // first address after _asmfunc
-7, // offset of save area from I6
0x00000380, 0, 0x0000001c, 0,
// bit set, bits 7=R5,8=R6,9=R7,66=I0,67=I1,68=I2
0; // always zero for non-c++
.section .gdt;
.align 4;
.fet_index:
.var = .function_exceptions_table;
// address of table in .gdt
.retain_name .fet_index;
Note that since the exceptions table is written for either
-char-size-8
or
-char-size-32
code, the function must be called from code compiled with the same char-size switch.
Data Storage Formats
This section explains how the compiler stores some kinds of data. It covers the following topics:
•
•
•
•
•
Precision Restrictions With 40-Bit Floating-Point Arithmetic
Using Data Storage Formats
The sizes of intrinsic C/C++ data types are selected by Analog Devices so that normal C/C++ programs execute with hardware-native data types, and, therefore, at high speed. The C/C++ run-time environment uses the intrinsic C/C+
+ data types and data formats that appear in the
Data Storage Formats and Data Type Sizes table, the Data Storage
Formats and Data Storage table, the Data Storage Format for Float and Double Types figure, and the Double-Preci-
sion IEEE Format figure.
NOTE: Except for the 64-bit floating-point types on processors with native double-precision floating-point support (see the
slowly than hardware-supported native data types. The 64-bit data types are long double
, long long
,
CrossCore Embedded Studio 2.2.0
2–267
Data Storage Formats
and unsigned long long
. When the
switch is specified, double is also a
64-bit data type.
Table 2-56: Data Storage Formats and Data Storage
Data Storage Format
long long unsigned long long long double
For processors without byte-addressing support, or when using the
-char-size-32
switch, writes 64-bit two's complement data with the most significant word closer to address
0x0
, proceeding toward the top of memory with the rest.
When using the
-char-size-8
switch on processors with byte-addressing support, writes 64bit two's complement data with the least significant byte closer to address
0x0
, proceeding toward the top of memory with the rest.
For processors without byte-addressing support, or when using the
-char-size-32
switch, writes 64-bit magnitude data with the most significant word closer to address
0x0
, proceeding toward the top of memory with the rest.
When using the
-char-size-8
switch on processors with byte-addressing support, writes 64bit magnitude data with the least significant byte closer to address
0x0
, proceeding toward the top of memory with the rest.
For processors without byte-addressing support, or when using the
-char-size-32
switch, writes 64-bit IEEE double-precision data with the most significant word closer to address
0x0
, proceeding toward the top of memory with the rest.
When using the
-char-size-8
switch on processors with byte-addressing support, writes 64bit IEEE double-precision data with the least significant byte closer to address
0x0
, proceeding toward the top of memory with the rest.
See the Processors Supporting Native Double-Precision Floating-Point Arithmetic table in
Processor Features for details.
Floating-Point Data Size
On SHARC processors, the float
data type is 32 bits, and the double
data type default size is 32 bits. This size is chosen because it is the most efficient. The 64-bit long double
data type is available if more precision is needed, although this is more costly because on processors without native double-precision floating-point support, the type exceeds the data sizes supported natively by hardware.
In the C language, floating-point literal constants default to the double
data type. When operations involve both float
and double
, the float
operands are promoted to double
and the operation is done at double
size. By having double
default to a 32-bit data type, the SHARC compiler usually avoids additional expense during these promotions. This does not, however, fully conform to the ISO/IEC 9899:1990 C standard, the ISO/IEC
9899:1999 C standard, and the ISO/IEC 14882:2003 C++ standard, all of which require that the double
type supports at least 10 digits of precision.
The
double
type to 64 bits if additional precision, or full standard conformance, is required.
2–268
CrossCore Embedded Studio 2.2.0
Data Storage Formats
The
-double-size-64
switch causes the compiler to treat the double
data type as a 64-bit data type, instead of a 32-bit data type. This means that all values are promoted to 64 bits, and consequently incur more storage and cycles during computation. The switch does not affect the size of the float
data type, which remains at 32 bits.
Consider the following case.
float add_two(float x) { return x + 2.0; } // has promotion
When compiling this function, the compiler promotes the float
value x
to double
, to match the literal constant
2.0
. The addition becomes a double
operation, and the result is truncated back to a float
before being returned.
switch, the promotion and truncation operations are empty
operations-they require no work because the float
and double
types default to the same size. Thus, there is no cost.
With the
-double-size-64
switch, the promotion and truncation operations require work because the double constant 2.0 is a 64-bit value. The x
value is promoted to 64 bits, a 64-bit addition is performed, and the result is truncated to 32 bits before being returned.
In contrast, since the literal constant
2.0f
in the following example has an f
suffix, it is a float-type constant, not a double-type constant.
float add_two(float x) { return x + 2.0f; } // no promotion
Thus, both operands to the addition are of type float
, and no promotion or truncation is necessary. This version of the function does not produce any performance degradation when the
-double-size-64
switch is used.
You must be consistent in your use of the
-double-size-{32|64}
switch.
Consider the two files, such as: file x.c: double add_nums(double x, double y) { return x + y; } file y.c: extern double add_nums(double, double); double times_two(double val) { return add_nums(val, val); }
Both files must be compiled with the same usage of
-double-size{32|64}
. Otherwise, times_two()
and add_nums()
will be exchanging data in mismatched formats, and incorrect behavior will occur. The
Use of the -
double-size-{32|64} switch table shows the results for the various permutations.
Table 2-57: Use of the -double-size-{32|64} switch
x.c
y.c
default default
-double-size-32 default
-double-size-32 default
Result
Okay
Okay
Okay
CrossCore Embedded Studio 2.2.0
2–269
Data Storage Formats
Table 2-57: Use of the -double-size-{32|64} switch (Continued)
x.c
y.c
-double-size-32
-double-size-64
-double-size-32
-double-size-64
-double-size-32
-double-size-64
-double-size-64
-double-size-32
Result
Okay
Okay
Error
Error
If a file does not make use of any double-typed data, it may be compiled with the
switch, to indicate this fact. Files compiled in this way may be linked with files compiled with
-double-size-32
or with
double-size-64
, without conflict.
Conflicts are detected by the linker and result in linker error li1151
, Input sections have inconsistent qualifiers.
Floating-Point Binary Formats
This section covers:
•
•
IEEE Floating-Point Implementation
IEEE Floating-Point Format
By default, the SHARC compiler provides floating-point arithmetic using IEEE single- and double-precision formats. Single-precision IEEE format (the Data Storage Format for Float and Double Types figure) provides a 32-bit value, with 23 bits for the mantissa, 8 bits for the exponent, and 1 bit for the sign. This format is used for the float
data type, and for the double
data type by default and when the
-double-size-32
switch is used. The
32-bit double
data type violates the ISO/IEC 9899:1990 C standard, the ISO/IEC 9899:1999 C standard, and the
ISO/IEC 14882:2003 C++ standard.
Single Word (32 bits)
31 23 22
0
Mantissa
Sign Bit
8-Bit Exponent
Biased by +127
Figure 2-3: Data Storage Format for Float and Double Types
In the Data Storage Format for Float and Double Types figure, the single word (32-bit) data storage format equates to:
–
1
Sign
x where
2–270
CrossCore Embedded Studio 2.2.0
Floating-Point Binary Formats
• Sign - comes from the sign bit.
• Mantissa - represents the fractional part of the mantissa 23 bits. (The "1." is assumed in this format.)
• Exponent - represents the 8-bit exponent.
Double-precision IEEE format (the Double-Precision IEEE Format figure) provides a 64-bit value, with 52 bits for the mantissa, 11 bits for the exponent, and 1 bit for the sign. This format is used for the long double
data type, and for the double
data type when the
-double-size-64
switch is used. A 64-bit value for the double
data type is compliant to with the ISO/IEC 9899:1990 C standard, the ISO/IEC 9899:1999 C standard, and the
ISO/IEC 14882:2003 C++ standard. See Language Standards Compliance
.
63
62
Most Significant Word (32 bits) at Memory Address N
52
51
31
Least Significant Word (32 bits) at Memory Address N+1
0
Mantissa
Sign Bit
11-Bit Exponent
Biased by +1023
Figure 2-5: Double-Precision IEEE Format
In the
Double-Precision IEEE Format figure, the two-word (64-bit) data storage format equates to:
–
1
Sign
x where
• Sign - comes from the sign bit.
• Mantissa - represents the fractional part of the mantissa 52 bits. (The "1." is assumed in this format.)
• Exponent - represents the 11-bit exponent.
IEEE Floating-Point Implementation
The following points should be noted about IEEE floating-point support for SHARC:
• For 32-bit (single-precision) arithmetic, the majority of operations are implemented using the processor's native hardware.
• For processors without native double-precision floating-point support (see the Double-Precision IEEE Format figure in
), 64-bit (double-precision) arithmetic operations are emulated using a software library.
• All NaNs are handled as quiet NaNs. Signalling NaNs are not supported.
• The sign bit of the NaN is not guaranteed to be preserved by library routines and type conversions.
• In general, denormalized numbers are flushed to zero before being used in arithmetic.
CrossCore Embedded Studio 2.2.0
2–271
Data Storage Formats
fract Data Representation
The fract
types are native fixed-point types that can be used to write code using saturating, fixed-point arithmetic.
The native fixed-point types are discussed in Using Native Fixed-Point Types .
The short fract
, fract
and long fract
type represent a 32-bit signed fractional value. All types have the range [-1.0,+1.0).
The short fract
, fract
, and long fract
data representations are shown in the following figure.
Short fract, fract, long fract (1.31)
Bit
31 30 29
Weight
(-1) 2 -1 2 -2
2
2 -29
1 0
2 -30 2 -31
Figure 2-5: Data Storage Format for short fract, fract, and long fract (1.31)
Therefore, to represent 0.25 in fract
, the HEX representation is
0x20000000
(2
-2
). For -1, the hexadecimal representation in fract
is
0x80000000
. short fract
, fract
, and long fract
cannot represent +1 exactly, but they get quite close with
0x7fffffff
.
The unsigned short fract
, unsigned fract
, and unsigned long fract
types represent a 32-bit unsigned fractional value. All types have the range [0.0,+1.0).
The unsigned short fract
, unsigned fract
, and unsigned long fract
data representations are shown in the following figure.
Unsigned short fract, unsigned fract, unsigned long fract (0.32)
31
30 29
Bit
Weight 2
-1
2
-2
2
-3
2
2
-30
1
2
-31
0
2
-32
Figure 2-6: Data Storage Format for unsigned short fract, unsigned fract, and unsigned long fract (0.32)
Therefore, to represent 0.25 in unsigned fract
, the hexadecimal representation is
0x40000000
(2
-2
).
unsigned short fract
, unsigned fract
, and unsigned long fract
cannot represent +1 exactly, but they get quite close with
0xffffffff
.
Precision Restrictions With 40-Bit Floating-Point Arithmetic
CCES does not provide full support for 40-bit arithmetic for SHARC processors. If you attempt to use 40-bit arithmetic, you may encounter circumstances that result in variable precision. For instance:
• By default, the SHARC memory is configured for 32-bit data, so any values that are saved and restored from memory will lose precision.
• The compiler often copies data in a way that does not preserve all 40 bits of data. For example, it may use a
DAG register as temporary storage if this offers a performance benefit.
2–272
CrossCore Embedded Studio 2.2.0
C/C++ Run-Time Model and Environment
If you enable 40-bit memory accesses, it is recommended that you compile any C code with the
-extraprecision
compiler switch.
The following run-time library routines supplied with CCES use 40-bit arithmetic internally (their inputs and outputs do not): asinf cfft_mag fmodf gen_harris polarf sinf cabsf cosf gen_blackman iir
(the scalar-valued version from the header file filters.h
) rfft_mag sqrtf cartesianf div gen_hamming ldiv rmsf twidfftf cexpf fir
(the scalar-valued version from the header file filters.h
) gen_hanning normf rqsrtf twidfft
If the switch
-double-size-64
has not been specified, then the following functions also use 40-bit arithmetic internally: asin fmod rsqrt cabs norm sin cexp polar sqrt cos rms
The compiler support routines for the following C/C++ operations use 40-bit arithmetic:
• Long double-to-unsigned integer conversion
• Modulus operator
• Integer division
To preserve all 40 bits of registers used in these routines, we recommend using the interrupt dispatchers provided by
Analog Devices; refer to the System Run-Time Documentation in the CCES online help.
Memory Section Usage
The C/C++ run-time environment requires that a specific set of memory section names are used to place code in memory. In assembly language files, these names are used as labels for the
.SECTION
directive. In the
.ldf
file, these names are used as labels for the output section names within the
SECTIONS{}
command. For information on
.ldf
file syntax and other information on the linker, see the Linker and Utilities Manual.
The
Input Section Names Used by the Compiler and Libraries table lists the input section names used by the compiler and run-time libraries. Refer to this table when creating your own mappings in an
.ldf
file. You can change
CrossCore Embedded Studio 2.2.0
2–273
C/C++ Run-Time Model and Environment
Table 2-58: Input Section Names Used by the Compiler and Libraries
Names Usage Description
seg_pmco seg_swco seg_dmda seg_dmda_nw seg_dmda_bw seg_pmda seg_pmda_nw seg_pmda_bw seg_init iv_code seg_int_code seg_int_code_sw
This section must be in Program Memory (PM), holds code, and is required by some functions
in the C/C++ run-time library. For more information, see Code Storage in Program Memory
.
This section contains short-word instructions for targets that support VISA (variable instruction set) execution. It is used by the compiler and by some functions in the short-word variants of the C/C++ run-time libraries. For more information, see
Code Storage in Program Memory
.
This section must be in Data Memory (DM), is the default location for global and static variables and string literals, and is required by some functions in the C/C++ run-time library. For
more information, see Data Storage in Data Memory
. For processors that support byte-addressing, this section can be either 32-bit or 8-bit in width, though can only be used with a single width within each assembly file.
This section is only used on processors that support byte-addressing. It is identical to seg_dmda
, but is only for use with 32-bit width.
This section is only used on processors that support byte-addressing. It is identical to seg_dmda
, but is only for use with 8-bit width.
This section must be in PM, holds PM data variables, and is required by some functions in the
C/C++ run-time library. For more information, see
Data Storage in Program Memory . For pro-
cessors that support byte-addressing, this section can be either 32-bit or 8-bit in width, though can only be used with a single width within each assembly file.
This section is only used on processors that support byte-addressing. It is identical to seg_pmda
, but is only for use with 32-bit width.
This section is only used on processors that support byte-addressing. It is identical to seg_pmda
, but is only for use with 8-bit width.
This section must be in PM, holds system initialization data, and is required for system initiali-
zation. For more information, see Initialization Data Storage .
This section contains the interrupt vector code, and must be mapped to the correct interrupt address range. For more details on interrupt management, refer to the System Run-Time Docu-
mentation.
These input sections are used to ensure that code is mapped to internal memory, and never to external memory, when it is desirable for performance reasons or necessary for any other reason
(such as a silicon anomaly workaround) .
The seg_int_code
section can be used to map normal-word or short-word code on processors that support VISA execution. The seg_int_code_sw
input section can only be used to map short-word code.
The default and generated
.ldf
files map these sections to internal memory configured for code of the required width. The run-time libraries make use of seg_int_code
to avoid silicon anomalies 02000055 and 04000046 in a small number of functions.
The following sections cover:
•
Code Storage in Program Memory
•
2–274
CrossCore Embedded Studio 2.2.0
Memory Section Usage
•
Data Storage in Program Memory
•
•
•
Code Storage in Program Memory
For processors that do not support VISA execution, seg_pmco
is the location where the compiler puts all the instructions that it generates when you compile your program. When linking, use your
.ldf
file to map this section to a program memory (PM) section.
On processors that support VISA execution, the compiler puts all the instructions that it generates into seg_swco by default. When linking, use your
.ldf
file to map this section to an SW-qualified memory output section. When
-nwc
or
-normal-word-code
is used, the compiler puts all instructions into seg_pmco
. When linking, use your
.ldf
file to map this section to a PM-qualified output section.
If you are assembling legacy assembly files and are using VISA execution support in your executable, use your
.ldf
file to map the input sections to an SW-qualified output section.
For processors that support VISA execution, the run-time libraries use both seg_pmco
and seg_swco
section names for VISA code; when mapping input sections to a VISA output section, the linker only maps input sections with the "short word" qualifier.
Data Storage in Data Memory
The data memory data section, seg_dmda
, is where the compiler puts global and static data. When linking, use your
.ldf
file to map this section to DM space.
By default, the compiler places static and global variables in the data memory data section. The compiler's dm
and pm
keywords (memory type qualifiers) let you override this default. If a memory type qualifier is not specified, the
static int data [10];
Data Storage in Program Memory
The program memory data section, seg_pmda
, is where the compiler puts global and static data in program memory. When linking, use your
.ldf
file to map this section to PM space.
By default, the compiler stores static and global variables in the data memory data section. The compiler's pm
keyword (memory type qualifier) lets you override this default and place variables in the program memory data section.
If a memory type qualifier is not specified, the compiler places static and global variables in data memory. For more
information on type qualifiers, see Dual Memory Support Keywords (pm dm)
. The following example allocates an array of 10 integers in the PM data section:
static int pm coeffs[10];
CrossCore Embedded Studio 2.2.0
2–275
Memory Section Usage
Run-Time Stack Storage
Because the run-time environment cannot function without a stack, you must define one in DM space. A typical size for the run-time stack is 8K 32-bit words of data memory.
The run-time stack is a 32-bit wide structure, growing from high memory to low memory. The compiler uses the run-time stack as the storage area for local variables and return addresses.
During a function call, the calling function pushes the return address onto the stack. See
.
Stack space is not provided by an input section. Instead, the
.ldf
file uses the
RESERVE
and
RESERVE_EXPAND directives to reserve a region of space within data memory. Depending on the target processor and the link-time options, the stack may be allocated a region of memory by itself, or a single, contiguous region may be allocated for both the stack and the default heap, with the stack occupying the higher addresses and the heap occupying the lower addresses.
Run-Time Heap Storage
To dynamically allocate and deallocate memory at runtime, the C/C++ run-time library includes several functions: malloc
, calloc
, realloc
and free
. These functions allocate memory from the run-time heap by default. A typical size for the run-time heap is 16K 32-bit words of data memory.
The default heap is not provided by an input section. Instead, the
.ldf
file uses the
RESERVE
and
RESERVE_EXPAND
directives to reserve a region of space within data memory. Depending on the target processor and the link-time options, the default heap may be allocated a region of memory by itself, or a single, contiguous region may be allocated for both the stack and the default heap, with the stack occupying the higher addresses and the heap occupying the lower addresses.
The run-time library also provides support for multiple heaps, which allow dynamically allocated memory to be lo-
NOTE: A default heap is always required by every project, as the run-time library makes some use of it internally.
Initialization Data Storage
The initialization section, seg_init
, is where the compiler puts the initialization data in program memory. When linking, use your Linker Description File to map this section to program memory space.
The initialization section may be processed by two different utility programs: mem21k
or elfloader
.
• When using the elfloader
utility, the seg_init
section needs only 16 slots/locations of space.
• When using the mem21k
utility, all RAM memory initialization will be stored in the seg_init
PM ROM section, so the seg_init section will need to be sized accordingly.
For more information, see
2–276
CrossCore Embedded Studio 2.2.0
C/C++ Run-Time Model and Environment
Global Array Alignment
Global arrays must be aligned on a 64-bit word boundary or greater; the compiler will normally use this knowledge when optimizing accesses. If you declare arrays in assembly files that will be accessed from C/C++, use the
.ALIGN
directive to ensure the array's starting address has an alignment of 2 words or greater.
Controlling System Heap Size and Placement
The system heap is the default heap used by calls to allocation functions like malloc()
in C and the new operator in C++. System heap placement and size are specified in the application’s
.ldf
file.
The following sections cover:
•
Managing the System Heap in the IDE
•
Managing the System Heap in the .LDF File
•
Managing the System Heap in the IDE
The
.ldf
files created by the
Project Wizard, with the Startup Code/LDF option accepted, can be controlled by the
System Configuration Overview dialog box.
1. Expand your new project in a project navigation view, such as Project Explorer.
2. Double-click system.svc
.
The Startup Code/LDF component appears in the System Configuration Overview dialog box.
3. Click the Startup Code/LDF tab at the bottom of the dialog box.
4. Click the LDF tab that appears at the left of the dialog box.
The
LDF Configuration page appears.
5. In the
System heap area, enable Customize the system heap.
6. (Optional) Modify the size and choose memory for the system heap.
7. When you have modified the settings as required, save the changes, via
Ctrl+S, File > Save, or by clicking the floppy disk icon in the toolbar. This causes the IDE to generate an updated LDF and related startup-code files, which configure your heaps during the application’s startup.
Managing the System Heap in the .LDF File
If an
.ldf
file has not been added to the project either by using the Project Wizard or by using a custom file, a default
.ldf
file from the
<install_path>\SHARC\ldf
directory will be used.
CrossCore Embedded Studio 2.2.0
2–277
Controlling System Heap Size and Placement
By default, the compiler uses the file arch.ldf
, where arch is specified via the
-proc arch
switch. For example, if
-proc ADSP-21469
is used, the compiler defaults to using adsp-21469.ldf
. The entry controlling the heap for processors without support for byte-addressing has a format similar to the following (simplified for clarity):
// macro that defines minimum system heap size
#define HEAP_SIZE 16K dm_block3_dm_data_prio0
{
INPUT_SECTION_ALIGN(2)
// allocate minimum of HEAP_SIZE to system heap
RESERVE(sys_heap, sys_heap_length = HEAP_SIZE, 2)
} > mem_block3_dm32
// all other uses of mem_block3_dm32 sys_heap
{
INPUT_SECTION_ALIGN(2)
// if any of mem_block3_dm32 is unused, add to system heap
RESERVE_EXPAND(sys_heap, sys_heap_length, 0)
// define symbols to configure the heap for runtime support
ldf_heap_space = sys_heap;
ldf_heap_end = ldf_heap_space + sys_heap_length;
ldf_heap_length = ldf_heap_end - ldf_heap_space;
} > mem_block3_dm32
In this example, the minimal size of the heap can be modified by changing the definition of the
HEAP_SIZE
macro.
If this value is larger than the memory output section being used, the linker issues error li2040
.
Processors with support for byte-addressing (see the the
Processors Supporting Byte-Addressing table in
output section is filtered by the DM qualifier:
// macro that defines minimum system heap size
#define HEAP_SIZE 16K dm_block3_dm_data_prio0 DM
{
INPUT_SECTION_ALIGN(2)
// allocate minimum of HEAP_SIZE to system heap
RESERVE(sys_heap, sys_heap_length = HEAP_SIZE, 2)
} > mem_block3_bw
// all other uses of mem_block3_bw sys_heap DM
{
INPUT_SECTION_ALIGN(2)
// if any of mem_block3_bw is unused, add to system heap
RESERVE_EXPAND(sys_heap, sys_heap_length, 0)
// define symbols to configure the heap for runtime support
2–278
CrossCore Embedded Studio 2.2.0
Controlling System Heap Size and Placement
ldf_heap_space = sys_heap;
ldf_heap_end = ldf_heap_space + sys_heap_length;
ldf_heap_length = ldf_heap_end - ldf_heap_space;
} > mem_block3_bw
The following macros can be used to configure the sizes of the system heap and stack, when using the default
.ldf
files. When using these macros, all three must be defined, for any of the definitions to take effect. All values are specified in 32-bit words.
•
HEAP_SIZE
– Defines the size of the system heap. A typical value would be “7K”.
•
STACK_SIZE
– Defines the size of the system stack. A typical value would be “8K”.
•
STACKHEAP_SIZE
– Defines the size of the combined area used for system heap and system stack. A typical value would be “15K”. Must be defined to be the sum of
HEAP_SIZE
and
STACK_SIZE
.
The default
.ldf
files support the placement of heaps in
L1
or SDRAM (where available). By default,
L1
is used.
To select alternative heap placement, the following macro can be defined when linking:
•
USE_SDRAM_HEAP
– Causes SDRAM memory to be used for the system heap. It provides large capacity but is slow to access. Enabling data cache for the memory used reduces the performance impact.
Besides the default system heap, you can also define other heaps. See
Using Multiple Heaps for more information.
Standard Heap Interface
The standard functions, calloc
and malloc
, allocate a new object from the default heap. If realloc
is called with a null pointer, it too allocates a new object from the default heap. Previously allocated objects can be deallocated with the free
or realloc
functions. When a previously allocated object is resized with realloc
, the returned object is in the same heap as the original object.
The space_unused
function returns the number of bytes unallocated in the heap with index 0. Note that you may not be able to allocate all of this space due to heap fragmentation and the overhead that each allocated block needs.
Using Multiple Heaps
The C/C++ run-time library supports the standard heap management functions calloc
, free
, malloc
, and realloc
. By default, a single heap, called the default heap, serves all allocation requests that do not explicitly specify an alternative heap. The default heap is defined in the standard linker description file and the run-time header.
Any number of additional heaps can be defined. These heaps serve allocation requests that are explicitly directed to them. These additional heaps can be accessed via the extension routines heap_calloc
, heap_free
, heap_malloc
, and heap_realloc
. For more information, see
Using the Alternate Heap Interface .
Multiple heaps allow the programmer to serve allocations using fast-but-scarce memory or slower-but-plentiful memory as appropriate.
The following sections cover:
•
CrossCore Embedded Studio 2.2.0
2–279
Using Multiple Heaps
•
Defining Additional Heaps in the IDE
•
•
•
Allocating C++ STL Objects to a Non-Default Heap
•
Using the Alternate Heap Interface
•
Defining a Heap
Heaps can be defined in the IDE or at runtime. In both cases, a heap has three attributes:
• Start (base) address (the lowest usable address in the heap)
• Length (in words)
• User identifier ( userid
, a number >= 1)
The default system heap, defined at link-time, always has userid 0
. In addition, heaps have indices. This is like the userid
, except that the index is assigned by the system. All the allocation and deallocation routines use heap indices, not heap user IDs. A userid
can be converted to its index using heap_lookup()
. Be sure to pass the correct identifier to each function.
Defining Additional Heaps in the IDE
The Startup Code/LDF Add-in allows you to configure and extend your heaps through a convenient graphical interface:
• Modify the size of your heaps.
• Change whether they are in internal or external memory (where available).
• Add additional heaps, or remove them.
To add a new heap:
1. Expand your new project in a project navigation view, such as
Project Explorer.
2. Double-click system.svc
.
The Startup Code/LDF component appears in the System Configuration Overview dialog box.
3. Click the
Startup Code/LDF tab at the bottom of the dialog box.
4. Click the LDF tab that appears at the left of the dialog box.
The LDF Configuration page appears.
5. In the Stack and Heaps area, click System heap.
6. Click
Add.
2–280
CrossCore Embedded Studio 2.2.0
Using Multiple Heaps
The
Add User Heap dialog box appears. Fill in the details for your new heap.
7. Click OK.
8. When you have modified the settings as required, save the changes, via Ctrl+S, File > Save, or by clicking the floppy disk icon in the toolbar. This causes the IDE to generate an updated LDF and related startup-code files, which configure your heaps during the application's startup.
The same interface allows you to edit additional heaps or remove them, via the Edit and Remove buttons, respectively.
Defining Heaps at Runtime
Heaps may also be defined and installed at runtime, using the heap_install()
function:
int heap_install(void *base, size_t length, int userid);
This function can take any section of memory and start using it as a heap. It returns the heap index allocated for the
newly installed heap, or a negative value if there was some problem. See Tips for Working With Heaps
.
Reasons why heap_install()
may return an error status include, but are not limited to:
• A heap using the specified userid
already exists
• A new heap appears too small to be usable (length too small)
A heap is automatically initialized during installation. If necessary, a heap can be re-initialized later on. For more
information, see Freeing Space
.
Tips for Working With Heaps
Not all memory in a heap is available to users. A few words are reserved per heap and per allocation (rounded to ensure the allocation is two-word aligned) which are used for housekeeping. Thus, a heap of 256 words is unable to serve four blocks of 64 words.
Memory reserved for housekeeping precedes the allocated blocks. Thus, if a heap begins at
0x0080 0000
, this particular address is never returned to the user program as the result of an allocation request; the first request returns an address some way into the heap.
The base address of a heap must be appropriately aligned for a two-word memory access. This means that allocations can then be used for vector and long-word operations.
For C++ compliance, calls to malloc
and calloc
with a size of 0 will allocate a block of size 1.
Allocating C++ STL Objects to a Non-Default Heap
C++ STL objects can be placed in a non-default heap through use of a custom allocator. To do this, you must first create your custom allocator. Below is an example custom allocator that you can use as a basis for your own. The most important part of customalloc.h
in most cases is the allocate function, where memory is allocated to the
STL object.
CrossCore Embedded Studio 2.2.0
2–281
Using Multiple Heaps
Currently, the pertinent line of code assigns to the default heap (0):
Ty* ty = (Ty*) heap_malloc(0, n * sizeof(Ty));
Simply by changing the first parameter of heap_malloc()
, you can allocate to a different heap:
• 0 is the default heap
• 1 is the first user heap
• 2 is the second user heap
• And so on
Once you have created your custom allocator, you must inform your STL object to use it. Note that the standard definition for "list":
list<int> a; is the same as writing:
list<int, allocator<int> > a; where "allocator" is the default allocator. Therefore, we can tell list "a" to use our custom allocator as follows:
list<int, customallocator<int> > a;
Once created, the list "a" can be used as normal. Also, example.cpp
(below) is a simple example that shows the custom allocator being used.
customalloc.h
template <class Ty> class customallocator { public:
typedef Ty value_type;
typedef Ty* pointer;
typedef Ty& reference;
typedef const Ty* const_pointer;
typedef const Ty& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
template <class Other>
struct rebind { typedef customallocator<Other> other; };
pointer address(reference val) const { return &val; }
const_pointer address(const_reference val)
const { return &val; }
customallocator(){}
customallocator(const customallocator<Ty>&){}
template <class Other>
customallocator(const customallocator<Other>&) {}
template <class Other>
2–282
CrossCore Embedded Studio 2.2.0
Using Multiple Heaps
customallocator<Ty>& operator=(const customallocator&)
{ return (*this); }
pointer allocate(size_type n, const void * = 0) {
Ty* ty = (Ty*) heap_malloc(0, n * sizeof(Ty));
cout << "Allocating 0x" << ty << endl;
return ty;
}
void deallocate(void* p, size_type) {
cout << "Deallocating 0x" << p << endl;
if (p) free(p);
}
void construct(pointer p, const Ty& val)
{ new((void*)p)Ty(val); }
void destroy(pointer p) { p->~Ty(); }
size_type max_size() const { return size_t(-1); } };
example.cpp
#include <iostream>
#include <list> using namespace std;
#include <customalloc.h> // include your custom allocator main(){
cout << "creating list" << endl;
list <int, customallocator<int> > a;
// create list with custom allocator
cout.setf(ios_base::hex,ios_base::basefield);
cout << "pushing some items on the back" << endl;
a.push_back(0xaaaaaaaa); // push items as usual
a.push_back(0xbbbbbbbb);
while(!a.empty()){
cout << "popping:0x" << a.front() << endl;
//read item as usual
a.pop_front(); //pop items as usual
}
cout << "finished." << endl;
}
Using the Alternate Heap Interface
The C run-time library provides the alternate heap interface functions heap_calloc
, heap_free
, heap_malloc
, and heap_realloc
. These routines work in exactly the same way as the corresponding standard functions without the heap_
prefix, except that they take an additional argument that specifies the heap index.
These are the library functions that can be used to initialize the heaps, to allocate memory, and to free memory; the functions are described in the C/C++ Library Manual for SHARC Processors.
CrossCore Embedded Studio 2.2.0
2–283
Using the Alternate Heap Interface
int heap_install(void base, size_t length, int userid); int heap_init(int idx); void *heap_calloc(int idx, size_t nelem, size_t elsize) void *heap_free(int idx, void *) void *heap_malloc(int idx, size_t length) void *heap_realloc(int idx, void *, size_t length) int heap_space_unused(int idx);
The actual entry point names for the alternate heap interface routines have an initial underscore. The stdlib.h
standard header file defines equivalent prototypes without the leading underscores, which are mapped onto the li-
brary entry points using #pragma linkage_name identifier .
Note that for
heap_realloc(idx, NULL, length) the operation is equivalent to
heap_malloc(idx, length)
However, for
heap_realloc(idx, ptr, length) where ptr != NULL
, the supplied idx
parameter is ignored; the reallocation is always done from the heap from which ptr
was allocated.
Similarly,
heap_free(idx, ptr) ignores the supplied index parameter, which is specified only for consistency-the space indicated by ptr
is always returned to the heap from which it was allocated.
The heap_space_unused(int idx)
function returns the number of words unallocated in the heap with index idx
. The function returns
-1
if there is no heap with the requested heap index.
C++ Run-Time Support for the Alternate Heap Interface
The C++ run-time library provides support for allocation and release of memory from an alternative heap via the new
and delete
operators.
Heaps should be initialized with the C run-time functions as described. These heaps can then be used via the new and delete
mechanism by simply passing the heap ID to the new
operator. There is no need to pass the heap ID to the delete
operator as the information is not required when the memory is released.
The routines are used as in the example below.
#include <heapnew> char *alloc_string(int size, int heapID)
{
char *retVal = new(heapID) char[size];
2–284
CrossCore Embedded Studio 2.2.0
Using Multiple Heaps
return retVal;
} void free_string(char *aString)
{
delete aString;
}
Freeing Space
When space is "freed", it is not returned to the "system". Instead, freed blocks are maintained on a free list within the heap in question. The blocks are coalesced where possible.
It is possible to re-initialize a heap, emptying the free list and returning all the space to the heap itself, using the heap_init
function:
int heap_init(int index)
This returns zero for success, and nonzero for failure. Note, however, that this discards all records within the heap, so it may not be used if there are any live allocations on the heap still outstanding.
Startup and Termination
When the processor starts running, it has to transfer control to the application's main()
function, but before doing so it has to ensure that all the expected parts of the C/C++ run-time environment have been set up, including:
• Registers, which must be configured according to the rules in
• Heap and stack, which must be set up according to
Controlling System Heap Size and Placement and
• Global variables must have been initialized to their starting values.
• Constructors of any static global instances must have been run.
• The arguments to main()
, argc
, and argv
, must have been set up.
This is the job of the startup code (or "C Run-Time Header", or "CRT"). The startup code is described in the
System Run-Time Documentation, but some additional information is provided in the following sections:
•
•
•
Memory Initialization
When control flow reaches the start of main()
, global and static variables must have been initialized to their default values. When you build your application, the toolchain arranges for the executable image to contain sections of memory that are either zero- or value-filled, depending on how your data is declared. The image also contains sections that are filled with executable code. Further details are in
.
CrossCore Embedded Studio 2.2.0
2–285
Memory Initialization
During development, when you load your application into your processor using the IDE, the IDE copies the contents of those sections from your executable image into the processor's memory.
Once your application is complete, you have to change your application so that you no longer rely on using the IDE to load it into memory. This can be done by either:
• Creating a bootable image with the loader; see
.
• Creating a non-bootable image with the memory initializer; see
.
Bootable Images
Usually, you will use the elfloader
utility to create a bootable image that can be stored in non-volatile memory, such as a SPI flash, and loaded into memory at power-up by the Boot Code. In this model, the Boot Code arranges for all of your application's code and data sections to be copied into the final volatile memory space before control is transferred to your application.
The elfloader
utility processes your executable file, producing a boot-loadable file which you can use to boot a target hardware system and initialize its memory.
The boot loader, elfloader
, operates on the executable file produced by the linker. When you run elfloader
as part of the compilation process (using the
-no-mem
switch), the linker (by default) creates a
*.dxe
file for processing with elfloader
.
For details on this process, refer to the Loader and Utilities Manual, and to your processor's programming reference manual.
Non-Bootable Images
If producing an executable file that is not going to be boot-loaded into the processor, you may use the mem21k utility to process your executable.
In this model, when the Boot Code transfers control to your application, your application's code and data have not yet all been transferred to their final locations in volatile memory. Instead, the startup code (which is in non-volatile memory) invokes the
__lib_setup_memory
run-time library function, which processes the initialization stream.
This performs the task of transferring your application's code and data to volatile memory.
The mem21k
utility processes your executable file, producing an optimized executable file in which all RAM memory initialization is stored in the seg_init
PM ROM section. This optimization has the advantage of initializing all
RAM to its proper value before the call to main()
and reducing the size of an executable file by combining contiguous, identical initializations into a single block.
The memory initializer, mem21k
, operates on the executable file produced by the linker. When running mem21k
as part of the compilation process, the linker (by default) creates a
*.dxe
file for processing with mem21k
.
The mem21k
utility processes all the
PROGBITS
and
ZERO_INIT
sections except the initialization section
( seg_init
), the run-time header section ( seg_rth
), and the code section ( seg_pmco
). These sections contain the initialization routines and data.
2–286
CrossCore Embedded Studio 2.2.0
Startup and Termination
The C run-time header reads the seg_init
section, generated by mem21k
, to determine which memory locations should be initialized to what values. This process occurs during the
__lib_setup_memory
routine that is called from the run-time header.
For more details, refer to the System Run-Time Documentation and Linker and Utilities Manual.
Global Constructors
This section covers:
•
Constructors and Destructors of Global Class Instances
•
Constructors, Destructors and Memory Placement
Constructors and Destructors of Global Class Instances
Constructors for global class instances are invoked by the C/C++ run-time header during start-up. There are several components that allow this to happen:
• The associated data space for the instance
• The associated constructor (and destructor, if one exists) for the class
• A compiler-generated "start" routine
• A compiler-generated table of such "start" routines
• A compiler-constructed linked-list of destructor routines
• The run-time header itself
The interaction of these components is as follows.
The compiler generates a "start" routine for each module that contains globally-scoped class instances that need constructing or destructing. There is at most one "start" routine per module; it handles all the globally-scoped class instances in the module:
• For each such instance, it invokes the instance's constructor. This may be a direct call, or it may be inlined by the compiler optimizer.
• If the instance requires destruction, the "start" routine registers this fact for later, by including pointers to the instance and its destructor into a linked list.
The start routine is named after the first such instance encountered, though the classes are not guaranteed to be constructed or destructed in any particular order (with the exception that destructors are called in the reverse order of the constructors). Such instances should not have any dependency on construction order; the
unconstructed objects during initialization.
A pointer to the "start" routine is placed into the ctdm
section of the generated object file. When the application is linked, all ctdm
sections are mapped into the same ctdm
output section, forming a table of pointers to the "start"
CrossCore Embedded Studio 2.2.0
2–287
Global Constructors
routines. An additional ctdml
object is appended to the end of the table; this contains a terminating NULL pointer.
When the run-time header is invoked, it calls
_ctor_loop()
, which walks the table of ctdm
sections, calling each pointed-to "start" function until it reaches the NULL pointer from ctdml
. In this manner, the run-time header calls each global class instance's constructor, indirectly through the pointers to "start" functions. On processors that support byte-addressing (see the
Processors Supporting Byte-Addressing table in Processor Features
), constructors for global class instances in source files compiled with
-char-size-8
are called before those from source files compiled with
-char-size-32
.
When the program reaches exit()
, either by calling it directly or by returning from main()
, the exit()
routine follows the normal process of invoking the list of functions registered through the atexit()
interface. One of these is a function that walks the list of destructors, invoking each in turn (in reverse order from the constructors).
This function is registered with atexit()
during the run-time header, before main()
is called.
NOTE: Functions registered with atexit()
may not make reference to global class instances, as the destructor for the instance may be invoked before the reference is used.
Constructors, Destructors and Memory Placement
By default, the compiler places the code for constructors and destructors into the same section as any other function's code. This can be changed either by specifying the section specifically for the constructor or destructor (see
#pragma section/#pragma default_section and
Placement Support Keyword (section) ), or by altering the default des-
tination section for generated code (see
#pragma section/#pragma default_section
and
tion_name[, id=section_name...]
). Note that if a constructor is inlined into the "start" routine by the op-
timizer, such placement will have no effect. For more information, see Inlining and Sections
.
While normal compiler-generated code is placed into the
CODE
area, the "start" routine is placed into the
STI
area.
Both
CODE
and
STI
default to the same section, but may be changed separately using
#pragma default_section
or the
-section
switch (as the "start" function is an internal function generated by the compiler, its placement cannot be affected by
#pragma section
).
The pointer to the "start" routine is placed into the ctdm
section. This is not configurable, as the invocation process relies on all of the "start" routine pointers being in the same section during linking, so that they form a table. It is essential that all relevant ctdm
sections are mapped during linking; if a ctdm
section is omitted, the associated constructor will not be invoked during start-up, and run-time behavior will be incorrect.
If destructors are required, the compiler generates data structures pointing to the class instance and destructor. These structures are placed into the default variable-data section (the
DATA
area).
Support for argv/argc
By default, the facility to specify arguments that are passed to your main()
( argv/argc
) at run-time is enabled.
However, to correctly set up argc
and argv
requires you to do some additional configuration. Modify your application as follows:
2–288
CrossCore Embedded Studio 2.2.0
Compiler C++ Template Support
• Define your command-line arguments in C by defining a variable called
__argv_string
. When linked, your new definition overrides the default zero definition otherwise found in the C run-time library. For example,
extern const char __argv_string[] = "-in x.gif -out y.jpeg";
For processors which support byte-addressing, this definition should be compiled with the same
-charsize-8/-char-size-32
switch as the source file which contains main
.
Compiler C++ Template Support
The compiler provides C++ templates as defined in the ISO/IEC 14882:2003 C++ standard.
Template Instantiation
Templates are instantiated automatically by the prelinker during compilation (see
Compiler Components ). This in-
volves compiling files, determining any required template instantiations, and then recompiling those files making the appropriate instantiations. The process repeats until all required instantiations have been made. Multiple recompilations may be required in the case when a template instantiation is made that requires another template instantiation to be made.
Exported Templates
The compiler supports the export
keyword. An exported template does not need to be present in a translation unit that uses the template. For example, the following is a valid C++ program consisting of two translation units:
// File 1
#include <iostream> static void print(void) { std::cout << "File 1" << std::endl;} export template <class T> T const &maxii(T const &a, T const &b); int main()
{
print();
return maxii(7,8);
}
// File 2
#include <iostream> static void print(void) { std::cout << "File 2" << std::endl;} export template <class T> T const &maxii(T const &a, T const &b)
{
print();
return (a>b) ? a : b;
}
The first file makes use of the maxii()
function exported by the second. Unrelated to this, both files declare their own, private copy of the print()
function.
CrossCore Embedded Studio 2.2.0
2–289
Template Instantiation
The two files are separate translation units; one is not included in the other, so no linking errors arise due to the individual definitions of the print()
functions. If file1.c
obtained file2.c
's definition of maxii()
by including file2.c
into file1.c (whether explicitly or implicitly - see
Implicit Instantiation ), file1.c would also include
file2.c
's definition of the print()
function, leading to a linkage error.
When a file containing a definition of an exported template is compiled, a file with a
.et
suffix is created and some extra information is included in the associated
.ti
file. The
.et
files are used by the compiler to find the translation units that define a given exported template.
Implicit Instantiation
mon practice. It results in having both the specification and definition available at point of instantiation.
NOTE: Implicit instantiation does not conform to the ISO/IEC 14882:2003 C++ standard, and does not work with exported templates. Implicit instantiation is disabled by default. It can be enabled via the
switch.
Implicit instantiation involves placing template specifications in a header (for example,
.h
) file and the definitions in a source (for example,
.cpp
) file. Any file being compiled that includes a header file containing template specifications will instruct the compiler to implicitly include the corresponding
.cpp
file containing the definitions of the compiler.
For example, you may have the header file tp.h
: template <typename A> void func(A var); and source file tp.cpp
template <typename A> void func(A var)
{
...code...
}
Two files file1.cpp
and file2.cpp
that include tp.h
will have file tp.cpp
included implicitly to make the template definitions available to the compilation.
NOTE: Because the whole of the file is included, other definitions in the .cpp file will also be visible, which can lead to problems if the .cpp file contains definitions unrelated to the templates being instantiated.
Exported Templates avoids this problem.
When generating dependencies, the compiler will only parse each implicitly included
.cpp
file once. This parsing avoids excessive compilation times in situations where a header file that implicitly includes a source file is included several times. If the
.cpp
file should be included implicitly more than once, the
switch can be used. For example, the file may contain macro guarded sections of code. This may result in more time required to generate dependencies.
2–290
CrossCore Embedded Studio 2.2.0
Template Instantiation
Generated Template Files
Regardless of whether implicit instantiation is used or not, the compilation process involves compiling one or more source files and generating a
.ti
file corresponding to the source files being compiled. These
.ti
files are then used by the prelinker to determine the templates to be instantiated. The prelinker creates an
.ii
file and recompiles one or more of the files instantiating the required templates.
The prelinker ensures that only one instantiation of a particular template is generated across all objects. For example, the prelinker ensures that if both file1.cpp
and file2.cpp
invoked the template function with an int
, that the resulting instantiation would be generated in just one of the objects.
Identifying Un-Instantiated Templates
If the prelinker is unable to instantiate all the templates required for a particular link, a link error will occur. For example:
[Error li1021] The following symbols referenced in processor 'P0' could not be resolved:
'Complex<T1> Complex<T1>::_conjugate() const [with T1=short]
[_conjugate__16Complex__tm__2_sCFv_18Complex__tm__4_Z1Z]' referenced from '.\Debug\main.doj'
'T1 *Buffer<T1>::_getAddress() const [with T1=Complex<short>]
[_getAddress__33Buffer__tm__19_16Complex__tm__2_sCFv_PZ1Z]' referenced from '.\Debug
\main.doj'
'T1 Complex<T1>::_getReal() const [with T1=short]
[_getReal__16Complex__tm__2_sCFv_Z1Z]' referenced from '.\Debug\main.doj'
Linker finished with 1 error
Careful examination of the linker errors reveals which instantiations have not been made. Below are some examples.
Missing instantiation:
Complex<short> Complex<short>::conjugate()
Linker Text:
'Complex<T1> Complex<T1>::_conjugate() const [with T1=short]
[_conjugate__16Complex__tm__2_sCFv_18Complex__tm__4_Z1Z]'
referenced from '.\Debug\main.doj'
Missing instantiation:
Complex<short> *Buffer<Complex<short>>::getAddress()
Linker Text:
'T1 *Buffer<T1>::_getAddress() const [with T1=Complex<short>]
[_getAddress__33Buffer__tm__19_16Complex__tm__2_sCFv_PZ1Z]'
referenced from '.\Debug\main.doj'
Missing instantiation:
Short Complex<short>::getReal()
Linker Text:
'T1 Complex<T1>::_getReal() const [with T1=short]
[_getReal__16Complex__tm__2_sCFv_Z1Z]' referenced from
'.\Debug\main.doj'
CrossCore Embedded Studio 2.2.0
2–291
File Attributes
There could be many reasons for the prelinker being unable to instantiate these templates, but the most common is that the
.ti
and
.ii
files associated with an object file have been removed. Only source files that can contain instantiated templates will have associated
.ti
and
.ii
files, and without this information, the prelinker may not be able to complete its task. Removing the object file and recompiling will normally fix this problem.
Another possible reason for un-instantiated templates at link time is when implicit inclusion (described above) is disabled but the source code has been written to require it. Explicitly compiling the
.cpp
files that would normally have been implicitly included and adding them to the final link is normally all that is needed to fix this.
Another likely reason for seeing the linker errors above is invoking the linker directly. It is the compiler's responsibility to instantiate C++ templates, and this is done automatically if the final link is performed via the compiler driver.
The linker itself contains no support for instantiating templates.
File Attributes
A file attribute is a name-value pair that is associated with a binary object, whether in an object file (
.doj
) or in a library file (
.dlb
). One attribute name can have multiple values associated with it. Attribute names and values are strings. A valid attribute name consists of one or more characters matching the following pattern:
[a-zA-Z_][a-zA-Z_0-9]*
An attribute value is a non-empty character sequence containing any characters apart from NUL.
Attributes help with the placement of run-time library functions. All of the runtime library objects contain attributes which allow you to place time-critical library objects into internal (fast) memory. Using attribute filters in the
LDF, you can place run-time library objects into internal or external (slow) memory, either individually or in groups.
This section describes:
•
Automatically-Applied Attributes
•
•
•
Automatically-Applied Attributes
By default, the compiler automatically applies a number of attributes when compiling a C/C++ file.
For example, it applies the
Content
,
FuncName
, and
Encoding
attributes. These automatically-applied attributes
switch. The Content Attributes figure shows a content attribute tree.
2–292
CrossCore Embedded Studio 2.2.0
Automatically-Applied Attributes
CodeData
Empty
Code Data
ZeroData InitData
VarData ConstData
Figure 2-7: Content Attributes
Content Attributes
The
Content
attributes can be used to map binary objects according to their kind of content, as show by the
Values
of the Content Attribute table.
Table 2-59: Values of the Content Attribute
Value Description
CodeData
Code
Data
ZeroData
This is the most general value, indicating that the binary object contains a mix of content types.
The binary object does not contain any global data, only executable code. This can be used to map binary objects into program memory, or into read-only memory.
The binary object does not contain any executable code. The binary object may not be mapped into dedicated program memory. The kinds of data used in the binary object vary.
The binary object contains only zero-initialized data. Its contents must be mapped into a memory section with the
ZERO_INIT
qualifier, to ensure correct initialization.
InitData
VarData
ConstData
Empty
The binary object contains only initialized global data. The contents may not be mapped into a memory section that has the
ZERO_INIT
qualifier.
The binary object contains initialized variable data. It must be mapped into read-write memory, and may not be mapped into a memory section with the
ZERO_INIT
qualifier.
The binary object contains only constant data (data declared with the C const qualifier). The
data can be mapped into read-only memory (see also the
effects).
The binary object contains neither functions nor global data.
FuncName Attributes
The
FuncName
attributes are multi-valued attributes whose values are all the assembler linkage names of the defined names in obj
.
CrossCore Embedded Studio 2.2.0
2–293
Automatically-Applied Attributes
Encoding Attributes
The
Encoding
attributes can be used to map binary objects according to the encoding of code they contain, as shown by the Values of the Encoding Attribute table.
Table 2-60: Values of the Encoding Attribute
Value Description
SW
NW
Mixed
The binary object contains only short-word code (processors that support VISA execution only).
The binary object contains only normal-word code.
The binary object contains a mixture of short-word and normal-word code (processors that support VISA execution only).
Default LDF Placement
The default
.ldf
file is written so that the order of preference for putting an object in section seg_dmda
, seg_pmco
or seg_swco
depends on the value of the prefersMem
attribute. Precedence is given in the following order:
1. Highest priority is given to binary objects that have a prefersMem
attribute with a value of internal
.
2. Next priority is given to binary objects that have no prefersMem
attribute, or a prefersMem
attribute with a value that is neither internal
nor external
.
3. Lowest priority is given to binary objects with a prefersMem
attribute with the value external
.
Although the default
.ldf
files only reference the values internal and external, prefersMem
may have other values.
For example, an object using a value such as
L2
will be given second priority, as the value is neither internal nor external. You may modify your
.ldf
file to assign appropriate priority to any value you choose, by mapping objects with higher-priority before objects with lower-priority values.
The prefersMemNum
attribute is similar to the prefersMem
attribute, but is given numerical values instead of textual values. This makes it easier to assign priority when there are many different levels, because you can use relational comparisons in the
.ldf
file instead of just equalities and inequalities. The
Values for prefersMemNum At-
tribute table shows the numerical values used by the run-time library for each corresponding prefersMem
attribute value.
Table 2-61: Values for prefersMemNum Attribute
prefersMem Attribute Value
internal any external
prefersMemNum Attribute Value
30
50
70
2–294
CrossCore Embedded Studio 2.2.0
File Attributes
Sections Versus Attributes
similar, since they can both affect how the application is linked. There are important differences, however. These differences will affect whether you choose to use sections or file attributes to control the placement of code and data.
Granularity
Individual components-global variables and functions-in a binary object can be assigned different sections, then those section assignments can be used to map each component of the binary object differently. In contrast, an attribute applies to the whole binary object. This means you do not have as fine control over individual components using attributes as when using sections.
"Hard" Versus "Soft"
A section qualifier is a hard constraint: when the linker maps the object file into memory, it must obey all the section qualifiers in the object file, according to instructions in the .
LDF
file. If this cannot be done, or if the .
LDF
file does not give sufficient information to map a section from the object file, the linker will report an error.
With attributes, the mapping is soft: the default LDFs use the prefersMem
attribute as a guide to give a better mapping in memory, but if this cannot be done, the linker will not report an error. For example, if there are more objects with prefersMem=internal
than will fit into internal memory, the remaining objects will spill over into external memory. Likewise, if there are less objects with the attribute prefersMem!=external
than are needed to fill internal memory, some objects with the prefersMem=external
attribute may get mapped to internal memory.
Section qualifiers are rules that must be obeyed, while attributes are guidelines, defined by convention, that can be used if convenient and ignored if inconvenient. The
Content
attribute is an example: you can use the
Content attribute to map
Code
and
ConstData
binary objects into read-only memory, if this is a convenient partitioning of your application. However, you need not do so if you choose to map your application differently.
Number of Values
Any given element of an object file is assigned exactly one section qualifier, to determine into which section it should be mapped. In contrast, an object file may have many attributes (or even none), and each attribute may have many different values. Since attributes are optional and act as guidelines, you need only pay attention to the attributes that are relevant to your application.
Using Attributes
You can add attributes to a file in two ways:
• Use
#pragma file_attr("name[=value]"[, "name[=value]" [...]])
Example 2 on the use of attributes.
CrossCore Embedded Studio 2.2.0
2–295
Using Attributes
The run-time libraries have attributes associated with the objects in them. For more information on the attributes in run-time library objects, see Library Attributes in the C/C++ Library Manual for SHARC Processors.
Example 1
This example demonstrates how to use attributes to encourage the placement of library functions in internal memory.
Suppose the file test.c
exists, as shown below:
#define MANY_ITERATIONS 500 void main(void) {
int i;
for (i = 0; i < MANY_ITERATIONS; i++) {
fft_lib_function();
frequently_called_lib_function();
}
rarely_called_lib_function();
}
Also suppose:
• The objects containing frequently_called_lib_function
and rarely_called_lib_function
are both in the standard library, and have the attribute prefersMem=any
.
• There is only enough internal memory to map fft_lib_function
(which has prefersMem=internal
) and one other library function into internal memory.
• The linker chooses to map rarely_called_lib_function
to internal memory.
For optimal performance in this example, frequently_called_lib_function
should be mapped to the internal memory in preference to rarely_called_lib_function
.
The
.ldf
file defines the following macro
$OBJS_LIBS_INTERNAL
to store all the objects that the linker should try to map to internal memory:
$OBJS_LIBS_INTERNAL =
$OBJECTS{prefersMem("internal")},
$LIBRARIES{prefersMem("internal")};
If they do not all fit in internal memory, the remainder get placed in external memory - no linker error will occur.
To add the object that contains frequently_called_lib_function
to this macro, extend the definition to read:
$OBJS_LIBS_INTERNAL =
$OBJECTS{prefersMem("internal")},
$LIBRARIES{prefersMem("internal")},
$LIBRARIES{ libFunc("frequently_called_lib_function") };
This ensures that the binary object that defines frequently_called_lib_function
is among those to which the linker gives highest priority when mapping binary objects to internal memory.
2–296
CrossCore Embedded Studio 2.2.0
Using Attributes
Note that it is not necessary for you to know which binary object defines frequently_called_lib_function
(or even which library). The binary objects in the run-time libraries all define the libFunc
attribute so that you can select the binary objects for particular functions without needing to know exactly where in the libraries a function is defined.
The modified line uses this attribute to select the binary object(s) for frequently_called_lib_function
and append them to the
$OBJS_LIBS_INTERNAL
macro. The .
ldf
file maps objects in
$OBJS_LIBS_INTERNAL
to internal memory in preference to other objects. Therefore, frequently_called_lib_function
gets mapped to L1.
Example 2
Suppose you want the contents of test.c
to get mapped to external memory by preference. You can do this by adding the following pragma to the top of test.c
:
#pragma file_attr("prefersMem=external") or use the
-file-attr
switch on the following command line: cc21k -file-attr prefersMem=external test.c
Both of these methods will mean that the resulting object file will have the attribute prefersMem=external
.
The
.ldf files give objects with this attribute the lowest priority when mapping objects into internal memory, so the object is less likely to consume valuable internal memory space which could be more usefully allocated to another function.
NOTE: File attributes are used as guidelines rather than rules. If space is available in internal memory after higherpriority objects have been mapped, it is permissible for objects with prefersMem=external
to be mapped into internal memory.
Implementation Defined Behavior
Each of the language standards supported by the compiler have implementation defined behavior for a list of areas.
The implementation used by the compilers is detailed in this section.
Enumeration Type Implementation Details
The compiler by default implements the underlying type for enumerations as the first type from the following list that can be used to represent all the values in the specified enumeration: unsigned int, int, unsigned long, long, unsigned long long, long long
. Enumeration constant values can be any integral type including long long
and unsigned long long
.
Enumerations types being implemented as long long
or unsigned long long
types is an Analog Devices extension to ANSI C89 standard (ISO/IEC 9899:1990). Allowing enumerations constants to be integral types other than int
is an Analog Devices extension to the ANSI C89 and ANSI C99 (ISO/IEC 9899:1999) standards. These extensions can be disabled by using the
-enum-is-int
switch.
For more information, see
CrossCore Embedded Studio 2.2.0
2–297
Implementation Defined Behavior
When
-enum-is-int
is used the compiler issues error cc0066 " enumeration value is out of "int" range
" when it encounters enumeration constant values that cannot be held using an int type. Warning cc1661
" enumeration value is greater than int type
" is issued when larger than int type enumeration values are used and not compiling with the
-enum-is-int
switch.
The different underlying types used by the compiler to implement enumerations can give rise to other compiler warnings. For example in the following enumeration the underlying type will be unsigned int
which will result in warning cc0186 " pointless comparison of unsigned integer with zero
".
typedef enum { v1, v2 } e1; void check (e1 v) {
if (v < 0) /* pointless comparison if e1 is unsigned */
printf("out of range");
}
If a negative enumeration constant was added to the definition of e1 or if the example was compiled with the
enum-is-int
switch the underlying type used will be signed int and there would be no warning issued for the comparison.
ISO/IEC 9899:1990 C Standard (C89 Mode)
The contents of this section refer to Annex G of the ISO/IEC 9899:1990 C Standard; subsection numbers such as
5.1.1.3 refer to the relevant section of that Standard, which has some implementation-defined aspect.
G3.1 Translation
5.1.1.3 How a diagnostic is identified
The compiler will emit descriptive diagnostics via the standard error stream at compilation time (e.g. "cc0223: function declared implicitly") or as annotations in generated assembly files.
G3.2 Environment
5.1.2.2.1 The semantics of the arguments to main
By default, argv[0]
is a
NULL
pointer.
The values given to the strings pointed to be the argv
argument can be defined by the user. For more information, see
.
5.1.2.3 What constitutes an interactive device
An interactive device is considered a paired display screen and keyboard.
G3.3 Identifiers
6.1.2 The number of significant initial characters (beyond 31) in an identifier without external linkage
The number of significant initial characters in an identifier without external linkage is 15,000.
6.1.2 The number of significant initial characters (beyond 6) in an identifier with external linkage
2–298
CrossCore Embedded Studio 2.2.0
ISO/IEC 9899:1990 C Standard (C89 Mode)
Identifiers with external linkage are treated in the same way as identifiers without.
6.1.2 Whether case distinctions are significant in an identifier with external linkage
Case distinctions are significant.
G3.4 Characters
5.2.1 The members of the source and execution character sets, except as explicitly specified in this International
Standard
The compiler supports the non-standard characters "$" and "`" (ASCII 39).
5.2.1.2 The shift states used for the encoding of multi-byte characters
No shift states are used for the encoding of multi-byte characters.
5.2.4.2.1 The number of bits in a character in the execution character set
When the
-char-size-8
switch is used on processors with support for byte-addressing (see the
Processors Sup-
porting Byte-Addressing table in Processor Features
), a character is 8 bits in size. Otherwise it is 32 bits in size.
6.1.3.4 The mapping of members of the source character set (in character constants and string literals) to members of the execution character set
Characters in the source file are interpreted as ASCII values, which are also used in the execution environment.
6.1.3.4 The value of an integer character constant that contains a character or escape sequence not represented in the basic execution set or the extended character set for a wide character constant
An unrecognized escape sequence will have the escape character dropped. E.g. '\k' becomes 'k'.
6.1.3.4 The value of an integer character constant that contains more than one character or a wide character constant that contains more than one multi-byte character
An integer character constant may contain 1 character. Using more than 1 character will result in warning cc2226 being issued and all but the last character being discarded.
Where a wide character contains more than one multi-byte character, only the first character is retained and warning cc0026 will be issued. Subsequent characters are discarded
6.1.3.4 The current locale used to convert multi-byte characters into corresponding wide characters (codes) for a wide character constant
Only the "C" locale is supported in Analog Devices toolchain and processors.
6.2.1.1 Whether a "plain" char has the same range of values as signed char or unsigned char
A "plain" char
has the same range and value as a signed char
.
G3.5 Integers
6.1.2.5 The representations and sets of values of the various types of integers
CrossCore Embedded Studio 2.2.0
2–299
ISO/IEC 9899:1990 C Standard (C89 Mode)
The representations are shown in the
Representations of Integer Types for the ADSP-21xxx and ADSP-SCxxx Pro-
cessors (with no support for byte-addressing, or when using the -char-size-32 switch) and Representations of Integer
Types when Using the -char-size-8 Switch on ADSP-21xxx and ADSP-SCxxx Processors (with support for byte-addressing) tables.
Table 2-62: Representations of Integer Types on the ADSP-21xxx and ADSP-SCxxx Processors (with no support for byte-addressing, or when using the -char-size-32 switch)
Type Width Minimum Value Maximum Value
(signed) char unsigned char
(signed) short unsigned short
(signed) int unsigned int
(signed) long unsigned long
(signed) long long unsigned long long
32 bits
32 bits
32 bits
32 bits
32 bits
32 bits
32 bits
32 bits
64 bits
64 bits
-2147483648
0
-2147483648
0
-2147483648
0
-2147483648
0
-9223372036854775808
0
2147483647
4294967295
2147483647
4294967295
2147483647
4294967295
2147483647
4294967295
9223372036854775807
18446744073709551615
Table 2-63: Representations of Integer Types when Using the -char-size-8 Switch on the ADSP-21xxx and ADSP-SCxxx Processors (with support for byte-addressing)
Type Width Minimum Value Maximum Value
(signed) char unsigned char
(signed) short
8 bits
8 bits
16 bits
-128
0
-32768
127
255
32767 unsigned short
(signed) int unsigned int
(signed) long unsigned long
(signed) long long unsigned long long
16 bits
32 bits
32 bits
32 bits
32 bits
64 bits
64 bits
0
-2147483648
0
-2147483648
0
-9223372036854775808
0
65535
2147483647
4294967295
2147483647
4294967295
9223372036854775807
18446744073709551615
6.2.1.2 The result of converting an integer to a shorter signed integer, or the result of converting an unsigned integer to a signed integer of equal length, if the value cannot be represented
When converting an unsigned integer to a signed integer of equal length, the exact value of the unsigned integer will be copied to the signed integer. If the sign bit is set, this will result in a negative number.
2–300
CrossCore Embedded Studio 2.2.0
ISO/IEC 9899:1990 C Standard (C89 Mode)
When converting a signed integer to a smaller signed integer, the lower bits of the signed integer (of the size of the smaller signed integer) are copied to the smaller signed integer. If the top-most copied bit is set, this will result in a negative number.
6.3 The results of bitwise operations on signed integers
The results of the operations are shown in the Bitwise Operations on Signed Integers table.
|
^
>>
&
Table 2-64: Bitwise Operations on Signed Integers
~
<<
Same as unsigned integer
Same as unsigned integer
Will fill upper bits with ones if sign bit was originally set
Same as unsigned integer
Same as unsigned integer
Same as unsigned integer
6.3.5 The sign of the remainder on integer division
The sign of the remainder on integer division will be the same as the sign of the first operand of the remainder operation.
6.3.7 The result of a right shift of a negative-valued signed integral type
Right shifts will retain the sign bit on a signed integer. All other bitwise operations treat signed integers as unsigned.
G3.6 Floating-Point
6.1.2.5 The representations and sets of values of the various types of floating-point numbers
The representations and value ranges are:
• float
• 32 bits (1 sign bit, 8 exponent bits, 32 mantissa bits)
-3.4028234663852886E+38 to 3.4028234663852886E+38
• double
(default setting)
• 32 bits (1 sign bit, 8 exponent bits, 32 mantissa bits)
-3.4028234663852886E+38 to 3.4028234663852886E+38
• double
(when compiling with
-double-size-64
)
• 64 bits (1 sign bit, 11 exponent bits, 52 mantissa bits)
-1.797693134862315708e+308 to 1.797693134862315708e+308
• long double
CrossCore Embedded Studio 2.2.0
2–301
ISO/IEC 9899:1990 C Standard (C89 Mode)
• 64 bits (1 sign bit, 11 exponent bits, 52 mantissa bits)
-1.797693134862315708e+308 to 1.797693134862315708e+308
6.2.1.3 The direction of truncation when an integral number is converted to a floating-point number that cannot exactly represent the original value
Round to nearest, ties to even.
6.2.1.4 The direction of truncation or rounding when a floating-point number is converted to a narrower floatingpoint number
Round to nearest, ties to even.
G3.7 Arrays and Pointers
6.3.3.4, 7.1.1 The type of integer required to hold the maximum size of an array-that is, the type of the sizeof operator, size_t
long unsigned int
.
6.3.4 The result of casting a pointer to an integer or vice-versa
A cast from pointer to integer results in the most-significant bits being discarded if the size of the pointer is larger than the integer. If the pointer is smaller than the integer type being cast to, the integer will be zero extended.
A cast from integer to pointer results in the most-significant bits being discarded if the size of the integer is larger that the pointer. If the integer is smaller than the pointer type being cast to, the pointer will be sign-extended.
6.3.6, 7.1.1 The type of integer required to hold the difference between two pointers to elements of the same array, ptrdiff_t
long int
.
G3.8 Registers
6.5.1 The extent to which objects can actually be placed in registers by use of the register storage-class specifier
The register
storage class specifier is ignored.
G3.9 Structures, Unions, Enumerations and Bit-Fields
6.3.2.3 A member of a union object is accessed using a member of a different type
The data stored in the appropriate location is interpreted as the type of the member accessed.
6.5.2.1 The padding and alignment of members of structures. This should present no problem unless binary data written by one implementation are read by another.
Within a structure, members of the fundamental types are aligned on a multiple of their size. Structures are aligned on the strictest alignment of any of their members, but are always aligned to at least 32 bits.
2–302
CrossCore Embedded Studio 2.2.0
ISO/IEC 9899:1990 C Standard (C89 Mode)
6.5.2.1 Whether a "plain" int bit-field is treated as a signed int bit-field or as an unsigned int bit-field
A "plain" int
bit-field is treated as a signed int
bit-field (including bit-fields of size 1).
6.5.2.1 The order of allocation of bit-fields within a unit
Low to High Order.
6.5.2.1 Whether a bit-field can straddle a storage-unit boundary
A bit-field will be placed in an adjacent storage unit instead of overlapping.
6.5.2.2 The integer type chosen to represent the values of an enumeration type
By default, the compiler defines enumeration types with integral types larger than int
, if int
is insufficient to represent all the values in the enumeration. The compiler can be forced to use only int
switch.
G3.10 Qualifiers
6.5.3 What constitutes an access to an object that has volatile-qualified type
Any reference to a volatile
-qualified object is considered to constitute an access.
G3.11 Declarators
6.5.4 The maximum number of declarators that may modify an arithmetic, structure, or union type
No maximum limit is enforced.
G3.12 Statements
6.6.4.2 The maximum number of case values in a switch statement
There is no hard-coded maximum number of case
values in a switch
statement.
G3.13 Preprocessing Directives
6.8.1 Whether the value of a single-character character constant in a constant expression that controls conditional inclusion matches the value of the same character constant in the execution character set; whether such a character constant may have a negative value
The character set used is the same.
Negative values are allowed.
6.8.2 The method for locating includable source files
Include files, whose names are not absolute path names and that are enclosed in "..." when included, are searched for in the following directories in this order:
1. The directory containing the current input file (the primary source file or the file containing the
#include
)
CrossCore Embedded Studio 2.2.0
2–303
ISO/IEC 9899:1990 C Standard (C89 Mode)
2. Any directories specified with the
-I
switch (
-I directory[{,|;} directory...
) in the order they are listed on the command line
3. Any directories on the standard list:
<install_path>\...\include
Note: If a file is included using the
<...>
form, this file is only searched for by using directories defined in items 2 and 3 above.
6.8.2 The support of quoted names for includable source files
Quoted file names are supported.
6.8.2 The mapping of source file character sequences
The source file character sequence is mapped to its corresponding ASCII character sequence.
6.8.6 The behavior on each recognized #pragma directive
For more information, see
6.8.8 The definitions for __DATE__ and __TIME__ when respectively, the data and time of translation are not available
The macros
__DATE__
and
__TIME__
will be defined as "[date unknown]" and "[time unknown]" respectively.
G3.14 Library Functions
7.1.6 The null pointer constant to which the macro NULL expands
NULL
expands to
((void *)0)
.
7.2 The diagnostic printed by and the termination behavior of the assert function
ASSERT
[{failed assertion expression}] fails at "
{file name}":{line number}
7.3.1 The sets of characters tested for by the isalnum, isalpha, iscntrl, islower, isprint, and isupper functions
The following characters are tested:
• isalnum
- 0-9, a-z or A-Z
• isalpha
- a-z or A-Z
• iscntrl
- 0x00-0x1F or 0x7F
• islower
- a-z
• isprint
- 0x20-0x7E
• isupper
- A-Z
7.5.1 The values returned by the mathematics functions on domain errors
The values are:
2–304
CrossCore Embedded Studio 2.2.0
ISO/IEC 9899:1990 C Standard (C89 Mode)
• acos
: 0
• asin
: 0
• atan2
: 0
• log
:
0
• log10
: 0
• pow
: 0
• sqrt
: 0
• fmod
: 0
7.5.1 Whether the mathematics functions set the integer expression errno to the value of the macro ERANGE on underflow range errors
The state of errno
should not be relied upon unless stated explicitly in the documentation.
7.5.6.4 Whether a domain error occurs or zero is returned when the fmod function has a second argument of zero
Zero is returned.
7.7.1.1 The set of signals for the signal function
The following signals are supported:
•
SIGTERM
•
SIGABRT
•
SIGFPE
•
SIGILL
•
SIGINT
•
SIGSEGV
7.7.1.1 The semantics for each signal recognized by the signal function
After the handler is invoked, the disposition of the signal is not reset to
SIG_DFL
.
7.7.1.1 The default handling and the handling at program startup for each signal recognized by the signal function
By default,
SIGABRT
will cause the program to terminate. All other signals are ignored by default.
7.7.1.1 If the equivalent of signal(sig, SIG_DFL); is not executed prior to the call of a signal handler, the blocking of the signal that is performed
Blocking of signals is not performed prior to the call of the signal handler.
7.7.1.1 Whether the default handling is reset if the SIGILL signal is received by a handler specified to the signal function
CrossCore Embedded Studio 2.2.0
2–305
ISO/IEC 9899:1990 C Standard (C89 Mode)
If the
SIGILL
signal is received, the reset to
SIG_DFL
is not performed.
7.9.2 Whether the last line of a text stream requires a terminating new-line character
The last line should have a terminating new-line character.
7.9.2 Whether space characters that are written out to a text stream immediately before a new-line character appear when read in
The space characters will appear.
7.9.2 The number of null characters that may be appended to data written to a binary stream
Any number of null characters may be appended.
7.9.3 Whether the file position indicator of an append mode stream is initially positioned at the beginning or end of the file
End of the file.
7.9.3 Whether a write on a text stream causes the associated file to be truncated beyond that point
The file will become truncated.
7.9.3 The characteristics of file buffering
stderr
is unbuffered, stdio
is line-buffered, and other streams are fully buffered.
7.9.3 Whether a zero-length file actually exists
A zero-length file does exist.
7.9.3 The rule for composing valid file names
Any basic ASCII character that isn't reserved by the file system is valid.
7.9.3 Whether the same file can be open multiple times
A file can be opened multiple times.
7.9.4.1 The effect of the remove function on an open file
There will be no effect on the file and the function will return
-1
.
7.9.4.2 The effect if a file with the new name exists prior to a call to the rename function
There will be no effect on the files and the function will return
-1
.
7.9.6.1 The output for %p conversion in the fprintf function
The pointer address will be printed as an 8-character hexadecimal value. e.g. 00004010.
7.9.6.2 The input for %p conversion in the fscanf function
2–306
CrossCore Embedded Studio 2.2.0
ISO/IEC 9899:1990 C Standard (C89 Mode)
All valid values that can be interpreted as a hexadecimal value will be read until an invalid value or line break is reached, at which point no further characters are read. if the value is larger than can be stored in an 8-character hexadecimal, then the value will saturate.
7.9.6.2 The interpretation of a - character that is neither the first nor the last character in the scanlist for %[ conversion in the fscanf function
A hyphen does not infer an inclusive range of values. e.g.
%[0-9]
will look for a sequence of '0', '-' and '5' chars.
7.9.9.1, 7.9.9.4 The value to which the macro errno is set by the fgetpos or ftell function on failure
errno
should never be relied upon.
7.9.10.4 The messages generated by the perror function
errno
should never be relied upon, so the error messages returned by this function should not be relied upon.
7.10.3 The behavior of the calloc, malloc, or realloc function if the size requested is zero
This is equivalent to a size request of 1.
7.10.4.1 The behavior of the abort function with regard to open and temporary files
abort
will cause execution to jump to exit
as if the program had run to the end of main
.
7.10.4.3 The status returned by the exit function if the value of the argument is other than zero, EXIT_SUCCESS, or EXIT_FAILURE
The exit
function never returns.
7.10.4.4 The set of environment names and the method for altering the environment list used by the getenv function
The getenv
function always returns
NULL
.
7.10.4.5 The contents and mode of execution of the string by the system function
The system
function always returns 0 and has no effect.
7.11.6.2 The contents of the error message strings returned by the strerror function
"There are no error strings defined!".
7.12.1 The local time zone and Daylight Saving Time
This implementation of time.h
does not support either daylight saving or time zones and hence this function will interpret the argument as Coordinated Universal Time (UTC).
7.12.2.1 The era for the clock function
The era for the clock is the number of clock ticks since the start of program execution.
CrossCore Embedded Studio 2.2.0
2–307
Implementation Defined Behavior
ISO/IEC 9899:1999 C Standard (C99 Mode)
The contents of this section refer to Annex J of the ISO/IEC 9899:1999 C Standard; the subsection numbers refer to parts of that Standard which have implementation-defined aspects.
J3.1 Translation
3.10, 5.1.1.3 How a diagnostic is identified
The compiler will emit descriptive diagnostics via the standard error stream at compilation time (e.g. "cc0223: function declared implicitly") or as annotations in generated assembly files.
5.1.1.2 Whether each non-empty sequence of white-space characters other than new-line is retained or replaced by one space character in translation phase 3
Non-empty sequences of white-space characters are retained in translation phase 3.
J3.2 Environment
5.1.1.2 The mapping between physical source file multi-byte characters and the source character set in translation phase 1
When a multi-byte character is encountered, the compiler will interpret the constituent bytes as ASCII characters irrespective of what was intended by the author.
5.1.2.1 The name and type of the function called at program startup in a freestanding environment
The name of the function called at program startup is: int main(); or, alternatively: int main(int argc, char *argv[]);
5.1.2.1 The effect of program termination in a freestanding environment
On program termination, functions registered by the atexit
function are called in reverse order of registration and then the processor is placed in an
IDLE
state.
5.1.2.2.1 An alternative manner in which the main function may be defined
The default startup code source, which calls ' main
', is provided and can be configured by the user.
Alternatively, startup code can be generated in the project settings within the IDE.
5.1.2.2.1 The values given to the strings pointed to by the argv argument to main
By default, argv[0]
is a
NULL
pointer.
The values given to the strings pointed to by the argv
argument can be defined by the user. For more information, see
.
2–308
CrossCore Embedded Studio 2.2.0
ISO/IEC 9899:1999 C Standard (C99 Mode)
5.1.2.3 What constitutes an interactive device
An interactive device is considered a paired display screen and keyboard.
7.14 The set of signals, their semantics, and their default handling
The following signals are supported:
•
SIGTERM
•
SIGABRT
•
SIGFPE
•
SIGILL
•
SIGINT
•
SIGSEGV
7.14 After the handler is invoked, the disposition of the signal is not reset to SIG_DFL
By default, these signals are ignored.
7.14.1.1 Signal values other than SIGFPE, SIGILL, and SIGSEGV that correspond to a computational exception
There are no other signal values that correspond to a computational exception.
7.14.1.1 Signals for which the equivalent of signal(sig, SIG_IGN); is executed at program startup
•
SIGTERM
•
SIGABRT
•
SIGFPE
•
SIGILL
•
SIGINT
•
SIGSEGV
7.20.4.5 The set of environment names and the method for altering the environment list used by the getenv function
There is no default operating system and getenv
will always return
NULL
.
7.20.4.6 The manner of execution of the string by the system function
The system
function always returns 0.
J3.3 Identifiers
6.4.2 Which additional multi-byte characters may appear in identifiers and their correspondence to universal character names
Multi-byte characters may not be used in identifiers.
CrossCore Embedded Studio 2.2.0
2–309
ISO/IEC 9899:1999 C Standard (C99 Mode)
5.2.4.1, 6.4.2 The number of significant initial characters in an identifier
The maximum number of significant initial characters in an identifier is 15,000.
J3.4 Characters
The number of bits in a byte
When the
-char-size-8
switch is used on processors with support for byte-addressing (see the
Processors Sup-
porting Byte-Addressing table in
), a byte is 8 bits in size. Otherwise it is 32 bits in size.
5.2.1 The values of the members of the execution character set
The values of the execution character set are shown in the The Execution Character Set table (with unprintable characters left blank).
Table 2-65: The Execution Character Set
0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xA 0xB 0xC 0xD 0xE 0xF
0x0
0x1
0x2 (space
)
0x3 0
0x4 @
0x5 P
0x6 `
0x7 p
!
1
A
Q a q r
"
2
B
R b s
#
3
C
S c t
$
4
D
T d
%
5
E
U e u f
&
6
F
V v
`
7
G
W g w
(
8
H
X h x i
I
)
9
Y y j
J
:
*
Z z
;
{
[
+
K k
| l
\
,
<
L
]
}
-
=
M m
.
>
N
^ n
~
/
?
O
_ o
(DEL)
5.2.2 The unique value of the member of the execution character set produced for each of the standard alphabetic escape sequences
These values are shown in the Escape Sequences in the Execution Character Set table.
\r
\t
\f
\n
Table 2-66: Escape Sequences in the Execution Character Set
Escape
\a
\b
Value
0x7
0x8
0xC
0xA
0xD
0x9
2–310
CrossCore Embedded Studio 2.2.0
ISO/IEC 9899:1999 C Standard (C99 Mode)
Table 2-66: Escape Sequences in the Execution Character Set (Continued)
Escape Value
\v 0xB
6.2.5 The value of a char object into which has been stored any character other than a member of the basic execution character set
The resulting value is derived from a cast of the character to signed int
, which will not affect the value of the character.
6.2.5, 6.3.1.1 Which of signed char or unsigned char has the same range, representation and behavior as "plain" char
A "plain" char
has the same range and value as a signed char
.
6.4.4.4, 5.1.1.2 The mapping of members of the source character set (in character constants and string literals) to members of the execution character set
Characters in the source file are interpreted as ASCII values, which are the same values used in the execution environment.
6.4.4.4 The value of an integer character constant containing more than one character or containing a character or escape sequence that does not map to a single-byte execution character
An integer character constant may contain 1 character. Using more than 1 character will result in warning cc2226 being issued and all but the last character being discarded. No escape characters other than those specified in the
C99 standard are supported, and these all map to a single byte in the execution environment.
6.4.4.4 The value of a wide character constant containing more than one multi-byte character, or containing a multi-byte character or escape sequence not represented in the extended execution character set
Where a wide character contains more than one multi-byte character, only the first character is retained and warning cc0026 will be issued. Subsequent characters are discarded. No escape characters other than those specified in the
C99 standard are supported, and these all map to a single byte in the execution environment.
6.4.4.4 The current locale used to convert a wide character constant consisting of a single multi-byte character that maps to a member of the extended execution character set into a corresponding wide character code
Only the "C" locale is supported in Analog Devices toolchain and processors.
6.4.5 The current locale used to convert a wide string literal into corresponding wide character codes
Only the "C" locale is supported in Analog Devices toolchain and processors.
6.4.5 The value of a string literal containing a multi-byte character or escape sequence not represented in the execution character set
There are no escape sequences outside the basic or extended character sets.
CrossCore Embedded Studio 2.2.0
2–311
ISO/IEC 9899:1999 C Standard (C99 Mode)
J3.5 Integers
6.2.5 Any extended integer types that exist in the implementation
None.
6.2.6.2 Whether signed integer types are represented using sign and magnitude, two's complement, or one's complement, and whether the extraordinary value is a trap representation or an ordinary value
Two's Complement:
• The sign bit being 1 and all value bits being zero is considered a normal number.
6.3.1.1 The rank of any extended integer type relative to another extended integer type with the same precision
N/A.
6.3.1.3 The result of, or the signal raised by, converting an integer to a signed integer type when the value cannot be represented in an object of that type
The hexadecimal value is copied and then interpreted as signed. e.g.
MAX_UINT
becomes -1.
6.5 The results of some bitwise operations on signed integers
Right shifts will retain the sign bit on a signed integer. All other bitwise operations treat signed integers as unsigned.
J3.6 Floating-Point
5.2.4.2.2 The accuracy of the floating-point operations and of the library functions in the <math.h> and <complex.h> that return floating-point results.
This is a conforming freestanding implementation of C99. The accuracy of the library functions in these headers are therefore undocumented.
5.2.4.2.2 The rounding behaviors characterized by non-standard values of FLT_ROUNDS
FLT_ROUNDS
is a standard value.
5.2.4.2.2 The evaluation methods characterized by non-standard negative values of FLT_EVAL_METHOD
FLT_EVAL_METHOD
is undefined.
6.3.1.4 The direction of rounding when an integer is converted to a floating-point number that cannot exactly represent the original value
Round to nearest, ties to even.
6.3.1.5 The direction of rounding when a floating-point number is converted to a narrower floating-point number
Round to nearest, ties to even.
6.4.4.2 How the nearest representable value or the larger or smaller representable value immediately adjacent to the nearest representable value is chosen for certain floating constants
2–312
CrossCore Embedded Studio 2.2.0
Implementation Defined Behavior
FLT_RADIX
is defined as 2 in
<float.h>
, so floating-point constants are represented using standards-conforming rounding.
6.5 Whether and how floating expressions are contracted when not disallowed by the FP_CONTRACT pragma
This is a conforming freestanding implementation of C99. The
FP_CONTRACT
pragma is therefore not supported.
7.6.1 The default the state for the FENV_ACCESS pragma
This is a conforming freestanding implementation of C99, and the
FENV_ACCESS
pragma is only used for accessing the floating-point environment fenv.h
- a header not required for such an implementation. As such The pragma is not supported.
7.6, 7.12 Additional floating-point exceptions, rounding modes, environments and classification, and their macro names
There are no additional floating-point exceptions, rounding modes, environments or classifications.
7.12.2 The default state for the FP_CONTRACT pragma
This is a conforming freestanding implementation of C99. The
FP_CONTRACT
pragma is therefore not supported.
F.9 Whether the "inexact" floating-point exception can be raised when the rounded result actually does equal the mathematical result in an IEC 60559 conformant implementation
The "inexact" floating-point exception is not supported for SHARC processors.
F.9 Whether the "underflow" (and "inexact") floating-point exception can be raised when a result is tiny but not inexact in an IEC 60559 conformant implementation
The "inexact" floating-point exception is not supported on SHARC processors.
The "underflow" floating-point exception is not enabled by default on SHARC processors.
ISO/IEC 14822:2003 C++ Standard (C++ Mode)
The subsection of this section refer to parts of the ISO/IEC 14822:2003 C++ Standard which have implementationdefined aspects.
1.7 The C++ Memory Model
The fundamental storage unit in the C + + memory model is the byte. A byte is at least large enough to contain any member of the basic execution character set and is composed of a contiguous sequence of bits, the number of which is implementation-defined.
When the
-char-size-8
switch is used on processors with support for byte-addressing (see the
Processors Sup-
porting Byte-Addressing table in
), a byte is 8 bits in size. Otherwise it is 32 bits in size.
1.9 Program Execution
What constitutes an interactive device is implementation-defined.
CrossCore Embedded Studio 2.2.0
2–313
ISO/IEC 14822:2003 C++ Standard (C++ Mode)
An interactive device is considered a paired display screen and keyboard.
2.1 Phases of Translation
Physical source file characters are mapped, in an implementation-defined manner, to the basic source character set
(introducing new-line characters for end-of-line indicators) if necessary.
Characters in the source file are interpreted as ASCII values, which are also used in the execution environment.
Whether each non-empty sequence of white-space characters other than new-line is retained or replaced by one space
character is implementation-defined.
Non-empty sequences of white-space characters are retained.
It is implementation-defined whether the source of the translation units containing these definitions is required to be
available.
The source of the translation units containing these definitions must be available.
2.2 Character Sets
The values of the members of the execution character sets are implementation-defined, and any additional members are locale-specific.
The values of the execution character set are shown in the Execution Character Set for C++ Mode table (with unprintable characters left blank).
Table 2-67: Execution Character Set for C++ Mode
0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xA 0xB 0xC 0xD 0xE 0xF
0x0
0x1
0x2 (space
)
0x3 0
0x4 @
0x5 P
0x6 `
0x7 p
!
1 a q
A
Q
"
2 r b
B
R
#
3 s c
C
S
$
4 t d
D
T
%
5 e u
E
U
&
6 f v
F
V
`
7 g w
G
W
(
8 h x
H
X
)
9 i y
I
Y
:
* j z
J
Z
;
+
{ k
[
K
| l
,
<
\
L
-
=
} m
]
M
.
> n
~
N
^
/
?
O
_ o
(DEL)
2.13.2 Character Literals
A multi-character literal has type int and implementation-defined value.
An integer character constant may contain 1 character. If more than 1 character is used, error cc0026 is issued.
The value of a wide-character literal containing multiple c-chars is implementation-defined.
2–314
CrossCore Embedded Studio 2.2.0
ISO/IEC 14822:2003 C++ Standard (C++ Mode)
Where a wide character contains more than one multi-byte character, only the first character is retained and warning cc0026 will be issued. Subsequent characters are discarded.
The value of a character literal is implementation-defined if it falls outside of the implementation-defined range de-
fined for char (for ordinary literals) or wchar_t (for wide literals).
The full 32-bit value is retained.
2.13.4 String Literals
Whether all string literals are distinct (that is, are stored in non-overlapping objects) is implementation-defined.
Identical string literals within the same object file will not be distinct. That is, only one copy of the string will exist.
3.6.1 Main Function
An implementation shall not predefine the main function. This function shall not be overloaded. It shall have a return type of type int, but otherwise its type is implementation-defined.
The name of the function called at program startup is: int main(); or, alternatively: int main(int argc, char *argv[]);
The linkage (3.5) of main is implementation-defined.
main
has external "C" linkage.
3.6.2 Initialization of Non-Local Objects
It is implementation-defined whether or not the dynamic initialization (8.5, 9.4, 12.1, 12.6.1) of an object of namespace scope is done before the first statement of main.
Dynamic initialization of an object of namespace scope is done before the first statement of main
.
3.9 Types
For POD types, the value representation is a set of bits in the object representation that determines a value, which is one discrete element of an implementation-defined set of values.
All POD types are represented in the same format as in C.
The alignment of a complete object type is an implementation-defined integer value representing a number of bytes; an object is allocated at an address that meets the alignment requirements of its object type.
Compound types (structs, classes) are aligned on the boundary that matches the alignment of the most strictlyaligned member of the type.
CrossCore Embedded Studio 2.2.0
2–315
ISO/IEC 14822:2003 C++ Standard (C++ Mode)
Top-level global arrays are always aligned on a double-word boundary, regardless of the underlying type. Arrays within structures are not aligned beyond the required alignment for their type.
3.9.1 Fundamental Types
It is implementation-defined whether a char object can hold negative values.
A char
can hold negative values.
The value representation of floating-point types is implementation-defined.
The representations of the floating point types are as follows:
• float
• 32 bits (1 sign bit, 8 exponent bits, 32 mantissa bits)
-3.4028234663852886E+38 to 3.4028234663852886E+38
• double
(default setting)
• 32 bits (1 sign bit, 8 exponent bits, 32 mantissa bits)
-3.4028234663852886E+38 to 3.4028234663852886E+38
• double
(when compiling with
-double-size-64
)
• 64 bits (1 sign bit, 11 exponent bits, 52 mantissa bits)
-1.797693134862315708e+308 to 1.797693134862315708e+308
• long double
• 64 bits (1 sign bit, 11 exponent bits, 52 mantissa bits)
-1.797693134862315708e+308 to 1.797693134862315708e+308
3.9.2 Compound Types
The value representation of pointer types is implementation-defined.
Pointer types are represented as 32-bit unsigned integers.
4.7 Integral Conversions
If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field
width); otherwise, the value is implementation-defined.
When converting a signed integer to a smaller signed integer, the lower bits of the signed integer (of the size of the smaller signed integer) are copied to the smaller signed integer. If the topmost copied bit is set, this will result in a negative number.
2–316
CrossCore Embedded Studio 2.2.0
ISO/IEC 14822:2003 C++ Standard (C++ Mode)
4.8 Floating-Point Conversions
If the source value is between two adjacent destination values, the result of the conversion is an implementationdefined choice of either of those values.
Round to nearest, ties to even.
4.9 Floating-Integral Conversions
An rvalue of an integer type or of an enumeration type can be converted to an rvalue of a floating-point type. The result is exact if possible. Otherwise, it is an implementation-defined choice of either the next lower or higher representable value.
Round to nearest, ties to even.
5.2.8 Type Identification
The result of a typeid expression is an lvalue of static type const std::type_info (18.5.1) and dynamic type const std::type_info or const name where name is an implementation-defined class derived from std::type_info which preserves the behavior described in 18.5.1.
The result of a typeid
expression is an lvalue of static type const std::type_info
and dynamic type const std::type_info
.
5.2.10 Reinterpret Cast
The mapping performed by reinterpret_cast is implementation-defined.
For an expression " reinterpret_cast<T>(v)
", the bits in the object representation of " v
" will be treated as type as an object of type "
T
".
A pointer can be explicitly converted to any integral type large enough to hold it. The mapping function is implementation-defined.
The bit pattern of the pointer is interpreted as the integral type. No sign extension is performed if the integral type is larger than the pointer.
A value of integral type or enumeration type can be explicitly converted to a pointer. A pointer converted to an integer of sufficient size (if any such exists on the implementation) and back to the same pointer type will have its original value; mappings between pointers and integers are otherwise implementation-defined.
A cast from pointer to integer results in the most-significant bits being discarded if the size of the pointer is larger than the integer. If the pointer is smaller than the integer type being cast to, the integer will be zero-extended.
A cast from integer to pointer results in the most-significant bits being discarded if the size of the integer is larger than the pointer. If the integer is smaller than the pointer type being cast to, the pointer will be sign-extended.
CrossCore Embedded Studio 2.2.0
2–317
ISO/IEC 14822:2003 C++ Standard (C++ Mode)
5.3.3 Sizeof
sizeof(char), sizeof(signed char) and sizeof(unsigned char) are 1; the result of sizeof applied to any other fundamental type (3.9.1) is implementation-defined. [Note: in particular, sizeof(bool) and sizeof(wchar_t) are implementationdefined.]
Sizes are as shown in the Sizes of C++ Standard Types table.
Table 2-68: Sizes of C++ Standard Types
Type
char
(signed, unsigned) short
(signed, unsigned) int
(signed, unsigned) long
(signed, unsigned) long long
(signed, unsigned) float double
(default) double
(
-double-size-64
) long double bool wchar_t
2
1
1
1
1
2
2
1
1
Sizeof with -char-size-32
1
1
8
4
4
4
4
8
8
1
4
Sizeof with -char-size-8
1
2
5.6 Multiplicative Operators
The binary / operator yields the quotient, and the binary % operator yields the remainder from the division of the first expression by the second. If the second operand of / or % is zero the behavior is undefined; otherwise (a/b)*b + a
%b is equal to a. If both operands are nonnegative then the remainder is nonnegative; if not, the sign of the remainder is implementation-defined.
If the first operand is negative, the sign of the remainder will be negative, otherwise the sign of the remainder is nonnegative.
5.7 Additive Operators
When two pointers to elements of the same array object are subtracted, the result is the difference of the subscripts of the two array elements. The type of the result is an implementation-defined signed integral type; this type shall be the same type that is defined as ptrdiff_t in the <cstddef> header (18.1).
The type of the result is int
.
2–318
CrossCore Embedded Studio 2.2.0
ISO/IEC 14822:2003 C++ Standard (C++ Mode)
5.8 Shift Operators
The value of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 divided by the quantity 2 raised to the power E2. If E1 has a signed type and a negative value, the resulting value is implementation-defined.
Right shifts will retain the sign bit on a signed integer.
7.1.5.2 Simply Type Specifiers
It is implementation-defined whether bit-fields and objects of char type are represented as signed or unsigned quantities.
By default, bit-fields and objects of char
type are represented as signed quantities.
Bit-fields can represented as unsigned quantities by using the compiler switch
.
7.2 Enumeration Declarations
It is implementation-defined which integral type is used as the underlying type for an enumeration except that the underlying type shall not be larger than int unless the value of an enumerator cannot fit in an int or unsigned int.
The underlying type for an enumeration shall be int
.
7.4 The asm Declaration
The meaning of an asm declaration is implementation-defined.
For more information, see
Inline Assembly Language Support Keyword (asm) .
7.5 Linkage Specifications
The string-literal indicates the required language linkage. The meaning of the string-literal is implementation-defined.
Three string-literals are supported:
•
"C"
- the function name in the source file is prefixed with an underscore ("_") in the object file.
•
"C++"
- the function name is mangled according to the compiler's name mangling rules.
•
"asm"
- the function name in the source file is used in the object file without a prefix or name-mangling.
Linkage from C++ to objects defined in other languages and to objects defined in C++ from other languages is implementation-defined and language-dependent.
Three string-literals are supported:
•
"C"
- the function name in the source file is prefixed with an underscore ("_") in the object file.
•
"C++"
- the function name is mangled according to the compiler's name mangling rules.
CrossCore Embedded Studio 2.2.0
2–319
ISO/IEC 14822:2003 C++ Standard (C++ Mode)
•
"asm"
- the function name in the source file is used in the object file without a prefix or name-mangling.
9.6 Bit-Fields
Allocation of bit-fields within a class object is implementation-defined. Alignment of bit-fields is implementationdefined.
Bit-fields are stored using a big-endian representation on processors without support for byte-addressing, or when the
-char-size-32
switch is used. Bit-fields are stored using a little-endian representation when the
-charsize-8
switch is used on processors with support for byte-addressing.
Bit-fields are aligned such that they do not cross a 32-bit word boundary (for bit-fields of type char
, short
, int or long
) or a 64-bit boundary (for bit-fields of type long long
). For example, a 24-bit bit-field can be placed immediately after an 8-bit bit-field, but a 25-bit bit-field member will be aligned on the next 32-bit boundary.
It is implementation-defined whether a plain (neither explicitly signed nor unsigned) char, short, int or long bit-field is signed or unsigned.
Plain bit-fields are signed.
14 Templates
A template name has linkage (3.5). A non-member function template can have internal linkage; any other template name shall have external linkage. Entities generated from a template with internal linkage are distinct from all entities generated in other translation units. A template, a template explicit specialization (14.7.3), or a class template partial specialization shall not have C linkage. If the linkage of one of these is something other than C or C++, the behavior is implementation-defined.
Only C++ linkage is supported for templates.
14.7.1 Implicit Instantiation
There is an implementation-defined quantity that specifies the limit on the total depth of recursive instantiations, which could involve more than one template.
The limit on the total depth of recursive instantiations is 64.
15.5.1 The terminate() Function
In the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before terminate() is called.
The stack is not unwound before the call to terminate()
.
15.5.2 The unexpected() Function
If the exception-specification does not include the class std::bad_exception (18.6.2.1) then the function terminate() is called, otherwise the thrown exception is replaced by an implementation-defined object of the type std::bad_exception
2–320
CrossCore Embedded Studio 2.2.0
ISO/IEC 14822:2003 C++ Standard (C++ Mode)
and the search for another handler will continue at the call of the function whose exception-specification was violated.
The object of the type std::bad_exception
will contain the string "bad exception".
16.1 Conditional Inclusion
Whether the numeric value for these character literals matches the value obtained when an identical character literal occurs in an expression (other than within a #if or #elif directive) is implementation-defined.
The numeric value for these character literals matches the value obtained when an identical character literal occurs in an expression.
Also, whether a single-character character literal may have a negative value is implementation-defined.
A single-character may have a negative value.
16.2 Source File Inclusion
Searches a sequence of implementation-defined places for a header identified uniquely by the specified sequence between the < and > delimiters, and causes the replacement of that directive by the entire contents of the header. How the places are specified or the header identified is implementation-defined.
Include files, whose names are not absolute path names and that are enclosed in "..." when included, are searched for in the following directories in this order:
• The directory containing the current input file (the primary source file or the file containing the
#include
).
• Any directories specified with the
-I
switch (
-I directory[{,|;} directory...
) in the order they are listed on the command line.
• Any directories on the standard list:
<install_path>\...\include
.
The mapping between the delimited sequence and the external source file name is implementation-defined.
The source file character sequence is mapped to its corresponding ASCII character sequence.
A #include preprocessing directive may appear in a source file that has been read because of a #include directive in another file, up to an implementation-defined nesting limit.
The compiler does not define a nesting limit for
#include
directives.
16.6 Pragma Directive
A preprocessing directive #pragma causes the implementation to behave in an implementation-defined manner.
For more information, see
16.8 Predefined Macro Names
If the date of translation is not available, an implementation-defined valid date is supplied.
CrossCore Embedded Studio 2.2.0
2–321
ISO/IEC 14822:2003 C++ Standard (C++ Mode)
The macro
__DATE__
will be defined as "[date unknown]".
If the time of translation is not available, an implementation-defined valid time is supplied.
The macros
__TIME__
will be defined as "[time unknown]".
Whether __STDC__ is predefined and if so, what its value is, are implementation-defined.
__STDC__
is predefined with the value 1.
17.4.4.5 Reentrancy
Which of the functions in the C++ Standard Library are not reentrant subroutines is implementation-defined.
The following functions are not reentrant in the C++ Standard library, as implemented in CCES:
• Functions that use streams.
• Dynamic memory allocation functions ( new
, delete
, etc.).
• The exceptions handling support routines.
Although these functions are not reentrant, thread-safe versions of them are implemented in the multi-threaded C++ library. For more information, see Using the Libraries in a Multi-Threaded Environment in the C/C++ Library
Manual for SHARC Processors.
17.4.4.8 Restrictions on Exception Handling
Any other functions defined in the C++ Standard Library that do not have an exception-specification may throw implementation-defined exceptions unless otherwise specified.
The Functions Which Throw Exceptions table shows which functions may throw the following exceptions, if the application is built with exceptions enabled.
Table 2-69: Functions Which Throw Exceptions
Function
ios_base::clear locale::locale
_Locinfo::_Addcats
_String_base::_Xlen
_String_base::_Xran array new and delete operators
Exception Type
failure runtime_error runtime_error length_error out_of_range bad_alloc
2–322
CrossCore Embedded Studio 2.2.0
ISO/IEC 14822:2003 C++ Standard (C++ Mode)
18.3 Start and Termination
Exit() - Finally, control is returned to the host environment. If status is zero or EXIT_SUCCESS, an implementation-defined form of the status successful termination is returned. If status is EXIT_FAILURE, an implementationdefined form of the status unsuccessful termination is returned. Otherwise the status returned is implementationdefined.
The program will idle at the label
__lib_prog_term
. R0 (the return register) will contain the status.
18.4.2.1 Class bad_alloc
The result of calling what() on the newly constructed object is implementation-defined.
what()
will return the string "bad allocation".
virtual const char* what() const throw(); Returns: An implementation-defined NTBS.
what()
will return the string "bad allocation".
18.5.1 Class type_info
const char* name() const; Returns: an implementation-defined NTBS.
The
Strings returned by name() table shows the string returned by the name() function for the basic types.
Table 2-70: Strings returned by name()
Type
bool char signed char unsigned char
(signed) short unsigned short
(signed) int unsigned int
(signed) long unsigned long
(signed) long long unsigned long long float double long double
String
x y l m f d e b t i j h s c a
CrossCore Embedded Studio 2.2.0
2–323
ISO/IEC 14822:2003 C++ Standard (C++ Mode)
Table 2-70: Strings returned by name() (Continued)
Type
wchar_t
String
w
18.5.2 Class bad_cast
virtual const char* what() const throw(); Returns: An implementation-defined NTBS.
Calling what()
will return the string "bad cast".
18.5.3 Class bad_typeid
bad_typeid() throw(); Notes: The result of calling what() on the newly constructed object is implementation-defined.
Calling what()
will return the string "bad typeid".
virtual const char* what() const throw(); Returns: An implementation-defined NTBS.
Calling what()
will return the string "bad typeid".
18.6.1 Class Exception
exception& operator=(const exception&) throw(); Notes: The effects of calling what() after assignment are implementation-defined.
Calling what()
will return the string "unknown".
virtual const char* what() const throw(); Returns: An implementation-defined NTBS.
Calling what()
will return the string "unknown".
18.6.2.1 Class bad_exception
bad_exception() throw(); Notes: The result of calling what()on the newly constructed object is implementation-defined.
Calling what()
will return the string "bad exception".
virtual const char* what() const throw(); Returns: An implementation-defined NTBS.
Calling what()
will return the string "bad exception".
21 Strings Library
The type streampos is an implementation-defined type that satisfies the requirements for POS_T in 21.1.2.
streampos
is a typedef
of the fpos
class.
The type streamoff is an implementation-defined type that satisfies the requirements for OFF_T in 21.1.2.
streamoff
is a typedef
of the long
type.
2–324
CrossCore Embedded Studio 2.2.0
ISO/IEC 14822:2003 C++ Standard (C++ Mode)
The type mbstate_t is defined in <cwchar> and can represent any of the conversion states possible to occur in an implementation-defined set of supported multi-byte character encoding rules.
Multi-byte characters are not supported in Analog Devices Compiler, so no multi-byte characters may be used in identifiers.
21.1.3.2 struct char_traits<wchar_t>
The type wstreampos is an implementation-defined type that satisfies the requirements for POS_T in 21.1.2.
The type wstreampos
not supported in Analog Devices toolset.
The type mbstate_t is defined in <cwchar> and can represent any of the conversion states possible to occur in an implementation-defined set of supported multi-byte character encoding rules.
Multi-byte characters are not supported in Analog Devices Compiler, so no multi-byte characters may be used in identifiers.
22.1.1.3 Locale Members
basic_string<char> name() const; Returns: The name of *this, if it has one; otherwise, the string "*". If *this has a name, then locale(name().c_str()) is equivalent to *this. Details of the contents of the resulting string are otherwise implementation-defined.
name
returns the name of
*this
, if it has one; otherwise, the string "*".
22.2.1.3 ctype Specializations
The implementation-defined value of member table_size is at least 256.
The value of member table_size
is 256.
22.2.1.3.2 ctype<char> Members
In the following member descriptions, for unsigned char values v where (v >= table_size), table()[v] is assumed to have an implementation-defined value (possibly different for each such value v) without performing the array lookup.
As table_size
has the value 256, it is not possible for v
to be greater than or equal to table_size
.
22.2.5.1.2 time_get Virtual Functions
iter_type do_get_year(iter_type s, iter_type end, ios_base& str, ios_base::iostate& err, tm* t) const; Effects: Reads characters starting at s until it has extracted an unambiguous year identifier. It is implementation-defined whether two-digit year numbers are accepted, and (if so) what century they are assumed to lie in. Sets the t->tm_yearmember accordingly.
If the two-digit year is less than '69', it is assumed that the year is in the 21st century (i.e. 2000 -> 2068); otherwise, it is assumed that the year is in the 20th century.
CrossCore Embedded Studio 2.2.0
2–325
ISO/IEC 14822:2003 C++ Standard (C++ Mode)
22.2.5.3.2 time_put Virtual Functions
Effects: Formats the contents of the parameter t into characters placed on the output sequence s. Formatting is controlled by the parameters format and modifier, interpreted identically as the format specifiers in the string argument to the standard library function strftime(). except that the sequence of characters produced for those specifiers that are described as depending on the C locale are instead implementation-defined.
The Outputs for time_put Specifiers table shows the character sequences produced for each specifier that depends on the C locale.
Table 2-71: Outputs for time_put Specifiers
Specifier Characters
%a
%A
%b
%B
%c
%p
%x
%X
"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
Date and time in the format:
DDD MMM DD HH:MM:SS YYYY
For example, "Sat Jan 31 23:59:59 2011".
"AM", "PM"
Date in the format:
MM/DD/YY
For example, "12/31/12".
Time in the format:
HH:MM:SS
For example, "23:59:59".
22.2.7.1.2 Messages Virtual Functions
catalog do_open(const basic_string<char>& name, const locale& loc) const; Returns: A value that may be passed to get()to retrieve a message, from the message catalog identified by the string name according to an implementationdefined mapping. The result can be used until it is passed to close().
This function has no effect.
string_type do_get(catalog cat, int set, int msgid, const string_type& dfault) const; Returns: A message identified by arguments set, msgid, and dfault, according to an implementation-defined mapping.
The function do_get
always returns the string pointed to by dfault
.
void do_close(catalog cat) const; Notes: The limit on such resources, if any, is implementation-defined.
2–326
CrossCore Embedded Studio 2.2.0
ISO/IEC 14822:2003 C++ Standard (C++ Mode)
This function has no effect.
26.2.8 Complex Transcendentals
The value returned for pow(0,0) is implementation-defined.
This is a conforming freestanding implementation of C++. Complex transcendentals are not supported.
27.1.2 Positioning Type Limitations
The classes of clause 27 with template arguments charT and traits behave as described if traits::pos_type and traits::off_type are streampos and streamoff respectively. Except as noted explicitly below, their behavior when traits::pos_type and traits::off_type are other types is implementation-defined.
traits::pos_type
and traits::off_type
are streampos
and streamoff
, respectively.
27.4.1 Types
The type streamoff is an implementation-defined type that satisfies the requirements of 27.4.3.2.
streamoff
is of type long
.
27.4.2.4 ios_base Static Members
bool sync_with_stdio(bool sync = true); Effects: If any input or output operation has occurred using the standard streams prior to the call, the effect is implementation-defined.
iostream
objects are always synchronised with the standard streams. This function has no effect.
27.4.4.3 basic_ios iostate Flags Functions
If (rdstate() & exceptions()) == 0, returns. Otherwise, the function throws an object fail of class basic_ios::failure
(27.4.2.1.1), constructed with implementation-defined argument values.
If ' ios_base::badbit
' is set, the exception is created with the string
"ios_base::badbit set"
.
If ' ios_base::failbit
' is set, the exception is created with the string
"ios_base::failbit set"
.
27.7.1.3 Overridden Virtual Functions
basic_streambuf<charT,traits>* setbuf(charT* s, streamsize n); Effects: implementation-defined, except that setbuf(0,0)has no effect.
streambuf()
has no effect.
27.8.1.4 Overridden Virtual Functions
basic_streambuf* setbuf(char_type* s, streamsize n); Effects: If setbuf(0,0) is called on a stream before any I/O has occurred on that stream, the stream becomes unbuffered. Otherwise the results are implementation-defined.
CrossCore Embedded Studio 2.2.0
2–327
ISO/IEC 14822:2003 C++ Standard (C++ Mode)
If setbuf(s, n)
is called before any I/O has occurred, the buffer ' s
', of size ' n
', is used by the I/O routines. Calls to setbuf()
on a stream after I/O has occurred are ignored.
int sync(); Effects: If a put area exists, calls filebuf::overflow to write the characters to the file. If a get area exists, the effect is implementation-defined.
The sync()
function has no effect on the get area.
C.2.2.3 Macro NULL
The macro NULL, defined in any of <clocale>, <cstddef>, <cstdio>, <cstdlib>, <cstring>, <ctime>, or <cwchar>, is an implementation-defined C++ null pointer constant in this International Standard (18.1).
The macro
NULL
is defined as 0.
D.6 Old iostreams Members
The type streamoff is an implementation-defined type that satisfies the requirements of type OFF_T (27.4.1).
streamoff
is a typedef
of the ' long
' type.
The type streampos is an implementation-defined type that satisfies the requirements of type POS_T (27.2).
streampos
is a typedef
of the fpos
class.
2–328
CrossCore Embedded Studio 2.2.0
تنزيل
إعلان