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); }