develooper Front page | perl.perl6.internals | Postings from August 2002

IRIX64 alignment problem

Thread Next
From:
Steven W McDougall
Date:
August 31, 2002 15:46
Subject:
IRIX64 alignment problem
Message ID:
200208312245.SAA119780661@shell.TheWorld.com
I checked out Parrot from CVS on 2002Aug30 to an IRIX64 system. 

    shell01:/usr/tmp/swmcd/parrot>uname -a
    IRIX64 shell01 6.5 07201611 IP27

make compiled OK.
make test failed all the t/pmc/perlhash.t tests.

Running 

    parrot t/pmc/perlhash_1.pbc 

produced a bus error.

I chased the error down to parrot/hash.c: new_bucket(), 
at the line marked below.

    static BucketIndex
    new_bucket(Interp *interpreter, HASH *hash, STRING *key, HASH_ENTRY *value)
    {
	BucketIndex bucket_index;
    
	if (key == NULL) {
	    internal_exception(INTERNAL_PANIC, "NULL key\n");
	    return NULLBucketIndex;
	}
    
	if (value == NULL) {
	    internal_exception(INTERNAL_PANIC, "NULL value\n");
	    return NULLBucketIndex;
	}
    
	bucket_index = hash->free_list;
	if (bucket_index != NULLBucketIndex) 
	{
	    HASHBUCKET *bucket = getBucket(hash, bucket_index);
	    
	    hash->free_list = bucket->next;
	    bucket->key = key;
====>>>>    bucket->value = *value;   <<<<==== BUS ERROR HERE
    
	    return bucket_index;
	}
    
	/* Free list is empty. Need to expand the hashtable. */
	expand_hash(interpreter, hash);
	return new_bucket(interpreter, hash, key, value);
    }   

bucket->value is a HASH_ENTRY, which is declared in
parrot/include/parrot/hash.h as

    typedef struct _hash_entry {
	HASH_ENTRY_TYPE type;
	UnionVal val;
    } HASH_ENTRY;

A little hacking showed that the bus error was occurring on the
assignment of val. val is a UnionVal, declared in
parrot/include/parrot/interpreter.h as

    typedef union UnionVal {
	INTVAL int_val;
	FLOATVAL num_val;
	DPOINTER* struct_val;
	STRING* string_val;
	PMC* pmc_val;
    } UnionVal;

Some printf's further showed that 

     (*value).val

was 8-byte aligned, while

    bucket->value.val

was 4 byte aligned.

sizeof(FLOATVAL) is 8 on IRIX64, so this starts to look like a memory
alignment problem. The next interesting question is how
bucket->value.val got be 4-byte aligned.

Here is a (very) schematic account of where bucket->value.val comes
from 

    new_bucket()
    get_bucket()
    hash->bucket_pool->bufstart
    new_hash()
    expand_hash()
    Parrot_reallocate()
    mem_allocate()
    
mem_allocate() is declared in resources.c as

    static void *
    mem_allocate(struct Parrot_Interp *interpreter, size_t *req_size,
		 struct Memory_Pool *pool, size_t align_1)

mem_allocate() allocates memory from a Memory_Pool. A Memory_Pool
manages a list of Memory_Block's. A Memory_Block manages a single
block of memory. It is declared as

    struct Memory_Block {
	size_t free;
	size_t size;
	struct Memory_Block *prev;
	struct Memory_Block *next;
	char *start;
	char *top;
    };  

start points to the entire block of memory, and top points to the next
byte to be allocated from the block. Initially, top is equal to start.
Each call to mem_allocate() advances top by the size of the block to
be allocated and then returns the previous value of top.

The align_1 parameter to mem_allocate() does not--as you might
think--control the alignment of the allocated block. Rather, it
controls the *size* of the allocated block, rounding it up to the next
multiple of the power of 2 indicated by align_1. This means that the
alignment of a block returned by mem_allocate() is dependent on the
*previous* calls to mem_allocate().

printf's show that start is 8-byte aligned on my system. As it
happens, something upstream of new_bucket() calls mem_allocate() to
allocate 18 bytes with an alignment of 4. mem_allocate() dutifully
rounds 18 up to 20 (the next multiple of 4) and allocates 20 bytes
from the block. (Plus some headers and padding, but the sizes of these
are both multiples of 8, so they don't affect what happens next.)

This puts top at a 4-byte aligned address. Later, when
new_bucket() obtains storage from mem_alloc(), it gets the 4-byte
aligned block, and the bus error follows.

I tested this theory by hacking mem_allocate() to enforce a minimum
round-up to an 8-type boundary. 

    /* Round up to requested alignment */
    if (align_1<7) align_1 = 7;  /* <<<==== HACK */
    size = (size + align_1) & ~align_1;

This keeps top 8-byte aligned. The bus error went away, and
t/pmc/perlhash_1.pbc produced the correct output.


I don't know how this thing is supposed to work, so I don't know how
to fix it. Logically, there are a few possibilities.

1. Fix gcc on IRIX64 to permit assignment of unions with arbitrary
   alignment.

2. Add a parameter to mem_alloc() (or use align_1) to control the
   alignment of the allocated block.

3. Require all callers of mem_alloc() to pass an align_1 parameter
   big enough to meet the alignment requirements of the platform.
   (Seems like the Wrong Thing.)


- SWM

Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About