7.3. Processing a bounced VFP instruction

Top-level Undefined Instruction handler

The top-level Undefined Instruction handler TLUndef_Handler() saves the processor state, calls an appropriate second-level handler, and then restores the saved state. There are separate second-level handlers for each coprocessor and for each type of undefined instruction.

This section describes example top-level handler code provided as TLUndef_Handler() in tlundef.s.

Overview

After doing some initial state saving, TLUndef_Handler() first of all checks whether the bounced instruction was a coprocessor instruction or not.

If the instruction that bounced was not a coprocessor instruction then one of Undef_ARM_Handler(), Undef_Thumb_Handler(), or Undef_Java_Handler() is called, depending on the type of instruction that bounced. In the supplied code these are simply dummy routines that go in to an infinite loop.

It then checks which coprocessor the instruction belongs to. The coprocessor number is then used in a table lookup to find the address of a suitable second-level handler. In the case of VFP the number will be decimal 10 or 11 and the appropriate function is VFP_Undef_Handler(). By default all other coproc numbers are vectored on to Unknown_Coproc_Handler().

On return from the second-level handler the saved state is restored, and a user-level exception handler may be called. This is described in Section 7.3.3.

Initial state saving

When the ARM core signals an Undefined Instruction exception it copies the current execution state to the R14_undef and SPSR_undef registers, disables IRQ interrupts, and starts executing the code at the UNDEF vector in ARM state.

The UNDEF vector contains a jump to the top-level handler code.

The top-level handler immediately re-enables IRQ interrupts.

As a consequence interrupt handlers that wishes to use VFP must save the contents of R14_und and SPSR_und to avoid corrupting return information stored there, and possibly also save the contents of the Undef stack if it is small.

This code effectively relies on the banked register state obeying the following hierarchy:

Table 8. 

Highest priorityFiq, Abort, Irq
 Undef
 Svc
Lowest priorityUser

At any point where an exception corresponding to banked registers can occur the registers must not contain live values. Thus Undef exceptions must not be allowed when in Fiq, Abort, Irq or Undef mode.

After enabling interrupts the top-level handler switches to Supervisor mode. It constructs a complete dump of the application's register state on the Supervisor stack, and copies the return information out of R14_und and SPSR_und, switching modes where necessary.

Note

If the exception occurred in Supervisor mode the R13_svc register is modified to allocated stack space for the register dump, before it is saved in to the register dump. In this case second-level handlers may not read or modify the saved value of R13. As a consequence if VFP instructions are used by Supervisor mode code, then this handler is not suitable for building a software implementation of the VFP architecture, where instructions that copy data to and from the VFP are emulated.

The top-level handler makes temporary use of two words of Undef stack, but mostly runs in Supervisor mode with the Undef stack empty. This reduces the need for a true Undef stack, and so reduces the total amount of stack space required by the system.

The top-level handler should be modified on systems that do not use the Supervisor stack for handling Undef exceptions.

Calling a second-level handler

The saved PSR is checked to find out if the processor was in ARM or Thumb state, and the instruction that caused the exception is loaded. If Thumb, Unknown_Thumb_Handler() is called. If ARM, it is checked for a coprocessor instruction encoding, otherwise Unknown_ARM_Handler() is called.

A function Unknown_Java_Handler() is provided for exceptions in Java state (on processors conforming to ARMv5TEJ and above). However, the processor never generates Undefined Instruction exceptions due to Java execution.

For coprocessor instructions the coprocessor number is used to index in to a table of second-level handlers, TLUndef_CpTable. Unused coprocessor entries use Unknown_Coproc_Handler().

The location, initialization and use of this table might be modified for systems that support other coprocessors, or dynamically load coprocessor second-level handlers.

Suitable implementations of Unknown_ARM_Handler(), Unknown_Thumb_Handler() and Unknown_Coproc_Handler() must be provided by the operating system. These are called for illegal instructions. They usually signal a fatal exception in the offending application.

The behavior of TLUndef_Handler when the second-level handler returns is described in Section 7.3.2.

Second-level handler prototype

All handlers have this ATPCS-compliant prototype:

__value_in_regs HandlerReturnType handler_function(
    uint32 instr, ARM_RegDump *regdump)

Where:

instr is the undefined instruction, where known.

regdump points to the register dump giving the processor state when the instruction bounced, including the CPSR. The handler might modify the contents of the register dump.

struct ARM_RegDump {
    uint32 reg[16];
    uint32 cpsr;
};

HandlerReturnType describes the two return values. These are returned in registers due to the "__value_in_regs" qualifier on the function definition.

struct HandlerReturnType {
    uint32 skip_instr;
    ControlBuffer *controlbuffer;
};

The skip_instr flag (r0) is usually set, to indicate that the bounced instruction has been processed. If the skip_instr flag is clear the top-level handler returns to and retries the bounced instruction. The VFP handler uses this for handling imprecise exceptions. When an internal exception condition caused by one coprocessor instruction is signaled imprecisely, by refusing to respond to a later coprocessor instruction, the handler takes the action necessary to clear the exception condition, and then returns to the refused instruction.

The controlbuffer pointer (r1), if valid, points to a ControlBuffer describing a user-level exception. Instead of returning to the application the top-level handler continues application execution in a user-level exception handler as defined in section 7.3.3.

A NULL controlbuffer pointer indicates exception processing is complete, and a standard return to the application code can be carried out.

Second-level VFP Undefined Instruction handler

An example VFP second-level handler is VFP_Undef_Handler in vfpundef.c.

This should need no modification for systems that can process VFP bounces in a user-level exception handler. For systems that process all VFP bounces on the kernel stack, the creation of a user-level exception control buffer can be replaced with a simple call to _VFP_Computation_Engine().

Overview

VFP_Undef_Handler() first checks the VFP is enabled, and if so it calls _VFP_Is_Compute_Exception() to check if it is a legal VFP instruction. If either check fails the handler calls Undef_Coproc_Handler() to process the bounce and VFP_Undef_Handler() will complete if it returns.

At this point, the Support Code knows the instruction is one that it needs to handle. The Support Code needs to handle it in the application's context so that the applications' floating-point trap handlers can be called when necessary. It is therefore necessary to create a data structure describing the exception that can be passed to the computation engine running in the same mode as the application. The Support Code also needs to arrange for TLUndef_Handler() to return to VFP_Computation_Engine_Wrapper() rather than directly back to the application code. The Support Code calls out to GetControlBuffer to allocate the necessary space. Note that this space must be thread local.

The Support Code then calls the _VFP_Collect_Trap_Description() to copy the exception description from the VFP coprocessor into the allocated space, and to reset the VFP coprocessor's exception mechanism. The exception description is a list of operations that must be processed in software.

Checking for illegal instructions

If any of the following conditions are true when an instruction is passed to the VFP, then the VFP bounces that instruction:

  • the instruction is illegal, or

  • the EN bit in the FPEXC is clear, and the instruction is not a privileged access to an exception register, (for example FPEXC or FPSID).

  • there is a pending exception (the EX bit in the FPEXC is set) and the instruction is not a privileged access to an exception register.

  • the VFP is signaling a precise exception for the current instruction.The Support Code first checks that the VFP is enabled. If not the faulting instruction is passed on to the system's undefined coprocessor instruction handler.

Then the support code checks if the only reason for the bounce was an illegal instruction. If there is a pending exception and another reason for the bounce, then the pending exception must be dealt with first. Subsequently retrying the bounced instruction then triggers illegal handling.

The EX bit in the FPEXC signals a pending exception. If the EX bit is clear then illegal instruction bounces can be identified by checking the validity of the encoding of the instruction that bounced.

The subarchitecture support library provides a subarchitecture optimized function _VFP_Is_Compute_Exception() for recognizing illegal instruction bounces. This is described in Section 7.6.1.

If illegal handling is chosen, then VFP bounce processing is finished, and the faulting instruction is passed on to the operating system's undefined instruction handler.

Collecting exception information

When the reason for the bounce has been confirmed as a floating-point operation that needs software involvement, then the details of the bounce are collected and stored. These details include both the bounced instruction encoding and, if the EX bit is set, private exception information in the VFP.

The subarchitecture support library provides the _VFP_Collect_Trap_Description() function to gather and record this information. This is described in Section 7.6.1. This function writes a bounce description to the data block provided by the caller. The data block describes the VFP operations that must be emulated before continuing normal execution.

When the bounce details have been saved the support library clears the EX bit in the FPEXC. This function returns a flag that indicates whether or not the return address should be incremented, so that the bounced instruction is not repeated.

The VFP second-level handler returns a ControlBuffer to the top-level handler, indicating that exception handling must continue in the application context, transferring control to the VFP_Computation_Engine_Wrapper() user-level exception handler.

The top-level handler restores the user context before continuing with VFP bounce processing. This is useful if FP traps are enabled, and full application-level IEEE Floating-point trap support is required, because it enables direct calls from the _VFP_Computation_Engine() to the application's _fp_trap() exception handler.

Returning to the top-level handler

The second-level handler returns to the top-level handler, where the application's context is restored.

The second-level handler might raise a user-level exception. In this case the top-level handler returns by transferring control and parameters to a user-level exception handler, instead of returning directly to the bounced instruction.

Returning directly to the application

On return, if the controlbuffer pointer is NULL, the top level handler restores saved registers, (as modified by the second-level handler), and switches back to the application, continuing execution either by retrying the refused instruction or from the following instruction, as specified by skip_instr.

Returning via a user-level exception handler

Alternatively the second handler might forward an exception on to an exception handler operating in the application context, that uses the application stack.

The handler can then make calls to user-defined exception handlers, which can in turn use longjmp() to change execution flow. In VFP this is necessary to support user-level trap handlers.

These can return a result for a trapped operation, and because a single instruction can cause multiple exceptions, there might be multiple entries to user-level handlers for a single instruction.

The mechanism used for this is likely to be operating system dependent, so this code and the associated ControlBuffer functions might need to be replaced for your system.

The handler provided calls GetControlBuffer() to get the address of a buffer for data that is to be passed to the user-level handler. This is likely to require modification for use with a multiple process operating system.

This has the prototype:

ControlBuffer *GetControlBuffer(
    ARM_RegDump *regdump, ReturnAddress target, unsigned size)

Where:

regdump is the register dump pointer, as passed to the handler.

ReturnAddress target identifies the entry point of the user-level exception handler.

Unsigned size gives the size of the exception description data that the handler wishes to pass to the user-level handler.

ControlBufferData(controlbuffer) returns a pointer to a buffer of data_size bytes that is copied somewhere the user-level handler can read it.

The top-level handler can be modified to copy the ControlBuffer information if appropriate.

User-level exception handler prototype

The Undef handler passes control to code that receives the description data and resaves user state as necessary, and then calls an appropriate handler in an ATPCS-compliant way.

On return from the handler the wrapper code must completely restore the application state, including the flags in the CPSR, and then return to an address passed in by the top-level Undefined Instruction handler.

User-level VFP handler

The VFP user-level exception handler is _VFP_Computation_Engine_Wrapper() in "vfpwrapper.s". This code runs in the application's context.

The user-level VFP handler processes bounced operations in the application's context using the VFP computation engine software. This emulates the instructions described in the bounce description data using software floating-point, and triggers floating-point traps as appropriate.

Saving state

The VFP_Computation_Engine_Wrapper() first saves some registers and copies the ControlBuffer information on to the application's stack. This enables the use of VFP in application trap handlers, which might cause recursive bounces to the Support Code.

Calling the VFP Computation Engine

The _VFP_Computation_Engine() function is called to emulate the bounced instructions, and signal floating-point traps as appropriate. This is described in detail in Section 7.5.

As floating-point traps are encountered calls can be made to application trap handlers as specified by ANSI / IEEE Std. 754-1985, IEEE Standard for Binary Floating-Point Arithmetic. The application trap handlers can either return substitute results, or abort processing with longjmp. The Support Code uses the floating-point trap handler mechanism in the ARM C library, as described in RVDS Compiler and Libraries Guide.

Note

A single VFP instruction can generate multiple floating-point traps. This is possible only if it is made up of multiple operations, as are vector instructions and multiply-accumulate instructions.

Returning to the application

When all bounced operations have been processed the user-level exception handler restores registers and returns directly to the application, using the return address provided by the top-level handler.

Copyright © 2005. All rights reserved.DAI0133B