[comp.unix.ultrix] Switch for under- or overflow in Fortran on 3100 ???

buc@jessica.stanford.edu (Robert Richards) (10/03/90)

Is there any way to turn off the IEEE floating point over and underflow
when running a fortran program on decstation 3100 ?

Thanks.

===============================================================================
        Rob Richards				
        Supercomputer & Workstation Support Staff
        AIR					
===============================================================================

roj@dodads.dco.dec.com (Roger Masse) (10/04/90)

In article <1990Oct3.081355.19910@portia.Stanford.EDU>,
buc@jessica.stanford.edu (Robert Richards) writes:

|> Is there any way to turn off the IEEE floating point over and underflow
|> when running a fortran program on decstation 3100 ?
|> 

> There is no "built in" facility for doing this, though the hardware and
> ULTRIX support intercepting such exceptions in user code and dealing with
> it there.

> Srini Uppungunduri of Mike Greenfield's group has written a set of
> routines designed to ignore the floating point 'inexact' exception, and
> catch and "fix up" the floating point 'underflow' and integer 'divide
> by zero' exceptions (yes, these are the routines you forwarded me,
> Tom).  The user must add calls to the approriate routines to set things
> up, and link in the [C] routines, to get the behavior desired.

> In the case of floating point underflow, the exception is caught, and
> the floating point result is set to 0.0, and execution continues.

> The package of routines follows.  Instructions for their use are
> included in the comments.  Thanks, Tom (and Srini!), for them.

Regards,

        Roger Masse'
                                       
        8301 Professional Place          Digital Equipment Corporation
        Landover, Maryland, 20785        Washington ULTRIX Resource Center 
        Internet: roj@dco.dec.com        uucp: decvax!decuac!roj

        Disclaimer:  My opinions do not necessarily represent anything.



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

/************************************************************************
 *									*
 *			Copyright (c) 1989 by				*
 *		Digital Equipment Corporation, Maynard, MA		*
 *			All rights reserved.				*
 *									*
 *   This software is furnished under a license and may be used and	*
 *   copied  only  in accordance with the terms of such license and	*
 *   with the  inclusion  of  the  above  copyright  notice.   This	*
 *   software  or  any  other copies thereof may not be provided or	*
 *   otherwise made available to any other person.  No title to and	*
 *   ownership of the software is hereby transferred.			*
 *									*
 *   The information in this software is subject to change  without	*
 *   notice  and should not be construed as a commitment by Digital	*
 *   Equipment Corporation.						*
 *									*
 *   Digital assumes no responsibility for the use  or  reliability	*
 *   of its software on equipment which is not supplied by Digital.	*
 *									*
 ************************************************************************
 * intr_handl.c
 *
 * Trap and correct floating point and integer exceptions :
 * _______________________________________________________
 *
 * This file contains the following routines
 *
 * stp_int_handl : Routine to set up integer exception handler
 *                 (only handles integer divide by zero's now)
 * stp_flt_handl : Routine to set up floating point exception
 *		   handler
 * handl_flt_intr: Handler for floating point interrupts 
 * handl_int_intr: Handler for integer divide by zero exceptions
 * stp_int_handl_: F77 wrapper for stp_int_handl
 * stp_flt_handl_: F77 wrapper for stp_flt_handl 
 * rls_flt_handl : Return to default action for floating point interrupt
 *		   handling
 * rls_int_handl : Return to default action for integer interrupt
 *		   handling
 * rls_int_handl_: F77 wrapper for rls_int_handl
 * rls_flt_handl_: F77 wrapper for rls_flt_handl 
 *
 * Compiler switches:
 * _________________
 *
 * The file can be compiled with two switches -DINEXACT and -DERROR_MSG
 *
 * -DERROR_MSG: This will print a message indicating the type of excpt. 
 *		encountered every time a exception is encountered
 * -DINEXACT  : This causes the inexact exception not to be trapped --
 *		if you donot compile with this flag serious performance
 *		problems can result. Inexact operations are triggered due
 *		to several reasons
 * 
 * It is suggested that you compile as
 *	cc -c -DINEXACT -DERROR_MSG -float intr_handl.c 
 * as a first pass and then turn off error reporting
 *
 * For a FORTRAN program after linking the routines in, to set up the
 * handlers include the following statements in your source
 * 	CALL STP_FLT_HANDL()
 * 	CALL STP_INT_HANDL()
 * 
 * Srini Uppugunduri   05/09/89
 * Updated to rectify only underflows on 07/17/90
 */

#include <mips/cpu.h>
#include <mips/fpu.h>
#include <sys/sysmips.h>
#include <mips/cachectl.h>
#include <mips/inst.h>
#include <fp_class.h>
#include <signal.h>
#include <stdio.h>
extern int fp_class_f(float x); 	/* will show up in fp_class.h soon */
static unsigned long save_csr_state;    /* need this variable to  	   */
					/* save the csr state which is used*/
					/* in the rls_flt_handl routine    */
static unsigned long instr;


/* 
 * stp_int_handl() sets up the signal handler for the integer
 * divide by zero which in normal cases causes a Trace/BPT trap
 */

void stp_int_handl()
{
	void handl_int_intr();

	signal(SIGTRAP, handl_int_intr);
} 

void rls_int_handl()
{
	signal(SIGTRAP, SIG_DFL);
}

/*
 * stp_flt_handl() sets up the signal handler and enables the
 * floating point exceptions. Refer to /usr/include/mips/fpu.h
 * for a detailed description of the structures
 */

void stp_flt_handl()
{
        union fpc_csr fpc_csr;
	void handl_flt_intr();
        /*
	 * The next line sets up the signal handler routine
         */

        signal(SIGFPE, handl_flt_intr); 

	/* The next few lines set up the fpc_csr structure to trap
	 * for floating interrupts and blanks out inexact floating
         * point interrupts which are generated often and cause a 
	 * a serious performance degradation
	*/
 
        fpc_csr.fc_word = save_csr_state = get_fpc_csr(); 
	
        /* Look at the /usr/include/mips/fpu.h to understand the next 
           few lines
	*/

	fpc_csr.fc_word |= 0x00000f80; 

	/* comment out the next few lines if you prefer to trap inexact
	   floating point exceptions
	*/

	fpc_csr.fc_struct.ex_inexact = 0;
	fpc_csr.fc_struct.en_inexact = 0;
	fpc_csr.fc_struct.se_inexact = 0;

        /* The next call ensures that a SIGFPE is generated for every 
	   floating point interrupt. 
	*/

        set_fpc_csr(fpc_csr.fc_word); 
}


void rls_flt_handl()
{
        union fpc_csr fpc_csr;

	/* Return the csr to intial state - dont need to trap floating
           point interrupts any longer
	*/

	fpc_csr.fc_word = save_csr_state; 
        set_fpc_csr(fpc_csr.fc_word); 

        signal(SIGFPE, SIG_DFL); 
}




/*
 *  stp_int_handl_ and stp_flt_handl_ are the F77 wrappers for setting up 
 *  the integer and floating point interrupt handlers  
 *  rls_flt_handl_ and rls_int_handl_ are the F77 wrappers for releasing
 *  the signal handlers
 */

void stp_int_handl_()
{
        stp_int_handl();
}

void stp_flt_handl_()
{
        stp_flt_handl();
}


void rls_int_handl_()
{
        rls_int_handl();
}

void rls_flt_handl_()
{
        rls_flt_handl();
}

/*
 * stp_flt_handl() is the signal handler for SIGFPE (floating point
 * interrupts).
 * Branch-delay-set will cause a emulate branch based on the branch
 * instruction 
 */

void handl_flt_intr(sig, code, scp)
int sig, code;
struct sigcontext *scp;
{
        union fpc_csr fpc_csr;
        union fpu_instr fpu_instr;
        union fpc_irr fpc_irr;
	unsigned long branch_instruction;
	unsigned long dreg, sreg1, sreg2, fmt, func;

	fpc_csr.fc_word = scp->sc_fpc_csr;

        if     (fpc_irr.fi_struct.implementation ==IMPLEMENTATION_R2360)
            	fpu_instr.instr = scp->sc_fpc_eir;
        else
            if (scp->sc_cause & CAUSE_BD)
                fpu_instr.instr = *(unsigned long *)(scp->sc_pc + 4);
            else
                fpu_instr.instr = *(unsigned long *)(scp->sc_pc);

        instr = fpu_instr.instr;	
	/*Destination Register */
	dreg = fpu_instr.rtype.rd; 

	/*Source Register 1 */
	sreg1 = fpu_instr.rtype.rs;
	/*Source Register 2 */
	sreg2 = fpu_instr.rtype.rt;
	
#ifdef ERROR_MSG

	if (fpc_csr.fc_struct.ex_invalid)
        {
	printf("\nFloating point interrupt: Invalid Operation \n");
	}
        if (fpc_csr.fc_struct.ex_divide0)
        {
	printf("\nFloating point interrupt: Divide by Zero Operation \n");
	} 
        if (fpc_csr.fc_struct.ex_underflow)
	{
	printf("\nFloating point interrupt: Underflow Operation \n");
	}
        if (fpc_csr.fc_struct.ex_overflow)
	{
	printf("\nFloating point interrupt: Overflow Operation \n");
	}
        if (fpc_csr.fc_struct.ex_inexact)
	{
	printf("\nFloating point interrupt: Inexact Operation \n");
	}
        if (fpc_csr.fc_struct.ex_unimplemented)
	{
	printf("\nFloating point interrupt: Unimplemented Operation \n");
	}
#endif

	/*
         * Modify the saved PC so that there is no attempt to re-execute
         * the floating point instruction that caused the initial
         * exception.
         */

	if (scp->sc_cause & CAUSE_BD) {

	    branch_instruction = *(unsigned long *)(scp->sc_pc);
	/*
 	 *          Look at signal(3) for more details of this step	
	 *	    Emulate the branch instruction found on the stack 
	 */
	    emulate_branch(scp, branch_instruction);	
	}    
	else
       	    scp->sc_pc += 4;

	/* Modify values in the destination register; if this were big
	   endian we would need to modify dreg-1 instead of dreg+1 */

        if (fpc_csr.fc_struct.ex_underflow)
	{
	scp->sc_fpregs[dreg] = 0 ;
	scp->sc_fpregs[dreg+1] = 0 ;
	}

        sigreturn(scp);
}

/* Signal Handler for the integer divide by zero operation */

void handl_int_intr(sig, code, scp)
int sig, code;
struct sigcontext *scp;
{
	union fpu_instr exception_instruction;
	unsigned long branch_instruction, rsreg, rtreg;

	/* Check if the SIGTRAP was a divide by zero and 
	 * set the appropriate register to give a zero result
	 */


	if (scp->sc_cause & CAUSE_BD) {
		branch_instruction = *(unsigned long *)(scp->sc_pc);
	/*
 	 *          Look in signal(3) for details of this step	
 	 *	    emulate_branch(scp,branch_instruction);
	 */
		exception_instruction.instr = 
			*(unsigned long *)(scp->sc_pc + 4);
		emulate_branch(scp, branch_instruction);	
	}    
	else { 
	    	exception_instruction.instr = 
			*(unsigned long *)(scp->sc_pc);
       	    	scp->sc_pc += 4;
	}

	if (code == BRK_DIVZERO)
        {
#ifdef ERROR_MSG
	printf("\nInteger Interrupt : Divide By zero operation\n");
#endif
	rsreg = exception_instruction.itype.base;
	rtreg = exception_instruction.itype.rt;
	scp->sc_regs[rsreg] = 0;
	scp->sc_regs[rtreg] = 1;
	scp->sc_mdlo = 0; 
	scp->sc_mdhi = 0;
	}

	sigreturn(scp);
}


cacheflush(addr, nbytes, cache)
char *addr;
int nbytes, cache;
{
	int status;
	status = syscall(151,MIPS_CACHEFLUSH,addr,nbytes,cache);
	if (status == -1 ) return(status) ;
	else 
	return(0);
}

Regards,

        Roger Masse'
                                       
        8301 Professional Place          Digital Equipment Corporation
        Landover, Maryland, 20785        Washington ULTRIX Resource Center 
        Internet: roj@dco.dec.com        uucp: decvax!decuac!roj

        Disclaimer:  My opinions do not necessarily represent anything.