HPlogo HP C/HP-UX Programmer's Guide: Workstations and Servers > Chapter 2 Storage and Alignment Comparisons

The HP_ALIGN Pragma

» 

Technical documentation

Complete book in PDF

 » Table of Contents

 » Index

The HP_ALIGN pragma controls data storage allocation and alignment of structures, unions, and type definitions, using typedefs. It enables you to control the alignment mode when allocating storage space for data. It is especially important when used to control the allocation of binary data that is transmitted among machines having different hardware architectures.

The HP_ALIGN pragma takes a parameter indicating which alignment mode to use. Not all modes are available on all HP platforms; the NATURAL alignment mode is the most widely available on HP-UX. This mode is the recommended standard.

The syntax for the HP_ALIGN pragma is:

#pragma HP_ALIGN align_mode [ PUSH ]
#pragma HP_ALIGN [ POP ]

where align_mode is one of the following:

  • HPUX_WORD

    This is the Series 300/400 default alignment mode.

  • HPUX_NATURAL_S500

    This is the Series 500 default alignment mode.

  • HPUX_NATURAL

    This is the HP 9000 workstations and servers and HP 3000 Series 900 systems default alignment mode.

  • NATURAL

    This mode provides a consistent alignment scheme across HP architectures.

  • DOMAIN_WORD

    This is the default word alignment mode on HP Apollo architecture.

  • DOMAIN_NATURAL

    This is the default natural alignment mode on HP Apollo architecture.

  • NOPADDING

    This causes all structures and union members that are not bit-fields to be packed on a byte boundary. It does not cause compressed packing where there are zero bits of padding. It only insures that there will be no full bytes of padding in the structure or union.

NOTE: The above alignment modes are only available on HP-UX systems.

The HP_ALIGN pragma affects struct and union definitions as well as typedef declarations. It causes data objects that are later declared using these types to have the size and alignment as specified by the pragma.

The alignment pragma in effect at the time of data type declaration has significance. The alignment pragma in effect at the time of data type declaration has precedence over the alignment pragma in effect when space for a data object of the previously declared type is allocated.

Using the HP_ALIGN Pragma

The HP_ALIGN pragma allows you to control data storage allocation and alignment of structures, unions, and typedefs.

NOTE: The basic scalar types, array types, enumeration types, and pointer types are not affected by the HP_ALIGN pragma. The pragma only affects struct or union types and typedefs - No other types are affected by specifying the HP_ALIGN pragma.

The HP_ALIGN pragma takes a parameter that specifies the alignment mode for example:

      #pragma HP_ALIGN HPUX_NATURAL

There is also an optional parameter PUSH, which saves the current alignment mode before setting the specified mode as the new alignment mode. For example, in the following sequence:

      #pragma HP_ALIGN NOPADDING PUSH
/* decls following */

the current alignment mode is saved on the stack. It is then set to the new alignment mode, NOPADDING.

The PUSHed alignment mode can be retrieved later by doing a

      #pragma HP_ALIGN POP

If the last alignment mode PUSHed on the stack was NOPADDING, the current alignment mode would now be NOPADDING.

Problems Sometimes Encountered with the HP_ALIGN Pragma

If only one alignment mode is used throughout the entire file, this pragma is straightforward to use and to understand. However, when a different mode is introduced in the middle of the file, you should be aware of its implications and effects.

The key to understanding HP_ALIGN is the following concept: typedefs and struct or union types retain their original alignment mode throughout the entire file. Therefore, when a type with one alignment is used in a different alignment mode, it will still keep its original alignment.

This feature may lead to confusion when you have a typedef, structure or union of one alignment nested inside a typedef, structure or union of another alignment.

Here are some examples of the most common misunderstandings.

Example 1: Using Typedefs

The alignment pragma will affect typedef, struct, and union types. Therefore, in the following declaration:

    #pragma HP_ALIGN HPUX_WORD
typedef int int32;

int32 is not equivalent to int. To illustrate:

     #pragma HP_ALIGN HPUX_WORD

typedef int int32;

void routine (int *x);

int main()
{
int *ok;
int32 *bad;

routine(ok);
routine(bad); /* warning */
}

Compiling this with -Aa -c will give two warnings:

     warning 604: Pointers are not assignment-compatible.
warning 563: Argument #1 is not the correct type.

These warnings occur because the actual pointer value of bad may not be as strictly aligned as the pointer type routine expects. This may lead to run-time bus errors in the called function if it dereferences the misaligned pointer.

Example 2: Using Combination of Different Alignment Modes

In the WORD alignment modes, the members of a structure whose sizes are larger than 2 bytes are aligned on a 2-byte boundary. However, this is only true if those member types are scalar or have been previously declared under the same alignment mode. If the member type is a typedef, struct, or union type which has been declared previously under a different alignment mode, it will retain its original alignment, regardless of current alignment mode in effect. For example:

     typedef int my_int;

#pragma HP_ALIGN HPUX_WORD

struct st {
char c;
my_int i;
};

int main()
{
char c;
struct st foo;
}

Although the size of my_int is greater than 2 bytes, because it was declared previously under HPUX_NATURAL with the alignment of 4 bytes it will be aligned on a 4-byte boundary, causing the entire struct st to be aligned on a 4-byte boundary. Compiling with the +m option to show the offsets of the identifiers (offsets given as "byte-offset @ bit-offset" in hexadecimal), you will get the following output:

                                   main

Identifier Class Type Address
- - -
c auto char SP-48
foo auto struct st SP-44
c member char 0x0 @ 0x0
i member int 0x4 @ 0x0

The resulting size of foo is 8 bytes, with 4-byte alignment.

If you change the type of member i in struct st to be a simple int type, then you will get the following result:

                                   main

Identifier Class Type Address
- -
c auto char SP-40
foo auto struct st SP-38
c member char 0x0 @ 0x0
i member int 0x2 @ 0x0

This time, the resulting size of foo is 6 bytes, with 2-byte alignment.

Example 3: Incorrect Use of Typedefs and Alignments

Often, you might mix typedefs and alignments without being aware of the actual alignment of the data types.

What may appear to be correct usages of these data types may turn out to be causes for misaligned pointers and run-time bus errors, among other things. For example, consider the following program.

    <my_include.h>
typedef unsigned short ushort;
extern int get_index(void);
extern ushort get_value(void);

<my_prog.c>
#include "my_include.h"

#pragma HP_ALIGN NOPADDING PUSH
struct s {
ushort member1;
ushort member2;
};
#pragma HP_ALIGN POP

char myBuffer[100];

int main()
{
struct s *my_struct;
int index = get_index();
int value = get_value();
int not_done = 1;

while (not_done) {
my_struct = (struct s*)&myBuffer[index];
my_struct->member1 = value;
.
.
.

}
}

This code is not written safely. Although struct s is declared under NOPADDING alignment mode, it has 2-byte alignment due to the typedef for ushort. However, a pointer to struct s can be assigned an address that can point to anywhere in the char array (including odd addresses). If the function get_index always returns an even number, you will not run into any problems, because it will always be 2-byte aligned. However, if the index happens to be an odd number, &myBuffer[index] will be an odd address. Dereferencing that pointer to store into a 2-byte aligned member will result in a run-time bus error.

Below are some examples of what you can do to avoid such behavior.

  • Compile with +u1 option, which forces all pointer dereferences to assume that data is aligned on 1-byte boundaries. However, this will have a negative impact on performance.

  • Put the typedef inside the NOPADDING alignment. However, if you use ushort in contexts where it must have 2-byte alignment, this may not be what you want.

  • Declare struct s with the basic type unsigned short rather than the typedef ushort.

  • Make sure that the pointer will always be 2-byte aligned by returning an even index into the char array.

  • Declare another typedef for ushort under the NOPADDING alignment:

            typedef ushort ushort_1

    and use the new type ushort_1 inside struct s.

As mentioned above, the HP_ALIGN pragma must have a global scope; it must be outside of any function or enclosing structure or union. For example, suppose you have the following sequence of pragmas:

#pragma HP_ALIGN HPUX_WORD PUSH

struct string_1 {
char *c_string;
int counter;
};

#pragma HP_ALIGN HPUX_NATURAL PUSH

struct car {
long double car_speed;
char *car_type;
};

#pragma HP_ALIGN POP

struct bus {
int bus_number;
char bus_color;
};

#pragma HP_ALIGN POP

Variables declared of type struct string_1, are aligned according to the HPUX_WORD alignment mode. Variables declared of type struct car, are aligned according to the HPUX_NATURAL alignment mode. Variables declared of type struct bus are aligned according to HPUX_WORD.

Accessing Non-Natively Aligned Data with Pointers

Be careful when using pointers to access non-natively aligned data types within structures and unions. Alignment information is significant, as pointers may be dereferenced with either 8-bit, 16-bit, or 32-bit machine instructions. Dereferencing a pointer with an incompatible machine instruction usually results in a run-time error.

HP C permanently changes the size and alignment information of typedefs defined within the scope of an HP_ALIGN pragma. It makes data objects, such as pointers, declared by using typedefs, compatible with similar objects defined within the scope of the pragma.

For example, a pointer to an integer type declared with a typedef that is affected by the HP_ALIGN pragma will be dereferenced safely when it points to an integer object whose alignment is the same as that specified in the pragma.

The typedef alignment information is persistent outside the scope of the HP_ALIGN pragma. An object declared with a typedef will have the same storage and alignment as all other objects declared with the same typedef, regardless of the location of other HP_ALIGN pragma statements in the program.

There is a slight performance penalty for using non-native data alignments. The compiler generates slower but safe code for dereferencing non-natively aligned data. It generates more efficient code for natively aligned data.

The following program generates a run-time error because a pointer that expects word-aligned data is used to access a half-word aligned item:

#pragma HP_ALIGN HPUX_WORD

struct t1 { char a; int b;} non_native_rec;

#pragma HP_ALIGN POP

main ()
{
int i;
int *p = &non_native_rec.b;
i = *p; /* assignment causes run-time bus error */
}

The following program works as expected because the pointer has the same alignment as the structure:

#pragma HP_ALIGN HPUX_WORD

struct t1 { char a; int b;} non_native_rec;
typedef int non_native_int;

#pragma HP_ALIGN POP

main ()
{
int i;
non_native_int *p = &non_native_rec.b;
i = *p;
}

An alternative to using the HP_ALIGN pragma and typedefs to control non-natively aligned pointers is to use the +ubytes compiler option of HP C/HP-UX. The +ubytes forces all pointer dereferences to assume that data is aligned on 8-bit, 16-bit, or 32-bit addresses. The value of bytes can be 1 (8-bit), 2 (16-bit), or 4 (32-bit). This option can be used when accessing non-natively aligned data with pointers that would otherwise be natively aligned. This option can be useful with code that generates the compiler warning message

#565 - "address operator applied to non natively aligned member."

and aborts with a run-time error.

The +ubytes option affects all pointer dereferences within the source file. It can have a noticeable, negative impact on performance.

NOTE: The HP C/iX implementation of the +u option omits the bytes parameter.

Defining Platform Independent Data Structures

One way to avoid trouble caused by differences in data alignment is to define structures so they are aligned the same on different systems. To do this, use padding bytes — that is, dummy variables to align fields the same way on different architectures.

For example, use:

   struct {
char cl;
char dum1;
char dum2;
char dum3;
int i1;
};

instead of:

struct {
char c1;
int i1;
};
© Hewlett-Packard Development Company, L.P.