2014-12-22 12:35:05 +01:00
|
|
|
|
/* Read-only tables for Lua */
|
2015-09-01 07:11:36 +02:00
|
|
|
|
#define LUAC_CROSS_FILE
|
2014-12-22 12:35:05 +01:00
|
|
|
|
|
|
|
|
|
#include "lua.h"
|
2015-09-01 07:11:36 +02:00
|
|
|
|
#include C_HEADER_STRING
|
|
|
|
|
#include "lrotable.h"
|
2014-12-22 12:35:05 +01:00
|
|
|
|
#include "lauxlib.h"
|
|
|
|
|
#include "lstring.h"
|
|
|
|
|
#include "lobject.h"
|
|
|
|
|
#include "lapi.h"
|
|
|
|
|
|
2019-02-23 15:09:29 +01:00
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
|
#define ALIGNED_STRING (__declspec( align( 4 ) ) char*)
|
|
|
|
|
#else
|
2018-10-13 16:14:33 +02:00
|
|
|
|
#define ALIGNED_STRING (__attribute__((aligned(4))) char *)
|
2019-02-23 15:09:29 +01:00
|
|
|
|
#endif
|
2019-05-17 14:04:19 +02:00
|
|
|
|
|
|
|
|
|
#ifdef LUA_CROSS_COMPILER
|
|
|
|
|
#undef IRAM_DATA_ATTR
|
|
|
|
|
#define IRAM_DATA_ATTR
|
|
|
|
|
#endif
|
|
|
|
|
|
2019-05-08 13:08:20 +02:00
|
|
|
|
#define LA_LINES 32
|
2018-10-13 16:14:33 +02:00
|
|
|
|
#define LA_SLOTS 4
|
|
|
|
|
//#define COLLECT_STATS
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* All keyed ROtable access passes through luaR_findentry(). ROTables
|
|
|
|
|
* are simply a list of <key><TValue value> pairs. The existing algo
|
|
|
|
|
* did a linear scan of this vector of pairs looking for a match.
|
|
|
|
|
*
|
|
|
|
|
* A N×M lookaside cache has been added, with a simple hash on the key's
|
|
|
|
|
* TString addr and the ROTable addr to identify one of N lines. Each
|
|
|
|
|
* line has M slots which are scanned. This is all done in RAM and is
|
|
|
|
|
* perhaps 20x faster than the corresponding random Flash accesses which
|
|
|
|
|
* will cause flash faults.
|
|
|
|
|
*
|
|
|
|
|
* If a match is found and the table addresses match, then this entry is
|
|
|
|
|
* probed first. In practice the hit-rate here is over 99% so the code
|
|
|
|
|
* rarely fails back to doing the linear scan in ROM.
|
|
|
|
|
*
|
|
|
|
|
* Note that this hash does a couple of prime multiples and a modulus 2^X
|
|
|
|
|
* with is all evaluated in H/W, and adequately randomizes the lookup.
|
|
|
|
|
*/
|
2019-05-17 14:04:19 +02:00
|
|
|
|
#define HASH(a,b) (unsigned)((((519*(size_t)(a)))>>4) + ((b) ? (b)->tsv.hash: 0))
|
2018-10-13 16:14:33 +02:00
|
|
|
|
|
2019-05-17 14:04:19 +02:00
|
|
|
|
typedef struct {
|
2018-10-13 16:14:33 +02:00
|
|
|
|
unsigned hash;
|
|
|
|
|
unsigned addr:24;
|
|
|
|
|
unsigned ndx:8;
|
2019-05-17 14:04:19 +02:00
|
|
|
|
} cache_line_t;
|
|
|
|
|
|
|
|
|
|
static cache_line_t IRAM_DATA_ATTR cache[LA_LINES][LA_SLOTS];
|
2018-10-13 16:14:33 +02:00
|
|
|
|
|
|
|
|
|
#ifdef COLLECT_STATS
|
|
|
|
|
unsigned cache_stats[3];
|
|
|
|
|
#define COUNT(i) cache_stats[i]++
|
|
|
|
|
#else
|
|
|
|
|
#define COUNT(i)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static int lookup_cache(unsigned hash, ROTable *rotable) {
|
|
|
|
|
int i = (hash>>2) & (LA_LINES-1), j;
|
|
|
|
|
|
|
|
|
|
for (j = 0; j<LA_SLOTS; j++) {
|
2019-05-17 14:04:19 +02:00
|
|
|
|
cache_line_t cl = cache[i][j];
|
|
|
|
|
if (cl.hash == hash && ((size_t)rotable & 0xffffffu) == cl.addr) {
|
2018-10-13 16:14:33 +02:00
|
|
|
|
COUNT(0);
|
2019-05-17 14:04:19 +02:00
|
|
|
|
return cl.ndx;
|
2014-12-22 12:35:05 +01:00
|
|
|
|
}
|
2018-10-13 16:14:33 +02:00
|
|
|
|
}
|
|
|
|
|
COUNT(1);
|
|
|
|
|
return -1;
|
2014-12-22 12:35:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-13 16:14:33 +02:00
|
|
|
|
static void update_cache(unsigned hash, ROTable *rotable, unsigned ndx) {
|
|
|
|
|
int i = (hash)>>2 & (LA_LINES-1), j;
|
2019-05-17 14:04:19 +02:00
|
|
|
|
#ifndef _MSC_VER
|
|
|
|
|
cache_line_t cl = {hash, (size_t) rotable, ndx};
|
|
|
|
|
#else
|
|
|
|
|
cache_line_t cl; // MSC doesn't allow non-scalar initialisers, which
|
|
|
|
|
cl.hash = hash; // is a pity because xtensa gcc generates optimum
|
|
|
|
|
cl.addr = (size_t) rotable; // code using them.
|
|
|
|
|
cl.ndx = ndx;
|
|
|
|
|
#endif
|
|
|
|
|
|
2018-10-13 16:14:33 +02:00
|
|
|
|
COUNT(2);
|
|
|
|
|
if (ndx>0xffu)
|
|
|
|
|
return;
|
|
|
|
|
for (j = LA_SLOTS-1; j>0; j--)
|
|
|
|
|
cache[i][j] = cache[i][j-1];
|
2019-05-17 14:04:19 +02:00
|
|
|
|
cache[i][0] = cl;
|
2018-10-13 16:14:33 +02:00
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* Find a string key entry in a rotable and return it. Note that this internally
|
|
|
|
|
* uses a null key to denote a metatable search.
|
|
|
|
|
*/
|
|
|
|
|
const TValue* luaR_findentry(ROTable *rotable, TString *key, unsigned *ppos) {
|
|
|
|
|
const luaR_entry *pentry = rotable;
|
|
|
|
|
const char *strkey = key ? getstr(key) : ALIGNED_STRING "__metatable" ;
|
2019-05-08 13:08:20 +02:00
|
|
|
|
unsigned hash = HASH(rotable, key);
|
|
|
|
|
|
2018-10-13 16:14:33 +02:00
|
|
|
|
unsigned i = 0;
|
|
|
|
|
int j = lookup_cache(hash, rotable);
|
2019-03-24 23:47:29 +01:00
|
|
|
|
unsigned l = key ? key->tsv.len : sizeof("__metatable")-1;
|
2018-10-13 16:14:33 +02:00
|
|
|
|
|
|
|
|
|
if (pentry) {
|
2019-05-08 13:08:20 +02:00
|
|
|
|
if (j >= 0 && !c_strcmp(pentry[j].key, strkey)) {
|
|
|
|
|
if (ppos)
|
|
|
|
|
*ppos = j;
|
|
|
|
|
//dbg_printf("%3d hit %p %s\n", (hash>>2) & (LA_LINES-1), rotable, strkey);
|
|
|
|
|
return &pentry[j].value;
|
2018-10-13 16:14:33 +02:00
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* The invariants for 1st word comparison are deferred to here since they
|
|
|
|
|
* aren't needed if there is a cache hit. Note that the termination null
|
|
|
|
|
* is included so a "on\0" has a mask of 0xFFFFFF and "a\0" has 0xFFFF.
|
|
|
|
|
*/
|
2019-03-24 23:47:29 +01:00
|
|
|
|
unsigned name4, mask4 = l > 2 ? (~0u) : (~0u)>>((3-l)*8);
|
|
|
|
|
c_memcpy(&name4, strkey, sizeof(name4));
|
|
|
|
|
|
2019-05-08 13:08:20 +02:00
|
|
|
|
for(;pentry->key != NULL; i++, pentry++) {
|
|
|
|
|
if (((*(unsigned *)pentry->key ^ name4) & mask4) == 0 &&
|
|
|
|
|
!c_strcmp(pentry->key, strkey)) {
|
|
|
|
|
//dbg_printf("%p %s hit after %d probes \n", rotable, strkey, (int)(rotable-pentry));
|
2018-10-13 16:14:33 +02:00
|
|
|
|
if (ppos)
|
|
|
|
|
*ppos = i;
|
2019-05-08 13:08:20 +02:00
|
|
|
|
update_cache(hash, rotable, pentry - rotable);
|
|
|
|
|
//dbg_printf("%3d %3d %p %s\n", (hash>>2) & (LA_LINES-1), (int)(pentry-rotable), rotable, strkey);
|
|
|
|
|
return &pentry->value;
|
2018-10-13 16:14:33 +02:00
|
|
|
|
}
|
2014-12-22 12:35:05 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-05-08 13:08:20 +02:00
|
|
|
|
//dbg_printf("%p %s miss after %d probes \n", rotable, strkey, (int)(rotable-pentry));
|
2018-10-13 16:14:33 +02:00
|
|
|
|
return luaO_nilobject;
|
2014-12-22 12:35:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Find the metatable of a given table */
|
2018-10-13 16:14:33 +02:00
|
|
|
|
void* luaR_getmeta(ROTable *rotable) {
|
|
|
|
|
const TValue *res = luaR_findentry(rotable, NULL, NULL);
|
2014-12-22 12:35:05 +01:00
|
|
|
|
return res && ttisrotable(res) ? rvalue(res) : NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-13 16:14:33 +02:00
|
|
|
|
static void luaR_next_helper(lua_State *L, ROTable *pentries, int pos,
|
|
|
|
|
TValue *key, TValue *val) {
|
2019-05-08 13:08:20 +02:00
|
|
|
|
if (pentries[pos].key) {
|
2014-12-22 12:35:05 +01:00
|
|
|
|
/* Found an entry */
|
2019-05-08 13:08:20 +02:00
|
|
|
|
setsvalue(L, key, luaS_new(L, pentries[pos].key));
|
|
|
|
|
setobj2s(L, val, &pentries[pos].value);
|
|
|
|
|
} else {
|
|
|
|
|
setnilvalue(key);
|
|
|
|
|
setnilvalue(val);
|
2014-12-22 12:35:05 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-13 16:14:33 +02:00
|
|
|
|
|
|
|
|
|
|
2014-12-22 12:35:05 +01:00
|
|
|
|
/* next (used for iteration) */
|
2018-10-13 16:14:33 +02:00
|
|
|
|
void luaR_next(lua_State *L, ROTable *rotable, TValue *key, TValue *val) {
|
2014-12-22 12:35:05 +01:00
|
|
|
|
unsigned keypos;
|
2018-10-13 16:14:33 +02:00
|
|
|
|
|
2014-12-22 12:35:05 +01:00
|
|
|
|
/* Special case: if key is nil, return the first element of the rotable */
|
2018-10-13 16:14:33 +02:00
|
|
|
|
if (ttisnil(key))
|
|
|
|
|
luaR_next_helper(L, rotable, 0, key, val);
|
2019-05-08 13:08:20 +02:00
|
|
|
|
else if (ttisstring(key)) {
|
2018-10-13 16:14:33 +02:00
|
|
|
|
/* Find the previous key again */
|
2014-12-22 12:35:05 +01:00
|
|
|
|
if (ttisstring(key)) {
|
2018-10-13 16:14:33 +02:00
|
|
|
|
luaR_findentry(rotable, rawtsvalue(key), &keypos);
|
|
|
|
|
}
|
2014-12-22 12:35:05 +01:00
|
|
|
|
/* Advance to next key */
|
2018-10-13 16:14:33 +02:00
|
|
|
|
keypos ++;
|
|
|
|
|
luaR_next_helper(L, rotable, keypos, key, val);
|
2014-12-22 12:35:05 +01:00
|
|
|
|
}
|
|
|
|
|
}
|