/*
 * 5799-WZQ (C) COPYRIGHT = NONE
 * LICENSED MATERIALS - PROPERTY OF IBM
 */
/*
 * Copyright (c) 1983 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

static char rcsid[] = "$Header:calls_ca.c 12.0$";
#ifndef lint
    static	char *sccsid = "@(#)calls.c	1.4 (Berkeley) 3/30/83";
#endif not lint

#include	"gprof.h"


    /*
     *	a namelist entry to be the child of indirect calls
     */
nltype	indirectchild = {
	"(*)" ,				/* the name */
	(unsigned long) 0 ,		/* the pc entry point */
	(unsigned long) 0 ,		/* entry point aligned to histogram */
	(double) 0.0 ,			/* ticks in this routine */
	(double) 0.0 ,			/* cumulative ticks in children */
	(long) 0 ,			/* how many times called */
	(long) 0 ,			/* how many calls to self */
	(double) 1.0 ,			/* propagation fraction */
	(double) 0.0 ,			/* self propagation time */
	(double) 0.0 ,			/* child propagation time */
	(bool) 0 ,			/* print flag */
	(int) 0 ,			/* index in the graph list */
	(int) 0 , 			/* graph call chain top-sort order */
	(int) 0 ,			/* internal number of cycle on */
	(struct nl *) &indirectchild ,	/* pointer to head of cycle */
	(struct nl *) 0 ,		/* pointer to next member of cycle */
	(arctype *) 0 ,			/* list of caller arcs */
	(arctype *) 0 			/* list of callee arcs */
    };

#define CAL16	0xc2
#define OIU	0xc3

findcalls( parentp , p_lowpc , p_highpc )
    nltype		*parentp;
    unsigned long	p_lowpc;
    unsigned long	p_highpc;
{
    register unsigned char	*instructp, *p;
    long		length;
    nltype		*childp;
    register unsigned long	destpc;

    if ( textspace == 0 ) {
	return;
    }
    if ( p_lowpc < s_lowpc ) {
	p_lowpc = s_lowpc;
    }
    if ( p_highpc > s_highpc ) {
	p_highpc = s_highpc;
    }
#   ifdef DEBUG
	if ( debug & CALLSDEBUG ) {
	    printf( "[findcalls] %s: 0x%x to 0x%x\n" ,
		    parentp -> name , p_lowpc , p_highpc );
	}
#   endif DEBUG
    for (   instructp = textspace + p_lowpc ;
	    instructp < textspace + p_highpc ;
	    instructp += length ) {
	length = 2;
	if ( *instructp == BALR && (instructp[1]&0xf0) == 0xf0) {
		/*
		 *	its a balr, better check it out.
		 *	the current calling sequence is:
		 *	cal16 rx,low-order 16 bits
		 *	oiu   rx,high-order 16 bits
		 *	balr	r15,rx
		 */
#	    ifdef DEBUG
		if ( debug & CALLSDEBUG ) {
		    printf( "[findcalls]\t0x%x:calls" , instructp - textspace );
		}
#	    endif DEBUG
		p = instructp - 8;
		if (p < textspace + p_lowpc)
			continue;		/* could not be it */
		if (p[0] == CAL16 && p[4] == OIU) {	/* normal call */
			destpc = ((unsigned short *) p)[1] | (((unsigned short *) p)[3] << 16);
		    if ( destpc >= s_lowpc && destpc <= s_highpc ) {
			childp = nllookup( destpc );
#			ifdef DEBUG
			    if ( debug & CALLSDEBUG ) {
				printf( "[findcalls]\tdestpc 0x%x" , destpc );
				printf( " childp->name %s" , childp -> name );
				printf( " childp->value 0x%x\n" ,
					childp -> value );
			    }
#			endif DEBUG
			if ( childp -> value == destpc ) {
				/*
				 *	a hit
				 */
			    addarc( parentp , childp , (long) 0 );
			    continue;
			}
			botched:
				/*
				 *	something funny going on.
				 */
#			    ifdef DEBUG
				if ( debug & CALLSDEBUG ) {
				    printf( "[findcalls]\tbut it's a botch\n" );
				}
#			    endif DEBUG
			    length = 1;
			    continue;
		    }
		}
		else {
			
			
			/*
			 *	indirect call: call through pointer
			 *	either	*d(r)	as a parameter or local
			 *		(r)	as a return value
			 *		*f	as a global pointer
			 *	[are there others that we miss?,
			 *	 e.g. arrays of pointers to functions???]
			 */
		    addarc( parentp , &indirectchild , (long) 0 );
		    continue;
		    }
	    }
	}
}
