/*
* %CopyrightBegin%
*
* Copyright Ericsson AB 2003-2011. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* %CopyrightEnd%
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "global.h"
#include "hipe_stack.h"
/*
* Native-code stack descriptor hash table.
*
* This uses a specialised version of BEAM's hash table code:
* - Hash table size is always a power of two.
* Permits replacing an expensive integer division operation
* with a cheap bitwise 'and' in the hash index calculation
* - Lookups assume the key is in the table.
* Permits removing NULL checks.
* - Switched order of the hash bucket next and hvalue fields.
* The hvalue field, which must always be checked, gets a zero
* structure offset, which is faster on some architectures;
* the next field is only referenced if hvalue didn't match.
* These changes yield a much more efficient lookup operation.
*/
struct hipe_sdesc_table hipe_sdesc_table;
static struct sdesc **alloc_bucket(unsigned int size)
{
unsigned long nbytes = size * sizeof(struct sdesc*);
struct sdesc **bucket = erts_alloc(ERTS_ALC_T_HIPE, nbytes);
sys_memzero(bucket, nbytes);
return bucket;
}
static void hipe_grow_sdesc_table(void)
{
unsigned int old_size, new_size, new_mask;
struct sdesc **old_bucket, **new_bucket;
unsigned int i;
old_size = 1 << hipe_sdesc_table.log2size;
hipe_sdesc_table.log2size += 1;
new_size = 1 << hipe_sdesc_table.log2size;
new_mask = new_size - 1;
hipe_sdesc_table.mask = new_mask;
old_bucket = hipe_sdesc_table.bucket;
new_bucket = alloc_bucket(new_size);
hipe_sdesc_table.bucket = new_bucket;
for (i = 0; i < old_size; ++i) {
struct sdesc *b = old_bucket[i];
while (b != NULL) {
struct sdesc *next = b->bucket.next;
unsigned int j = (b->bucket.hvalue >> HIPE_RA_LSR_COUNT) & new_mask;
b->bucket.next = new_bucket[j];
new_bucket[j] = b;
b = next;
}
}
erts_free(ERTS_ALC_T_HIPE, old_bucket);
}
struct sdesc *hipe_put_sdesc(struct sdesc *sdesc)
{
unsigned long ra;
unsigned int i;
struct sdesc *chain;
unsigned int size;
ra = sdesc->bucket.hvalue;
i = (ra >> HIPE_RA_LSR_COUNT) & hipe_sdesc_table.mask;
chain = hipe_sdesc_table.bucket[i];
for (; chain != NULL; chain = chain->bucket.next)
if (chain->bucket.hvalue == ra)
return chain; /* collision! (shouldn't happen) */
sdesc->bucket.next = hipe_sdesc_table.bucket[i];
hipe_sdesc_table.bucket[i] = sdesc;
hipe_sdesc_table.used += 1;
size = 1 << hipe_sdesc_table.log2size;
if (hipe_sdesc_table.used > (4*size)/5) /* rehash at 80% */
hipe_grow_sdesc_table();
return sdesc;
}
void hipe_init_sdesc_table(struct sdesc *sdesc)
{
unsigned int log2size, size;
log2size = 10;
size = 1 << log2size;
hipe_sdesc_table.log2size = log2size;
hipe_sdesc_table.mask = size - 1;
hipe_sdesc_table.used = 0;
hipe_sdesc_table.bucket = alloc_bucket(size);
hipe_put_sdesc(sdesc);
}
/*
* XXX: x86 and SPARC currently use the same stack descriptor
* representation. If different representations are needed in
* the future, this code has to be made target dependent.
*/
struct sdesc *hipe_decode_sdesc(Eterm arg)
{
Uint ra, exnra;
Eterm *live;
Uint fsize, arity, nlive, i, nslots, off;
Uint livebitswords, sdescbytes;
void *p;
struct sdesc *sdesc;
if (is_not_tuple(arg) ||
(tuple_val(arg))[0] != make_arityval(6) ||
term_to_Uint((tuple_val(arg))[1], &ra) == 0 ||
term_to_Uint((tuple_val(arg))[2], &exnra) == 0 ||
is_not_small((tuple_val(arg))[3]) ||
(fsize = unsigned_val((tuple_val(arg))[3])) > 65535 ||
is_not_small((tuple_val(arg))[4]) ||
(arity = unsigned_val((tuple_val(arg))[4])) > 255 ||
is_not_tuple((tuple_val(arg))[5]))
return 0;
/* Get tuple with live slots */
live = tuple_val((tuple_val(arg))[5]) + 1;
/* Get number of live slots */
nlive = arityval(live[-1]);
/* Calculate size of frame = locals + ra + arguments */
nslots = fsize + 1 + arity;
/* Check that only valid slots are given. */
for (i = 0; i < nlive; ++i) {
if (is_not_small(live[i]) ||
(off = unsigned_val(live[i]), off >= nslots) ||
off == fsize)
return 0;
}
/* Calculate number of words for the live bitmap. */
livebitswords = (fsize + arity + 1 + 31) / 32;
/* Calculate number of bytes needed for the stack descriptor. */
sdescbytes =
(exnra
? offsetof(struct sdesc_with_exnra, sdesc.livebits)
: offsetof(struct sdesc, livebits))
+ livebitswords * sizeof(int);
p = erts_alloc(ERTS_ALC_T_HIPE, sdescbytes);
/* If we have an exception handler use the
special sdesc_with_exnra structure. */
if (exnra) {
struct sdesc_with_exnra *sdesc_we = p;
sdesc_we->exnra = exnra;
sdesc = &(sdesc_we->sdesc);
} else
sdesc = p;
/* Initialise head of sdesc. */
sdesc->bucket.next = 0;
sdesc->bucket.hvalue = ra;
sdesc->summary = (fsize << 9) | (exnra ? (1<<8) : 0) | arity;
/* Clear all live-bits */
for (i = 0; i < livebitswords; ++i)
sdesc->livebits[i] = 0;
/* Set live-bits given by caller. */
for (i = 0; i < nlive; ++i) {
off = unsigned_val(live[i]);
sdesc->livebits[off / 32] |= (1 << (off & 31));
}
#ifdef DEBUG
{
Eterm mfa_tpl = tuple_val(arg)[6];
sdesc->dbg_M = tuple_val(mfa_tpl)[1];
sdesc->dbg_F = tuple_val(mfa_tpl)[2];
sdesc->dbg_A = tuple_val(mfa_tpl)[3];
}
#endif
return sdesc;
}