ftp.nice.ch/pub/next/unix/developer/pcn.2.0.s.tar.gz#/src/runtime/macros.h

This is macros.h in view mode; [Download] [Up]

/*
 * PCN Abstract Machine Emulator
 * Authors:     Steve Tuecke and Ian Foster
 *              Argonne National Laboratory
 *
 * Please see the DISCLAIMER file in the top level directory of the
 * distribution regarding the provisions under which this software
 * is distributed.
 *
 * macros.h	- Macros for accessing various structures, etc.
 */

/*
 * Some general purpose macros
 */
#if !defined(MAX)
#define MAX(A,B)	(((A) > (B)) ? (A) : (B))
#define MIN(A,B)	(((A) < (B)) ? (A) : (B))
#endif


/*
 * Some cell handling macros...
 */
#define IsRef(C)		((*((u_int_t *) (C)) & 3) == 0)
#define IsType(C,Tag)		(((data_header_t *)(C))->tag == (Tag))
#define IsUndef(C)		(((data_header_t *)(C))->tag == UNDEF_TAG)
#define IsRref(C)		(((data_header_t *)(C))->tag == RREF_TAG)
#define IsTuple(C)		(((data_header_t *)(C))->tag == TUPLE_TAG)
#define IsInt(C)		(((data_header_t *)(C))->tag == INT_TAG)
#define IsString(C)		(((data_header_t *)(C))->tag == STRING_TAG)
#define IsDouble(C)		(((data_header_t *)(C))->tag == DOUBLE_TAG)
#define IsTrailer(C)		(((data_header_t *)(C))->tag == TRAILER_TAG)
#define IsProcRecord(C)		(((data_header_t *)(C))->tag ==PROC_RECORD_TAG)
#define IsValueNote(C)		(((data_header_t *)(C))->tag == VALUE_NOTE_TAG)
#define IsStream(C)		(((data_header_t *) (C))->tag == STREAM_TAG)
#define IsUnknown(C)		(IsUndef(C) || IsRref(C))
#define SuspensionsAt(C)	(((undef_t *)(C))->suspensions != 0)
#define DHCellTag(C)		((data_header_t *)(C))->tag
#define DHCellMark(C)		((data_header_t *)(C))->mark
#define DHCellSize(C)		((data_header_t *)(C))->size
#define IsMarked(Ptr)		(((data_header_t *)(Ptr))->mark)
#define RrefNotRead(C) \
    (((rref_t *)(C))->value_return_irt == RREF_NOT_READ_IRT)


/*
 * Stuff for dealing with variable suspension queues
 */
#define LOW_PART_PTR_BITS	12
#define LOW_PART_PTR_MASK	0xFFF
#define GetHighPart(Ptr)	(((u_int_t)(Ptr)) >> LOW_PART_PTR_BITS)
#define GetLowPart(Ptr)		(((u_int_t)(Ptr)) &  LOW_PART_PTR_MASK)

/*
 * SuspendedProcs()
 *
 * Macro for retrieving the pointer to a suspension queue that is
 * hung off an undef or rref.  'C' is assumed to be a pointer
 * to an undef or rref.
 */
#define SuspendedProcs(C) \
    ((proc_record_t *) (IsUndef(C) \
       ? ( (((undef_t *)(C))->high_part_ptr << LOW_PART_PTR_BITS) \
	  | ((undef_t *)(C))->low_part_ptr ) \
       : ( (((rref_t *)(C))->high_part_ptr << LOW_PART_PTR_BITS) \
	  | ((rref_t *)(C))->low_part_ptr ) ) )

/*
 * SetSuspendedProcs()
 *
 * Macro for setting the pointer in undef or rref 'C' to the
 * suspension queue pointed to by 'Proc'.
 */
#define SetSuspendedProcs(C, Proc) \
{ \
    if (IsUndef(C)) \
    { \
	((undef_t *)(C))->high_part_ptr = GetHighPart(Proc); \
	((undef_t *)(C))->low_part_ptr = GetLowPart(Proc); \
	((undef_t *)(C))->suspensions = 1; \
    } \
    else /* IsRref(C) */ \
    { \
	((rref_t *)(C))->high_part_ptr = GetHighPart(Proc); \
	((rref_t *)(C))->low_part_ptr = GetLowPart(Proc); \
	((rref_t *)(C))->suspensions = 1; \
    } \
}


/*
 * Stuff to deal with double word alignment
 */
#ifdef PCN_ALIGN_DOUBLES

#if CELL_SIZE == 4
#define DOUBLE_WORD_MASK	0x07
#define DOUBLE_WORD_SIZE	8
#define DoubleAligned(P)	((((u_int_t)(P)) & DOUBLE_WORD_MASK) == 0)
#define OddWordDoubleAligned(P)	((((u_int_t)(P)) & DOUBLE_WORD_MASK) == 4)
#else /* CELL_SIZE == 8 */
#define DOUBLE_WORD_MASK	0x0f
#define DOUBLE_WORD_SIZE	16
#define DoubleAligned(P)	((((u_int_t)(P)) & DOUBLE_WORD_MASK) == 0)
#define OddWordDoubleAligned(P)	((((u_int_t)(P)) & DOUBLE_WORD_MASK) == 8)
#endif
#define PCN_ALIGN_DOUBLES_PAD	1

/*
 * If alignment of doubles is required, then we might need to align all
 * data structures on an odd word.  (This is so that after the
 * header word, doubles will end up double word aligned.)
 */
#define AlignDoubleOnOddWord(Type, Ptr, Pad) \
{ \
    for (Pad = 0; \
	 !OddWordDoubleAligned(Ptr); \
	 Pad++, \
	 Ptr = Type (((char *) (Ptr)) + 1)) \
	; \
}
#define AlignDoubleOnEvenWord(Type, Ptr, Pad) \
{ \
    for (Pad = 0; \
	 !DoubleAligned(Ptr); \
	 Pad++, \
	 Ptr = Type (((char *) (Ptr)) + 1)) \
	; \
}

#else  /* PCN_ALIGN_DOUBLES */

#define AlignDoubleOnOddWord(Type, Ptr, Pad)
#define AlignDoubleOnEvenWord(Type, Ptr, Pad)

#define PCN_ALIGN_DOUBLES_PAD	0

#endif /* PCN_ALIGN_DOUBLES */


/*
 * Macros to compute sizes of the various data structures, with
 * and without the trailer cell.
 *
 * StringSizeToCells() is in src/include/pcn_structs.h
 */
#define RrefSizeWithTrailer()		RrefSize
#define RrefSizeWithoutTrailer()	(RrefSizeWithTrailer() - 1)
#define UndefSizeWithTrailer()		UndefSize
#define UndefSizeWithoutTrailer()	(UndefSizeWithTrailer() - 1)

#define DoubleSizeWithoutTrailer(N)	(1 + CELLS_PER_DOUBLE*(N))
#define DoubleSizeWithTrailer(N)	(DoubleSizeWithoutTrailer(N) + 1)
#define EmptyListSizeWithoutTrailer()	1

#ifdef PCN_ALIGN_DOUBLES

#define EmptyListSizeWithTrailer()	2
#define TupleSizeWithoutTrailer(N)	(1 + (N) + ((N)%2))
#define TupleSizeWithTrailer(N)		(TupleSizeWithoutTrailer(N) + 1)
#define IntSizeWithoutTrailer(N)	(1 + (N) + ((N)%2))
#define StringSizeWithoutTrailer(N)	(1 + StringSizeToCells(N) \
					 + (StringSizeToCells(N)%2))

#else /* PCN_ALIGN_DOUBLES */

#define EmptyListSizeWithTrailer()	1
#define TupleSizeWithoutTrailer(N)	(1 + (N))
#define TupleSizeWithTrailer(N)		(N == 0 ? (1) : \
					 (TupleSizeWithoutTrailer(N) + 1))
#define IntSizeWithoutTrailer(N)	(1 + (N))
#define StringSizeWithoutTrailer(N)	(1 + StringSizeToCells(N))

#endif /* PCN_ALIGN_DOUBLES */

#define IntSizeWithTrailer(N)		(IntSizeWithoutTrailer(N) + 1)
#define StringSizeWithTrailer(N)	(StringSizeWithoutTrailer(N) + 1)


/*
 * Dereference()
 *
 * Fill New with the dereferenced cell_t* of Old.
 */
#define Dereference(Type, Old, New) \
/* \
cell_t *Old; \
Type New; \
*/ \
{ \
    New = Type (Old); \
    while(IsRef(New)) \
	New = Type (*((cell_t *) (New))); \
}


/*
 * Garbage collection handling macros
 */
#define NeedToGC()		(_p_heap_ptr >= _p_heap_gc_top)
#define NeedToGCWithSize(Size) \
    ( ((_p_heap_ptr + (Size)) > _p_heap_hard_top) /* normal case */ \
     || ((_p_heap_ptr  + (Size)) < _p_heap_ptr) ) /* wraparound case */

#define DoGC()			_p_collect_garbage(_p_gc_slack)
#define DoGCWithSize(Size)	_p_collect_garbage(Size)

#define TryGC()		if (NeedToGC()) DoGC();
#define TryGCWithSize(Size) \
{ \
    int_t __TryGCWithSize_Size = (Size); \
    if (NeedToGCWithSize(__TryGCWithSize_Size)) \
    { \
	DoGCWithSize(__TryGCWithSize_Size); \
    } \
}

#define CalcHeapTops() \
{ \
    _p_heap_hard_top = _p_heap_cancel_top - CANCEL_SIZE; \
    _p_heap_gc_top = _p_heap_hard_top - _p_gc_slack; \
}

#define PushGCReference(TheGCRef) \
    _p_gc_ref_stack[_p_gc_ref_stack_top++] = (TheGCRef);
#define PopGCReference(NumToPop)	_p_gc_ref_stack_top -= (NumToPop);


/*
 * Macros used mostly by the garbage pcollector, but occasionally by
 * debugging code as well...
 */
#define OnHeap(Ptr)  ((_p_heap_bottom <= (Ptr)) && ((Ptr) <= _p_heap_hard_top))
#define OffHeap(Ptr) (((Ptr) < _p_heap_bottom) || (_p_heap_hard_top < (Ptr)))

#if CELL_SIZE == 8
#define PTR_REVERSED_MASK	0xFFFFFFFFFFFFFFFD
#else
#define PTR_REVERSED_MASK	0xFFFFFFFD
#endif
#define ClearReversed(Ptr) ((cell_t *) (((u_int_t) (Ptr)) & PTR_REVERSED_MASK))
#define SetReversed(Ptr)	((cell_t *) (((u_int_t) (Ptr)) | 2))
#define IsReversed(Ptr)		((*((u_int_t *) (Ptr)) & 3) == 2)

#define IsGCRef(Ptr)	((*((u_int_t) (Ptr)) & 1) == 0)

#define ReversePointer(SOURCE, TARGET) \
{ \
    *((cell_t **) (SOURCE)) = *((cell_t **) (TARGET)); \
    *((cell_t **) (TARGET)) = SetReversed(SOURCE); \
}


/*
 * Message handling macros
 */
#ifdef PARALLEL

#if ASYNC_MSG == 1
#define TryMSG()	if (_p_multiprocessing && _p_msg_avail) \
				_p_process_messages(RCV_NOBLOCK);
#define TryMSGNoSkip()	TryMSG()
#else  /* ASYNC_MSG */
#define TryMSG() \
if (_p_multiprocessing && _p_msg_skip-- <= 0) \
{ \
    _p_process_messages(RCV_NOBLOCK); \
    _p_msg_skip = MSG_SKIP_POLL; \
}
#define TryMSGNoSkip() \
    if (_p_multiprocessing) _p_process_messages(RCV_NOBLOCK);
#endif /* ASYNC_MSG */

#else /* PARALLEL */

#define TryMSG()
#define TryMSGNoSkip()

#endif /* PARALLEL */


/*
 * HeaderCell()
 *
 * Build a header cell
 */
#define HeaderCell(C,Tag,Size) \
{ \
    ((data_header_t *) (C))->tag = (Tag); \
    ((data_header_t *) (C))->mark = 0; \
    ((data_header_t *) (C))->size = (Size); \
}
#define TrailerCell(C,Offset)	HeaderCell(C,TRAILER_TAG,Offset)


/*
 * BuildUndef()
 *
 * Build an Undef on the heap and place a reference to it (with
 *   a Ref tag) into the passed cell.
 *
 * GC_ALERT: This macro may induce garbage collection
 */
#define BuildUndefNoGC(Type, C) \
/* \
Type C; \
*/ \
{ \
    C = Type _p_heap_ptr; \
    ((undef_t *) (_p_heap_ptr))->tag = UNDEF_TAG; \
    ((undef_t *) (_p_heap_ptr))->mark = 0; \
    ((undef_t *) (_p_heap_ptr))->suspensions = 0; \
    ((undef_t *) (_p_heap_ptr))->trailer_tag = UNDEF_TRAILER_TAG; \
    _p_heap_ptr += UndefSizeWithTrailer(); \
}
#define BuildUndef(Type, C) \
/* \
Type C; \
*/ \
{ \
    TryGCWithSize(UndefSizeWithTrailer()); \
    BuildUndefNoGC(Type, C); \
}


/*
 * BuildRref()
 *
 * Build a remote reference on the heap using the passwd Weight,
 *   Location, and Node.  Place a reference to it (with a remote
 *   reference tag) into the passed cell.
 *
 * GC_ALERT: This macro may induce garbage collection
 */
#define BuildRref(Type, C, Weight, Location, Node) \
/* \
Type C; \
u_int_t Weight; \
u_int_t Location; \
u_int_t Node; \
*/ \
{ \
    TryGCWithSize(RrefSizeWithTrailer()); \
    HeaderCell(_p_heap_ptr, RREF_TAG, 0); \
    C = Type _p_heap_ptr; \
    ((rref_t *) (_p_heap_ptr))->tag = RREF_TAG; \
    ((rref_t *) (_p_heap_ptr))->mark = 0; \
    ((rref_t *) (_p_heap_ptr))->suspensions = 0; \
    ((rref_t *) (_p_heap_ptr))->node = (Node); \
    ((rref_t *) (_p_heap_ptr))->location = (Location); \
    ((rref_t *) (_p_heap_ptr))->weight = (Weight); \
    ((rref_t *) (_p_heap_ptr))->value_return_irt = RREF_NOT_READ_IRT; \
    ((rref_t *) (_p_heap_ptr))->trailer_tag = RREF_TRAILER_TAG; \
    _p_heap_ptr += RrefSizeWithTrailer(); \
}

/*
 * BuildString()
 *
 * Build an string on the heap and place a pointer to it
 *   into the passed cell pointer.  The length should *not*
 *   include the null termination.  (Thus, strlen() will give the
 *   correct length.)
 *
 * GC_ALERT: This macro may induce garbage collection
 */
#define BuildString(Type, C, Str, Len) \
/* \
Type C; \
char_t *Str; \
int_t Len; \
*/ \
{ \
    int_t __BuildString_Len = (Len) + 1; \
    int_t __BuildString_Size = StringSizeWithoutTrailer(__BuildString_Len); \
    TryGCWithSize(StringSizeWithTrailer(__BuildString_Len)); \
    HeaderCell(_p_heap_ptr, STRING_TAG, __BuildString_Len); \
    C = Type _p_heap_ptr; \
    memcpy((_p_heap_ptr + 1), Str, __BuildString_Len); \
    _p_heap_ptr += __BuildString_Size; \
    TrailerCell(_p_heap_ptr, __BuildString_Size); \
    _p_heap_ptr++; \
}

/*
 * BuildInt()
 *
 * Build an integer on the heap and place a pointer to it
 *   into the passed cell pointer.
 *
 * GC_ALERT: This macro may induce garbage collection
 */
#define BuildInt(Type, C, Int) \
/* \
Type C; \
int_t Int; \
*/ \
{ \
    int_t __BuildInt_Size = IntSizeWithoutTrailer(1); \
    TryGCWithSize(IntSizeWithTrailer(1)); \
    HeaderCell(_p_heap_ptr, INT_TAG, 1); \
    C = Type _p_heap_ptr; \
    *((int_t *) (_p_heap_ptr + 1)) = (Int); \
    _p_heap_ptr += __BuildInt_Size; \
    TrailerCell(_p_heap_ptr, __BuildInt_Size); \
    _p_heap_ptr++; \
}

/*
 * BuildDouble()
 *
 * Build an double on the heap and place a pointer to it
 *   into the passed cell pointer.
 *
 * GC_ALERT: This macro may induce garbage collection
 */
#define BuildDouble(Type, C, D) \
/* \
Type C; \
double_t D; \
*/ \
{ \
    int_t __BuildDouble_Size = DoubleSizeWithoutTrailer(1); \
    TryGCWithSize(DoubleSizeWithTrailer(1)); \
    HeaderCell(_p_heap_ptr, DOUBLE_TAG, 1); \
    C = Type _p_heap_ptr; \
    *((double_t *) (_p_heap_ptr + 1)) = (D); \
    _p_heap_ptr += __BuildDouble_Size; \
    TrailerCell(_p_heap_ptr, __BuildDouble_Size); \
    _p_heap_ptr++; \
}


/*
 * BuildTuple()
 *
 * Build a tuple on the heap and place a
 *   pointer to it into the passed cell pointer.
 *   Does not put anything into the tuple's arguments.
 *   Rather, _p_structure_ptr is set to the first argument, and
 *   can be used to fill in the tuple.
 *
 * GC_ALERT: This macro may induce garbage collection
 */
#ifdef PCN_ALIGN_DOUBLES
#define BuildTupleTrailer(Arity) \
    TrailerCell(_p_heap_ptr, __BuildTuple_Size); \
    _p_heap_ptr++;
#else  /* PCN_ALIGN_DOUBLES */
#define BuildTupleTrailer(Arity) \
if ((Arity) > 0) \
{ \
    TrailerCell(_p_heap_ptr, __BuildTuple_Size); \
    _p_heap_ptr++; \
}
#endif /* PCN_ALIGN_DOUBLES */
#define BuildTuple(Type, C, Arity) \
/* \
Type C; \
int_t Arity; \
*/ \
{ \
    int_t __BuildTuple_Size = TupleSizeWithoutTrailer(Arity); \
    TryGCWithSize(TupleSizeWithTrailer(Arity)); \
    HeaderCell(_p_heap_ptr, TUPLE_TAG, Arity); \
    C = Type _p_heap_ptr; \
    _p_structure_start_ptr = _p_heap_ptr; \
    _p_structure_ptr = (_p_heap_ptr + 1); \
    _p_heap_ptr += __BuildTuple_Size; \
    BuildTupleTrailer(Arity); \
}


/*
 * BuildEmptyList()
 *
 * Build an empty list (a tuple with arity 0) on the heap and place a
 *   pointer to it into the passed cell (pointer).
 *
 * GC_ALERT: This macro may induce garbage collection
 */
#ifdef PCN_ALIGN_DOUBLES
#define BuildEmptyList(Type, C) \
/* \
Type C; \
*/ \
{ \
    TryGCWithSize(EmptyListSizeWithTrailer()); \
    HeaderCell(_p_heap_ptr, TUPLE_TAG, 0); \
    C = Type _p_heap_ptr++; \
    TrailerCell(_p_heap_ptr, 1); \
    _p_heap_ptr++; \
}
#else  /* PCN_ALIGN_DOUBLES */
#define BuildEmptyList(Type, C) \
/* \
Type C; \
*/ \
{ \
    TryGCWithSize(EmptyListSizeWithTrailer()); \
    HeaderCell(_p_heap_ptr, TUPLE_TAG, 0); \
    C = Type _p_heap_ptr++; \
}
#endif /* PCN_ALIGN_DOUBLES */


#ifdef PDB
#define PDB_EnqueueProcess(Proc)	_pdb_enqueue_process(Proc)
#define PDB_DequeueProcess(Proc)	_pdb_dequeue_process(Proc)
#else  /* PDB */
#define PDB_EnqueueProcess(Proc)
#define PDB_DequeueProcess(Proc)
#endif /* PDB */


/*
 * EnqueueProcess()
 *
 * Enqueue the passed process (Proc) onto the passed queue (QF,QB).
 */
#define EnqueueProcess(Proc, QF, QB) \
/* \
proc_record_t *Proc, *QF, *QB; \
*/ \
{ \
    if (QF == (proc_record_t *) NULL) \
    { \
	QF = QB = Proc; \
	(Proc)->next = (proc_record_t *) NULL; \
    } \
    else \
    { \
	(Proc)->next = (proc_record_t *) NULL; \
	(QB)->next = Proc; \
	QB = Proc; \
    } \
}


/*
 * EnqueueProcessAtFront()
 *
 * Enqueue the passed process (Proc) onto the front of the
 * passed queue (QF,QB).
 */
#define EnqueueProcessAtFront(Proc, QF, QB) \
/* \
proc_record_t *Proc, *QF, *QB; \
*/ \
{ \
    if (QF == (proc_record_t *) NULL) \
    { \
	QF = QB = Proc; \
	(Proc)->next = (proc_record_t *) NULL; \
    } \
    else \
    { \
	(Proc)->next = (proc_record_t *) QF; \
	QF = Proc; \
    } \
}


/*
 * DequeueProcess()
 *
 * Dequeue the first process from the passed queue (QF,QB), and place
 *   it in the passed process (Proc).
 */
#define DequeueProcess(Proc, QF, QB) \
/* \
proc_record_t *Proc, *QF, *QB; \
*/ \
{ \
    Proc = QF; \
    if (QF == QB) \
	QF = QB = (proc_record_t *) NULL; \
    else \
	QF = (QF)->next; \
}


/*
 * AppendProcessQueue()
 *
 * Append queue 2 (Q2F,Q2B) to the end of queue 1 (Q1F,Q1B).
 */
#define AppendProcessQueue(Q1F, Q1B, Q2F, Q2B) \
/* \
proc_record_t *Q1F, *Q1B, *Q2F, *Q2B; \
*/ \
{ \
    if (Q1F == (proc_record_t *) NULL) \
    { \
	Q1F = Q2F; \
	Q1B = Q2B; \
    } \
    else \
    { \
	(Q1B)->next = Q2F; \
	Q1B = Q2B; \
    } \
    Q2F = Q2B = (proc_record_t *) NULL; \
}
#define ProcessQueueIsEmpty(QF, QB)	QF == (proc_record_t *) NULL

    
/*
 * EnqueueSuspension()
 *
 * Enqueue the passed process onto the suspension queue for the
 *   passed variable (Undef or Rref).
 *
 * Suspension queues are circular queues, to facilitate easy appending
 *   to the active queue.
 */
#define EnqueueSuspension(Proc, C) \
/* \
proc_record_t *Proc; \
cell_t *Cp; \
*/ \
{ \
    if (SuspensionsAt(C)) \
    { \
	proc_record_t *__CP = SuspendedProcs(C); \
	(Proc)->next = (__CP)->next; \
	(__CP)->next = Proc; \
    } \
    else \
    { \
	SetSuspendedProcs(C, Proc); \
	(Proc)->next = Proc; \
    } \
}


#define IncrementCounter(Offset, I)	*((int_t *) (Offset)) += (I);
#define Fail() \
{ \
    program_counter = failure_label; \
    break; \
}


/*
 * SuspendOn()
 *
 * Note: SuspendOn is meant to break out of the emulator's switch.
 *   Therefore, SuspendOn should never be used inside a loop in the
 *   emulator.
 */
#ifdef PARALLEL
#define SuspendOn(C) \
/* \
cell_t *C; \
*/ \
{ \
    if ( IsRref(C) && RrefNotRead(C)) \
	_p_send_read(C); \
    if (_p_suspension_var == (cell_t *) NULL) \
	_p_suspension_var = C; \
    else if (_p_suspension_var != C) \
	_p_suspension_var = (cell_t *) -1; \
    program_counter = failure_label; \
    break; \
}
#else  /* PARALLEL */
/* \
cell_t *C; \
*/ \
#define SuspendOn(C) \
{ \
    if (_p_suspension_var == (cell_t *) NULL) \
	_p_suspension_var = C; \
    else if (_p_suspension_var != C) \
	_p_suspension_var = (cell_t *) -1; \
    program_counter = failure_label; \
    break; \
}
#endif /* PARALLEL */

/*
 * DerefAndCheckSusp()
 *
 * Dereference the Old pointer, and suspend
 *   if it dereferences to an Unknown.
 */
#define DerefAndCheckSusp(Type, Old, New) \
/* \
cell_t *Old; \
Type New; \
*/ \
{ \
    Dereference(Type, Old, New); \
    if (IsUnknown(New)) \
    { \
	SuspendOn(New); \
    } \
}


/*
 * Arith()
 *
 * Macro for doing arithmatic operations.  Supply your favorite operator.
 *   It builds room on the heap for the result.
 *   This is meant to be used by emulate.c.
 */
#define Arith(Op) \
{ \
    cp1 = _p_a_reg[instr->I_ARITH_LSRC_R]; \
    cp2 = _p_a_reg[instr->I_ARITH_RSRC_R]; \
    if (IsInt(cp1) && IsInt(cp2)) \
    { \
	i1 = *((int_t *) (cp1 + 1)); \
	i2 = *((int_t *) (cp2 + 1)); \
	BuildInt((cell_t *), _p_a_reg[instr->I_ARITH_DEST_R], (i1 Op i2)); \
    } \
    else \
    { \
	if (IsDouble(cp1)) \
	    d1 = *((double_t *) (cp1 + 1)); \
	else if (IsInt(cp1)) \
	    d1 = (double_t) *((int_t *) (cp1 + 1)); \
        else \
	{ \
	    d1 = 1.0; \
	    fprintf(_p_stdout, \
		    "(%lu,%lu) Warning: arithmetic operation on non-numeric value in %s:%s\n", \
		    (unsigned long) _p_my_id, (unsigned long) _p_reduction, \
		    _p_current_proc->proc->module_name, \
		    _p_current_proc->proc->proc_name); \
	} \
	if (IsDouble(cp2)) \
	    d2 = *((double_t *) (cp2 + 1)); \
	else if (IsInt(cp2)) \
	    d2 = (double_t) *((int_t *) (cp2 + 1)); \
        else \
	{ \
	    d2 = 1.0; \
	    fprintf(_p_stdout, \
		    "(%lu,%lu) Warning: arithmetic operation on non-numeric value in %s:%s\n", \
		    (unsigned long) _p_my_id, (unsigned long) _p_reduction, \
		    _p_current_proc->proc->module_name, \
		    _p_current_proc->proc->proc_name); \
	} \
	BuildDouble((cell_t *), _p_a_reg[instr->I_ARITH_DEST_R], \
		    (double_t) (d1 Op d2)); \
    } \
}

/*
 * Compare()
 *
 * Macro for doing comparison operations.  Supply your favorite operator.
 *   It causes a Fail() if the comparison is false.
 *   This is meant to be used by emulate.c.
 */
#define Compare(Op) \
{ \
    DerefAndCheckSusp((cell_t *), _p_a_reg[instr->I_COMPARE_LSRC_R], cp1); \
    DerefAndCheckSusp((cell_t *), _p_a_reg[instr->I_COMPARE_RSRC_R], cp2); \
    _p_a_reg[instr->I_COMPARE_LSRC_R] = cp1; \
    _p_a_reg[instr->I_COMPARE_RSRC_R] = cp2; \
    if (IsInt(cp1) && IsInt(cp2)) \
    { \
	if (!(*((int_t *) (cp1 + 1)) Op *((int_t *) (cp2 + 1)))) \
	{ \
	    Fail(); \
	} \
    } \
    else if (IsDouble(cp1) && IsDouble(cp2)) \
    { \
	if (!(*((double_t *) (cp1 + 1)) Op *((double_t *) (cp2 + 1)))) \
	{ \
	    Fail(); \
	} \
    } \
    else if (IsInt(cp1) && IsDouble(cp2)) \
    { \
	if (!(*((int_t *) (cp1 + 1)) Op *((double_t *) (cp2 + 1)))) \
	{ \
	    Fail(); \
	} \
    } \
    else if (IsDouble(cp1) && IsInt(cp2)) \
    { \
	if (!(*((double_t *) (cp1 + 1)) Op *((int_t *) (cp2 + 1)))) \
	{ \
	    Fail(); \
	} \
    } \
    else \
    { \
	fprintf(_p_stdout, \
		"(%lu,%lu) Warning: comparison operation on non-numeric value in %s:%s\n", \
		(unsigned long) _p_my_id, (unsigned long) _p_reduction, \
		_p_current_proc->proc->module_name, \
		_p_current_proc->proc->proc_name); \
	Fail(); \
    } \
}

/* Arguments for foreign function calls */
#define F_REG_1		_p_f_reg[0]
#define F_REG_2		F_REG_1,_p_f_reg[1]
#define F_REG_4		F_REG_2,_p_f_reg[2],_p_f_reg[3]
#define F_REG_8		F_REG_4,_p_f_reg[4],_p_f_reg[5],_p_f_reg[6],_p_f_reg[7]
#define F_REG_16	F_REG_8,_p_f_reg[8],_p_f_reg[9],_p_f_reg[10],\
    _p_f_reg[11],_p_f_reg[12],_p_f_reg[13],_p_f_reg[14],_p_f_reg[15]
#define F_REG_32	F_REG_16,_p_f_reg[16],_p_f_reg[17],_p_f_reg[18],\
    _p_f_reg[19],_p_f_reg[20],_p_f_reg[21],_p_f_reg[22],_p_f_reg[23],\
    _p_f_reg[24],_p_f_reg[25],_p_f_reg[26],_p_f_reg[27],_p_f_reg[28],\
    _p_f_reg[29],_p_f_reg[30],_p_f_reg[31]
#define F_REG_64	F_REG_32,_p_f_reg[32],_p_f_reg[33],_p_f_reg[34],\
    _p_f_reg[35],_p_f_reg[36],_p_f_reg[37],_p_f_reg[38],_p_f_reg[39],\
    _p_f_reg[40],_p_f_reg[41],_p_f_reg[42],_p_f_reg[43],_p_f_reg[44],\
    _p_f_reg[45],_p_f_reg[46],_p_f_reg[47],_p_f_reg[48],_p_f_reg[49],\
    _p_f_reg[50],_p_f_reg[51],_p_f_reg[52],_p_f_reg[53],_p_f_reg[54],\
    _p_f_reg[55],_p_f_reg[56],_p_f_reg[57],_p_f_reg[58],_p_f_reg[59],\
    _p_f_reg[60],_p_f_reg[61],_p_f_reg[62],_p_f_reg[63]


#define IrtIndex(I)	(((irt_t *) (I)) - _p_irt)
#define IrtAddress(N)	(_p_irt + (N))


#ifdef DEBUG
#define GlobalDebug(Level)	\
    ((_p_global_dl >= (Level)) && (_p_reduction >= _p_start_em_debug))
#define EmDebug(Level)	\
    ((_p_em_dl >= (Level)) && (_p_reduction >= _p_start_em_debug))
#define GCDebug(Level)	\
    ((_p_gc_dl >= (Level)) && (_p_reduction >= _p_start_em_debug))
#define ParDebug(Level)	\
    ((_p_par_dl >= (Level)) && (_p_reduction >= _p_start_em_debug))
#endif /* DEBUG */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Marcel Waldvogel and Netfuture.ch.