Project:Alpha/Porting guide

Introduction
This guide is intended to show C programmers how to port programs written on other architectures to the Alpha architecture. The overview has some basic information that should be known when porting any application to Alpha. The subsequent sections will have example problems, explanations, and solutions. All of the code examples compile and work without warnings on i386 systems, but fail or are potentially unsafe on alpha.

Endianness
Linux/Alpha is.

Word Size
Alpha is a 64-bit platform. Alpha systems use a 64 bit kernel and a 64 bit userland.

Variable Sizes (in bytes)
The most important things to note here are that the size of  is not the same as the size of any of the pointer types and that the memory must be naturally aligned.


 * sizes are the same for signed and unsigned variables.

Identifying the Problem
You will see compile time warnings about casting between types of different sizes.

cast to pointer from integer of different size warning

cast from pointer to integer of different size warning

Explanation of the Problem
When you store a value in a variable of one size and convert it to another size, bits either get added or chopped from the original value. A lot of programmers assume that integers and pointers are the same size and cast freely between the two. While it works on x86 systems, it can cause problems on systems where the size of an integer is different than the size of a pointer.

Below is an example of a cast to pointer from integer of different size. This cast is dangerous because we are casting a 64 bit pointer to a 32 bit integer. We effectively chop off the 32 most significant bits, then we try to access the memory at that 64 bit address using only the 32 least significant bits. This will cause a segfault unless your program is located in physical memory between address  and address.

{Code|cast to pointer from integer of different size| int main(void) { /* Expected output: "Ice cream has no bones." */   /* Actual   output: "Segmentation fault"      */

int x = (int) "Ice cream has no bones.\n"; printf((const char*)x); return 0; } }}

Below is an example of a cast from pointer to integer of different size. Usually this is done for pointer arithmetic. Most of the time the pointers we use are relatively close to each other and they have a good position in memory, so the program executes normally. However, problems occur when the calculations require more than 32 bits. The example below would seg fault if the  was located at   and the array started at.

cast from pointer to integer of different size

Finding a Solution
When programming, don't try to be smart and do tricks with pointers. Always use the appropriate type, and don't cast between pointer and non-pointer types. Following that advice creates more portable code, and the code produced is generally easier to read.

always use the appropriate storage class

When doing pointer arithmetic, you should avoid casting between pointer and non-pointer types as much as possible.

avoid casting when doing pointer arithmetic

Identifying the Problem
The kernel will log an unaligned trap.

error message in the kernel log

Explanation of the Problem
On Alpha, all memory accesses have to be naturally aligned. For example, the address of a 4 byte integer must be a multiple of 4. The address of an 8 byte (long) integer must be a multiple of 8. The compiler will do its best to align variables automatically, but sometimes you can override this by doing some pointer tricks or adding assembly language code.

When a program does an unaligned memory access, the CPU traps into the kernel. The kernel then does an aligned access and returns the unaligned portion of memory that the program requested. The program continues to function normally as if nothing has happened.

Even though the program will work correctly with unaligned accesses, you should try to remove as many unaligned accesses as possible because they cause a performance hit.

The following example shows how to make an unaligned memory access by manipulating pointers.

unaligned memory access

Finding a Solution
The first step in fixing unaligned traps is determining where the unaligned access happens in our code. To do that we are going to use the following tool.

unalign.c - unaligned access debugging tool

Using this tool is very simple. Compile the code above. Then, compile your program with debugging information via the  compiler flag. Finally, execute  with the name of your program as an argument.

using unalign

Another approach is to use  package which uses   syscall for the same purpose. Make sure you have at least  for it.

using sys-apps/prctl

As with most programming problems, there are multiple solutions. In the first solution we simply pad  with some extra bytes so that   points to an aligned memory address.

no unaligned memory access

In the second solution we use types with the same byte alignment to avoid the possibility of an unaligned access.

no unaligned memory access

Identifying the Problem
You will encounter a Floating point exception at runtime.

Floating point exception

Explanation of the Problem
When the Alpha architecture's floating point unit was first designed, the designers traded performance for functionality. As a result, all Alpha systems below  do not fully implement the IEEE floating point standard. For those earlier systems, there is no hardware support for denormalized numbers or exceptional IEEE values like not a number and positive/negative infinity. If you wish to use those features, you must enable software assisted IEEE floating point at compile time and have kernel support for software completion enabled.

exceptional IEEE values

Finding a Solution
Enable software assisted floating point at compile time with the  compiler flag.

using the -mieee compiler flag

Enable kernel floating point software completion
enabling kernel floating point software completion

You can make sure the program is being compiled with the  compiler flag by checking for. It is defined when a program is compiled with.

give an error if the program isn't compiled with -mieee

Identifying the Problem
You will get unexpected and/or incorrect results.

Explanation of the Problem
Sometimes programmers don't think about Alpha when conditionally compiling sections of code. They may make assumptions, or only consider a subset of all architectures.

In the following example the programmer assumes that if the system compiling the program is, then it is little endian. The programmer also assumes that the only other system type that would compile this program is  and therefore would be big endian.

forget about alpha when determining endianness

Finding a Solution
When determining whether the system compiling your program has a particular property (example, little endian vs. big endian), you should never use. If you do, then to ensure portability you will have to determine if the property exists or not for every architecture, and you will have to add new architectures as they are created. Instead you should use preprocessor definitions, or a runtime check if the definitions are not available.

correctly determining endianness

Identifying the Problem
You will get unexpected and/or incorrect results or possibly a crash.

Explanation of the Problem
Functions like  can accept any number of arguments. The arguments are stored on the stack. Generally, they are accessed by doing pointer arithmetic and casting. The direction in which you iterate over the remaining arguments varies depending on architecture / operating system. On some systems you increment the pointer to reach the next parameter, on other systems you decrement the pointer to reach the next parameter. Furthermore, there is no compile time storage size check, nor type checking.

variable length parameter list

Finding a Solution
Since accessing parameters is so system dependent, we cannot easily write very portable code without the help of some preprocessor macros. Fortunately, those preprocessor macros are already written for us.

variable length parameter list using stdarg.h

Identifying the Problem
A program that displays or uses the CPU frequency for some purpose will fail at runtime.

Explanation of the Problem
There are some programs that determine the CPU frequency from. The format of this file is slightly different on Alpha. Below are example  listings.

/proc/cpuinfo on amd64

Above, in the amd64 example, you will notice that the CPU frequency is given on the  line. Below, in the alpha example, you will notice that the CPU frequency is given on the  line.

/proc/cpuinfo on alpha

conky is an advanced, highly configurable system monitor for X. The program can display your CPU frequency. Below is the  parser that comes with versions of conky prior to 1.4.1. It doesn't work correctly on alpha. It reports a CPU frequency of 0 MHz.

{{Code|/proc/cpuinfo parser from conky|

Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * Neither the name of the  nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

/* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */ void get_freq( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor ) {       FILE *f; char frequency[32]; char s[256]; double freq = 0;

if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 ) return;

f = fopen(CPUFREQ_CURRENT, "r"); if (f) { /* if there's a cpufreq /sys node, read the current frequency from this node; * divide by 1000 to get Mhz. */               if (fgets(s, sizeof(s), f)) { s[strlen(s)-1] = '\0'; freq = strtod(s, NULL); }               fclose(f); snprintf( p_client_buffer, client_buffer_size, p_format, (freq/1000)/divisor ); return; }

f = fopen("/proc/cpuinfo", "r");               //open the CPU information file if (!f) return;

while (fgets(s, sizeof(s), f) != NULL){        //read the file if (strncmp(s, "cpu MHz", 7) == 0) {   //and search for the cpu mhz if (strncmp(s, "clock", 5) == 0) {     // this is different on ppc for some reason strcpy(frequency, strchr(s, ':') + 2); //copy just the number frequency[strlen(frequency) - 1] = '\0'; // strip \n freq = strtod(frequency, NULL); break; }       }
 * 1) if defined(__i386) || defined(__x86_64)
 * 1) else
 * 1) endif

fclose(f); snprintf( p_client_buffer, client_buffer_size, p_format, (float)freq/divisor ); return; } }}

Unlike some of the other portability problems that this document discusses, this problem is not language specific. Here is an example of the problem in python.

/proc/cpuinfo parser written in python

Finding a Solution
To solve the problem we make an exception for alpha by using the  preprocessor definition. We also need to divide the value by 1000000 to get the CPU frequency in MHz.

{{Code|/proc/cpuinfo parser from conky| Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * Neither the name of the  nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

/* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */ void get_freq( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor ) {       FILE *f; char frequency[32]; char s[256]; double freq = 0;

if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 ) return;

f = fopen(CPUFREQ_CURRENT, "r"); if (f) { /* if there's a cpufreq /sys node, read the current frequency from this node; * divide by 1000 to get Mhz. */               if (fgets(s, sizeof(s), f)) { s[strlen(s)-1] = '\0'; freq = strtod(s, NULL); }               fclose(f); snprintf( p_client_buffer, client_buffer_size, p_format, (freq/1000)/divisor ); return; }

f = fopen("/proc/cpuinfo", "r");               //open the CPU information file if (!f) return;

while (fgets(s, sizeof(s), f) != NULL){        //read the file if (strncmp(s, "cpu MHz", 7) == 0) {   //and search for the cpu mhz if (strncmp(s, "cycle frequency [Hz]", 20) == 0) {             // different on alpha if (strncmp(s, "clock", 5) == 0) {     // this is different on ppc for some reason
 * 1) if defined(__i386) || defined(__x86_64)
 * 1) else
 * 2) if defined(__alpha)
 * 1) else
 * 1) endif // defined(__alpha)
 * 2) endif // defined(__i386) || defined(__x86_64)

strcpy(frequency, strchr(s, ':') + 2); //copy just the number frequency[strlen(frequency) - 6] = '\0';// strip " est.\n" freq = strtod(frequency, NULL)/1000000; // kernel reports in Hz               frequency[strlen(frequency) - 1] = '\0'; // strip \n freq = strtod(frequency, NULL); break; }       }
 * 1) if defined(__alpha)
 * 1) else
 * 1) endif

fclose(f); snprintf( p_client_buffer, client_buffer_size, p_format, (float)freq/divisor ); return; } }}

The python fix is similar, just make an exception for alpha by using  to determine the system architecture.

{{Code|/proc/cpuinfo parser written in python| import re, os

def get_freq_alpha: r = re.compile('^cycle frequency \[Hz\]\s+:\s+(\d+)\s+est\.$', re.M)       m = r.search( open('/proc/cpuinfo').read ) return float(int(m.group(1))/1000000.0)

def get_freq: if os.uname[-1] == 'alpha': return get_freq_alpha else: r = re.compile('^cpu MHz\s+:\s+(\d+\.\d+)$', re.M)               m = r.search( open('/proc/cpuinfo').read ) return float(m.group(1)) }}