64bit/32bit compatibility

From Cellbe

(Difference between revisions)

Revision as of 01:18, 14 July 2007

This article describes how we deal with 64bit/32bit code portability. Since we're writing a library (there's no info about that now on the wiki, but there will be soon), we want to make sure an application can link against it, no matter if it's compiled in 32 bit or 64 bit mode. And, of course, we don't want our code to be cluttered by wordsize checks and/or conditional compilation directives, and we like to have a uniform way to export/use addresses in spe and ppe programs.

Here are described solutions for both C and C++ code.

Contents

C code

Pointer export structure

/* =============================================================================
 *										
 *         Author:  Alessandro Piras (laynor@gmail.com)				
 *   Description :  structure used to export effective addresses		
 *        Company:								
 *										
 * ===========================================================================*/
#pragma once
union ea_t{
    struct{
	unsigned h;
	unsigned l;
    };
    unsigned long long e;
    struct{
#ifndef __powerpc64__
	unsigned int reserved;
#endif
	void *p;
    };
};

As you can see, this union has the usual fields you can find in many examples found in the cell sdk by IMB (fields h, l, and e). Fields h and l are inside an anonymous structure, so you can access them easily:

union eat addr;
...
...
addr.h=something;
addr.l=something_else;

There is also another field, p, that is used when exporting the pointer. It's a void*, so no casting is required. The field reserved is present only when compiling the code in 32 bit mode, and should not be touched.

How to use it

Let's say you have a buffer buff, and you want to export its address. No matter if you are doing this in ppe32, ppe64 or spe code, you have to do:

float buff[BUFFERSIZE] __attribute__((aligned(16))); /*this is just an example*/
ea_t buff_addr;
...
buff_addr.e=0; /* assign 0 to the e field to remove possible 
		  garbage when the code is compiled in 32 bit mode */

buff_addr.p=buff; //No casting required!

Now, let's say you you did that in an spe, and want to send the exported address to the ppe via mailboxes. This is easily done:

spu_write_out_mbox(buff_addr.h);
spu_write_out_mbox(buff_addr.l);

To read it, at the ppe side, you'll do just

ea_t buff_ls_addr;
...
...
while(!spe_out_mbox_status(ctx1));
spe_out_mbox_read(ctx1, &buff_ls_addr.h, 1);
while(!spe_out_mbox_status(ctx1));
spe_out_mbox_read(ctx1, &buff_ls_addr.l, 1);

Now, on the ppe side, to use the exported address as an effective address you have to sum the effective base address of the local store to the exported address. Easy to do, the code becomes

ea_t buff_ls_addr;
ea_t base_ls_addr;
ea_t buff_effective_address;
...
...
while(!spe_out_mbox_status(ctx1));
spe_out_mbox_read(ctx1, &buff_ls_addr.h, 1);
while(!spe_out_mbox_status(ctx1));
spe_out_mbox_read(ctx1, &buff_ls_addr.l, 1);
buff_ls_addr.e = 0;
buff_ls_addr.p = spe_ls_area_get(ctx1); /*getting LS base address*/
buff_effective_addresses.e = buff_ls_addr.e + base_ls_addr.e;

When using an exported address, the e field always contains the right value to use for dma transfers. You can find a simple but complete example in SPE_to_SPE_DMA_transfer.

C++ code

When writing (and compiling!) C++ code, assigning an int * to a void * raises a type error. To avoid ugly casts, we devised a similar export scheme.

Pointer export structure

/* =============================================================================
 *										
 *         Author:  Alessandro Piras (laynor@gmail.com)				
 *   Description :  structure used to export effective addresses		
 *        Company:								
 *										
 * ===========================================================================*/
#pragma once
template<class T>
union ea_t{
    struct{
	unsigned h;
	unsigned l;
    };
    unsigned long long e;
    struct{
#ifndef __powerpc64__
	unsigned int reserved;
#endif
	T p;
    };
};

As you can see, the structure is the same but the type of the p field. Follows usage.

How to use it

Everything said for the structure used in C code is still true, (remember to set the e field to 0 before setting the p field if you want your code to work in 32 bit mode too!). The declaration changes though, as you have to instantiate the template to the right pointer type. So, as before, let's say you want to export buff's address, which is a float array.

float buff[256] __attribute__((aligned (16)));
ea_t<float *> buff_addr;
...
...
buff_addr.e = 0;
buff_addr.p = buff;

Same as the C version and, of course, no casts :-)

Personal tools