Artifact [9ccecd9480]
Not logged in

Artifact 9ccecd94805e3779b4cbbbb97b917df02773af5f:


/* **********************************************
 * 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);