Sample TRAP Signal Handler

The sample C code below defines a TRAP signal handler fhandler_. It uses the fp_enable and fp_disable_all support routines from the Base Operating System (BOS) Runtime Services to enable or disable floating-point exceptions. The sample handler prints an error message indicating the type and location of the operation that caused the exception. You can use a load map and compiler listing to show the location and identify the source code line that generated the exception. The signal-handling code also allows the results of failing instructions to be modified to specific values.

The program myprogram.c would be compiled with the command:

xlC -c myprogram.c

and the resulting object file would be linked with other C object files produced using the flttrap option.

Note: This code is for illustrative purposes; even when support code such as this is used, the implementation of flttrap does not fully support the exception-handling environment suggested by the IEEE floating-point standard.

   /*
    * Exception handling support for use with the 'flttrap' compiler
    * option. Provides routines to enable, disable, and handle
    * exceptions. Exception handling includes the ability to
    * identify the point where an exception occurred and to continue
    * execution following an exception, possibly supplying a value
    * as the result of the failing instruction.
    *
    * Two routines are visible:
    *   enable_fp_traps_(mask)
    *   disable_fp_traps_()
    * The names contain a trailing underscore to enable their use
    * with the FORTRAN 'extname' compiler option.
    *
    * The flttrap compiler option will generate TRAP signals when
    * floating-point exceptions occur. It does so by setting the
    * record bit on all floating-point instructions, and then
    * trapping if condition register bit 5 is set (that is, if
    * the floating-point enabled exception (FEX) bit is set in
    * the floating-point status and control register).
    */
 
   #include <stdio.h>
   #include <stdlib.h>
   #include <signal.h>
   #include <fptrap.h>
   #include <fpxcp.h>
 
   /*
    * The specific trap instruction used by the flttrap option is
    * TRAP R15=R15. This is the machine code for that instruction.
    */
   #define FLTTRAPINST (0x7c8f7808)
 
   /*
    * The following table maps instruction bit patterns to the name
    * of a floating-point instruction. This table is referenced
    * using bits 26-30 of a floating-point instruction.
    */
   static char *op_table[32] = {
       "fcmp", "?", "?", "?", "?", "?", "?", "?",
       "?", "?", "?", "?", "frsp", "?", "?", "?",
       "?", "?", "fd", "?", "fs", "fa", "?", "?",
       "?", "fm", "?", "?", "fms", "fma", "fnms", "fnma" };
 
   /*
    * The following variables record the location of the failing
    * operation, the kind of operation, and the floating-point
    * registers found in a failing instruction. Note that the
    * valid registers depend on the instruction type.
    */
   static unsigned int *fpe_loc;
   static char *opcode;
   static int frt_reg, fra_reg, frb_reg, frc_reg;
 
   /* Mask value to check for floating-point exceptions. */
   #define TST_MASK (FP_INVALID|FP_OVERFLOW| \
       FP_UNDERFLOW|FP_DIV_BY_ZERO|FP_INEXACT)
 
   /* Function Prototypes */
   static int find_instr(unsigned int *trap_loc);
   void enable_fp_traps_(int *mask);
   void disable_fp_traps_();
 
   /*
    * Sample exception handler.
    * Customize this code by printing additional debugging
    * information and defining exception results.
    */
   static void fhandler_(int sig,int code,struct sigcontext *scp)
   {
       fptrap_t fpstat;
       int result_reg;
       fpstat = scp->sc_jmpbuf.jmp_context.fpscr;
 
       /* Check that the trap is of the type used for the flttrap
        * option and that the floating-point status and control
        * register indicates that an exception has occurred.
        */
 
       if (*((int *) scp->sc_jmpbuf.jmp_context.iar) != FLTTRAPINST ||
    !(fpstat & TST_MASK)) {
 
    /*
     * This must be a trap caused by an integer division by
     * zero or a subscript out of range. */
 
    fputs("SIGTRAP without floating-point exception\n",stderr);
    exit(42);
       }
 
       /*
        * Find the floating-point instruction causing the exception and
        * decode it. find_inst sets the static variables that indicate
        * the instruction location, kind, and registers.
        */
 
       if (find_instr((unsigned int *)scp->sc_jmpbuf.jmp_context.iar)) {
    fputs("SIGTRAP handler failed to find exception point\n",
          stderr);
 
    /*
     * Note that, because the exception might have occurred in a
     * subroutine that was not compiled with the flttrap option,
     * it may be desirable simply to ignore the exception by
     * clearing the exception bits and returning.
     */
 
    exit(43);
       }
 
       /* Examine the floating-point status and control register for
        * enabled exceptions. Customize each case below. */
 
       if ((fpstat & (TRP_INVALID|FP_INVALID)) ==
    (TRP_INVALID|FP_INVALID)) {
    fprintf(stderr,
     "FP invalid operation, operation '%s', location %x\n",
     opcode, fpe_loc);
 
    /*
     * Consider an invalid operation an unrecoverable error.
     * By examining other bits in the status and control register,
     * we can identify the specific invalid operation that
     * occurred (for example, zero divided by zero). Using the
     * kind of operation, we can examine the source operands.
     * If the instruction has any result registers, they
     * have not been modified.
     */
 
    exit(44);
       }
       if ((fpstat & (TRP_OVERFLOW|FP_OVERFLOW)) ==
    (TRP_OVERFLOW|FP_OVERFLOW)) {
    fprintf(stderr,"FP overflow, operation '%s', location %x\n",
     opcode, fpe_loc);
 
    /*
     * Note that the result register in an overflow contains a
     * correctly rounded normalized number, but 1536 has been
     * subtracted from the exponent.
     * Set the result of any overflow to zero.
     */
 
    scp->sc_jmpbuf.jmp_context.fpr[frt_reg] = 0.0;
       }
       if ((fpstat & (TRP_UNDERFLOW|FP_UNDERFLOW)) ==
    (TRP_UNDERFLOW|FP_UNDERFLOW)) {
    fprintf(stderr,"FP underflow, operation '%s', location %x\n",
     opcode, fpe_loc);
 
    /*
     * Note that the result register in an underflow contains a
     * correctly rounded normalized number, but 1536 has been
     * added to the exponent.
     * Set the result of any underflow to zero.
     */
 
    scp->sc_jmpbuf.jmp_context.fpr[frt_reg] = 0.0;
       }
 
       if ((fpstat & (TRP_DIV_BY_ZERO|FP_DIV_BY_ZERO)) ==
    (TRP_DIV_BY_ZERO|FP_DIV_BY_ZERO)) {
    fprintf(stderr,
     "FP division by zero, operation '%s', location %x\n",
    opcode, fpe_loc);
 
    /*
     * Print the source operands for the division; the divide
     * instruction uses FRA and FRB. Note that the result
     * register has not been modified by the divide.
     */
 
    fprintf(stderr,"  Division source operands: %f / %f\n",
     scp->sc_jmpbuf.jmp_context.fpr[fra_reg],
     scp->sc_jmpbuf.jmp_context.fpr[frb_reg]);
    /* Set the result of any division by zero to zero.    */
   scp->sc_jmpbuf.jmp_context.fpr[frt_reg] = 0.0;
       }
 
       if ((fpstat & (TRP_INEXACT|FP_INEXACT)) ==
    (TRP_INEXACT|FP_INEXACT)) {
    fprintf(stderr,"FP inexact, operation '%s', location %x\n",
     opcode, fpe_loc);
    /* No action, just ignore this. */
        }
 
       /* Reset the exception bits because they are sticky. */
       scp->sc_jmpbuf.jmp_context.fpscr &= ~FP_ALL_XCP;
 
       /* signal(SIGTRAP,fhandler_); */
 
       /* Continue execution with the instruction following the trap.*/
       scp->sc_jmpbuf.jmp_context.iar += 4;
   }
 
   /*
    * Find and decode the floating-point instruction causing the
    * exception. Return 1 if not found, else zero.
    */
 
   static int find_instr(unsigned int *trap_loc)
   {
       /*
        * Search backward in the instruction stream starting from
        * trap_loc, looking for a floating-point instruction (bits
        * 0-5 equal decimal 63). The first such instruction found
        * will be assumed to be the failing operation.
        * Note that a linear backward search assumes that there is
        * no branching code separating the trap instruction from
        * the failing floating-point operation. This will always
        * be true with the current implementation of the flttrap
        * option (in fact, in the current implementation the
        * failing operation will always be the second last
        * instruction before the trap point), except in the case
        * of subroutine calls causing an exception.
        * For safety we limit the search length.
        */
 
       int i = 0;
 
       while ((*(--trap_loc) >> 26 != 63) &&
       (++i <10)); 
       if (*trap_loc >> 26 != 63) return(1); /* no float op found */
 
       /* Check that the operation found has the record bit set. */
 
       if (!(*trap_loc & 1)) return(1); /* record bit not set */
       /*
        * Check to see if the instruction found was a move register.
        * This instruction is produced after calls to external
        * routines to see if they returned with any exception bits
        * set. Any such external routine must be a library routine
        * or in user code that was not compiled with flttrap.
        */
 
       if (((*trap_loc >> 1) & 0x3ff) == 72) return(1); /* fmr found */
       /* Decode the instruction to identify the kind of operation
        * and the source and result registers. */
 
       fpe_loc = trap_loc;
       opcode = op_table[(*trap_loc >> 1) & 0x1f];
       frt_reg = (*trap_loc >> 21) & 0x1f;
       fra_reg = (*trap_loc >> 16) & 0x1f;
       frb_reg = (*trap_loc >> 11) & 0x1f;
       frc_reg = (*trap_loc >> 6) & 0x1f;
       return(0);
   }
 
   /*
    * Install a trap handler and enable floating-point exceptions.
    * The mask parameter indicates which exceptions should be enabled
    * as follows (values are from /usr/include/fptrap.h):
    *   Invalid Operation = TRP_INVALID = 0x00000080
    *   Overflow = TRP_OVERFLOW = 0x00000040
    *   Underflow = TRP_UNDERFLOW = 0x00000020
    *   Division by Zero = TRP_DIV_BY_ZERO = 0x00000010
    *   Inexact = TRP_INEXACT = 0x00000008
    * To enable multiple exceptions, OR values together. Note that
    * the parameter is a pointer, for FORTRAN call by reference.
    */
 
   void enable_fp_traps_(int *mask)
   {
       signal(SIGTRAP,(void(*)())fhandler_);
       fp_enable(*mask);
   }
 
   /* Disable all floating-point exceptions and remove trap handler.*/
   void disable_fp_traps_()
   {
        fp_disable_all();
       signal(SIGTRAP,SIG_DFL);
   }