/* **********************************************
* NRE internals
* **********************************************
*/
#define NRE_STACK_SIZE 100
/*
* This is the main data struct for representing NR commands. It was
* originally designed to fit in sizeof(Tcl_Obj) in order to exploit the
* fastest memory allocator available. The current version completely changed
* the memory management approach (stack vs linked list), and the struct was
* reduced in size to make it cache friendlier (two callbacks fit in one cache
* line).
*/
typedef struct NRE_callback {
Tcl_NRPostProc *procPtr;
ClientData data[3];
} NRE_callback;
/*
* - NOTES ON ALIGNMENT -
*
* . Cache lines are 64B long; callbacks are 32B on x86_64 and 16B on x32. In
* order to insure that no callback ever strides cache line boundaries, we
* require the first callback to be aligned to 32b (on x32, 16B would be
* enough).
* . we use a gcc'ism to insure alignment, portability not being the priority
* right now.
*/
typedef struct NRE_stack {
struct NRE_callback items[NRE_STACK_SIZE];
struct NRE_stack *next;
} NRE_stack __attribute__ ((aligned (32)));
#define NRE_newExtra(ptr) \
TclSmallAlloc(5*sizeof(ClientData), ptr)
#define NRE_freeExtra(ptr) TclSmallFree(ptr)
/*
* Inline versions of Tcl_NRAddCallback and friends
*/
#define TOP_CB(iPtr) (((Interp *)(iPtr))->execEnvPtr->callbackPtr)
#define TclNRAddCallback(interp,postProcPtr,data0,data1,data2) \
do { \
NRE_callback *cbPtr; \
ALLOC_CB(interp, cbPtr); \
INIT_CB(cbPtr, postProcPtr,data0,data1,data2); \
} while (0)
#define INIT_CB(cbPtr, postProcPtr,data0,data1,data2) \
do { \
cbPtr->procPtr = (postProcPtr); \
cbPtr->data[0] = (ClientData)(data0); \
cbPtr->data[1] = (ClientData)(data1); \
cbPtr->data[2] = (ClientData)(data2); \
} while (0)
#define POP_CB(interp, cbPtr) \
(cbPtr) = TOP_CB(interp)++
#define ALLOC_CB(interp, cbPtr) \
do { \
ExecEnv *eePtr = ((Interp *) interp)->execEnvPtr; \
NRE_stack *this = eePtr->NRStack; \
\
if (eePtr->callbackPtr && \
(eePtr->callbackPtr > &this->items[0])) { \
(cbPtr) = --eePtr->callbackPtr; \
} else { \
(cbPtr) = TclNewCallback(interp); \
} \
} while (0)
#define NEXT_CB(ptr) TclNextCallback(ptr)
#define NRE_TRAMPOLINE 0
#if NRE_TRAMPOLINE
#define NRE_JUMP(interp,postProcPtr,data0,data1,data2) \
TclNRAddCallback((interp),(postProcPtr),(data0),(data1),(data2)); \
NRE_NEXT(TCL_OK)
#define NRE_NEXT(result) \
return (result)
#else
/* no trampoline, optimized sibcalls */
#define NRE_JUMP(interp,postProcPtr,data0,data1,data2) \
TclNRAddCallback((interp),(postProcPtr),(data0),(data1),(data2)); \
NRE_NEXT(TCL_OK)
#define NRE_NEXT(result) \
do { /* optimized indirect sibling calls?! */ \
NRE_callback *cbPtr; \
POP_CB(interp, cbPtr); \
return (cbPtr->procPtr)(cbPtr->data, interp, (result)); \
} while (0)
#endif
MODULE_SCOPE TCL_NOINLINE NRE_callback *TclNewCallback(Tcl_Interp *interp);
MODULE_SCOPE TCL_NOINLINE NRE_callback *TclPopCallback(Tcl_Interp *interp);
MODULE_SCOPE TCL_NOINLINE NRE_callback *TclNextCallback(NRE_callback *ptr);