
.
.     APD assembly modules which intercept procedures between caller
.     and callee.
.
.
PMM$INTERCEPT_PROCEDURES      IDENT
.
               def            pmp$initial_intercept_procedure
               def            pmp$intercept_call_procedure
               def            record_trapped_block_exit
               def            clean_up_condition_handler
               def            pmp$apd_call_to_users_procedure
               defg           pmp$simulate_call_overhead
               defg           pmp$simulate_return_overhead
.
.
.     external cybil procedure references
.
               ref            osv$default_block_exit_desc
               ref            pmp$open_new_interblock_segment
               ref            pmp$get_apd_task_jobmode_stats
               ref            process_block_exits
               ref            process_exit_from_apd
.
.
.     set up binding section for external procedures
.
.
                    use               binding
.
ptr_block_exit_desc address           p,osv$default_block_exit_desc
ptr_open_new_seg    address           ce,pmp$open_new_interblock_segment
ptr_get_job_stats   address           ce,pmp$get_apd_task_jobmode_stats
ptr_cleanup_handler address           ce,clean_up_condition_handler
ptr_cond_handler    address           ce,record_trapped_block_exit
ptr_proc_block_exit address           ce,process_block_exits
ptr_proc_exit_apd   address           ce,process_exit_from_apd
.
.
                    use               #lastsec
.
.
word_size                       equ       8
est_handler_size                equ      62     . size of pmt$established_handler
interblock_reference_size       set      30     . size of pmt$interblock_reference
.
.     constants for offsets into pmt$loader_seq_descriptor
.
last_interblock_seg_offset      set      95     . .last_interblock_segment offset
accum_intercept_time_offset     set     111     . .accumulated_intercept_time offset
no_of_calls_offset              set     121     . .number_of_intercepted_calls offset
no_of_returns_offset            set     125     . .number_of_intercepted_returns offset
accum_call_time_offset          set     129     . .accum_intercept_call_time offset
accum_return_time_offset        set     135     . .accum_intercept_return_time offset
avg_call_time_offset            set     141     . .average_intercept_call_time offset
avg_return_time_offset          set     147     . .average_intercept_return_time offset
avg_stats_request_time_offset   set     153     . .average_stats_request_time offset
timed_call_overhead_offset      set     159     . .timed_call_intercept_overhead offset
timed_return_overhead_offset    set     165     . .timed_return_intercept_overhead offset
untimed_call_overhead_offset    set     171     . .untimed_call_intercept_overhead offset
untimed_return_overhead_offset  set     177     . .untimed_return_intercept_overhead offset
.
.
.     constants for offsets into pmt$interblock_references_hdr
.
.
no_of_interblock_ref_offset     set       4
.
.
.     constants for offsets into pmt$interblock_reference
.
.
reference_time_offset           set       1
block_id_offset                 set       7
paging_statistics_offset        set      12
.
.
paging_statistics_size          equ      18    . size of pmt$paging_statistics
jobmode_stats_paging_stats      set       8    . offset into pmt$apd_task_jobmode_
.                                                statistics
.
.     constant for maximum number of intercepted calls and returns to be timed
.
maximum_timed_intercepts        vfd,64  5000(10)
.
.     constants for offsets of areas used in the workspace.  Additions may have
.     been made to insure word boundaries.
.
stack_workspace                 set     360
.
ws_callers_x0                   set       8     . size = 8
ws_callers_regs                 set      16     . size = 224
ws_parameter_list               set     240     . size = 8
ws_est_handler                  set     248     . size = 64
ws_jobmode_stats                set     312     . size = 32
ws_save_a3a4                    set     344     . size = 16
.
               title          c'pmp$initial_intercept_procedure'
               align          0,8
.
PMP$INITIAL_INTERCEPT_PROCEDURE   addaq       a0,a0,stack_workspace . save workspace
.
.     save original caller's environment at the top of stack
.
               sx             x0,a1,ws_callers_x0         . save actual callers X0
.
               enta           x0,31ff(16)                 . save registers from actual
.                                                           caller which are used by
.                                                           this intercept procedure;
.                                                           registers A3 - AF
.                                                           registers X1 - XF
               smult          x0,a1,ws_callers_regs
.
.     change A3 to point to the intercept procedures binding section
.
               addaq          a3,a3,2*word_size
               la             a5,a3,2                     . A5 = Binding section ptr
.
.     establish block exit condition handler
.
               addaq          a6,a1,ws_est_handler        . get ptr to established_handler
               la             a8,a5,ptr_block_exit_desc   . get ptr to osv$default_
.                                                           block_exit_desc
               movb,a8,x0 a6,x1 0,9,est_handler_size,0 0,9,est_handler_size,0
.
.     pmt$established_handler.handler
.
               addaq          a8,a5,ptr_cleanup_handler
               sa             a8,a6,7
.
.     Store pointer to loader_seq_descriptor in static link of pointer to procedure
.
               addaq          a8,a3,word_size*2+2
               la             a7,a8,0                     . A7 = pointer to loader
.                                                           _seq_descriptor
               sa             a7,a6,13
.
.     store stack frame word (pmt$os_stack_frame_word) at A1
.
               sa             a6,a1,0                     . pointer to
.                                                           pmt$established_handler
               ente           x2,4000(16)                 . block_exit_frame = TRUE
               sbyts,2        x2,a1,x0,6                  . debug_cff_frame = FALSE
.                                                           ada_critical_frame = FALSE
.                                                           ada_critical_frame_count=0
.
.     set critical_frame_flag
.
               ente           x2,0e1(16)
               cpyxs          x2,x2
.
.     set on_condition_flag
.
               ente           x2,0e3(16)
               cpyxs          x2,x2
.
.     restore original callers environment except registers A0, A1, and A2
.
               enta           x0,31ff(16)
               lmult          x0,a1,ws_callers_regs
.
               lx             x0,a1,ws_callers_x0         . restore original X0
.
               callseg        0,a3,a4
               return
.
               title          c'pmp$intercept_call_procedure'
.
. PURPOSE:
.   The purpose of this request is to intercept a program call from an
.   instrumented task and record the reference times and page statistics for
.   the callee in the interblock_reference file.  This information is recorded
.   at the points of call and return for the callee.
. DESIGN:
.   The accumulated time spent processing all call and return intercepts is
.   subtracted from the reference times for the callee before they are recorded
.   in the file.  This sequence of times approximates the actual reference
.   times for standalone execution of the instrumented task.  The time spent
.   processing call and return intercepts is monitored for a count of
.   maximum_timed_intercepts and the average values are used for subsequent
.   calls and returns.  There is no adjustment to the page statistics for
.   page faults due to accessing the interblock_reference file.
. NOTES:
.   Instructions that cannot be timed in this procedure are estimated prior to
.   execution of an instrumented task and stored in the loader_seq_descriptor
.   file for access by this procedure.  Any modifications to the procedure calls
.   that obtain the task jobmode statistics or code outside the pairs of these
.   calls may require similar modifications to the procedure that initializes
.   the intercept variables in pmm$analyze_program_dynamics.
.   The tag, pmp$apd_call_to_users_procedure, in this procedure functions as an
.   entry point and must always be aligned on a word boundary.  Check this on an
.   assembly listing after making any changes to the code preceding this tag
.   and adjust the padding instructions preceding the tag if necessary.
.
.
               align          0,8
.
. NOTE: start of code simulated in pmp$simulate_return_overhead procedure
.
PMP$INTERCEPT_CALL_PROCEDURE   addaq        a0,a0,stack_workspace . save workspace
.
.     save original caller's environment at the top of stack
.
               sx             x0,a1,ws_callers_x0         . save actual callers X0
               enta           x0,31ff(16)                 . save registers from actual
.                                                           caller which are used by
.                                                           this intercept procedure;
.                                                           registers A3 - AF
.                                                           registers X1 - XF
               smult          x0,a1,ws_callers_regs       . store A and X registers
.                                                           in stack
.
.     change A3 to point to the intercept procedures binding section
.
               addaq          a3,a3,2*word_size
               la             a5,a3,2                     . A5 = Binding section ptr
               addaq          a4,a1,ws_parameter_list     . get ptr to parameter
.                                                           list
.
. NOTE: end of code simulated in pmp$simulate_return_overhead procedure
.
.
.     set up parameter list for call to pmp$get_apd_task_jobmode_stats
.
.     pmt$apd_task_jobmode_statistics
.
               addaq          a6,a1,ws_jobmode_stats      . get ptr to jobmode_stats
               sa             a6,a4,0                     . in parameter list
               enta           x0,050(16)                  . save registers A0 - A5
.                                                           on call to pmp$get_apd_
.                                                           task_jobmode_statistics
               callseg        ptr_get_job_stats,a5,a4
.
.     establish block exit condition handler
.
               addaq          a6,a1,ws_est_handler        . get ptr to pmt$established_handler
               la             a8,a5,ptr_block_exit_desc   . get ptr to osv$default_
.                                                           block_exit_desc
               movb,a8,x0 a6,x1 0,9,est_handler_size,0 0,9,est_handler_size,0
.
.     pmt$established_handler.handler
.
               addaq          a8,a5,ptr_cond_handler
               sa             a8,a6,7
.
.     Store pointer to loader_seq_descriptor in static link of pointer to procedure
.
               addaq          a8,a3,word_size*2+2
               la             a7,a8,0                     . A7 = pointer to loader
.                                                           _seq_descriptor
               sa             a7,a6,13
.
.     store stack frame word (pmt$os_stack_frame_word) at A1
.
               sa             a6,a1,0                     . pointer to
.                                                           pmt$established_handler
               ente           x2,4000(16)                 . block_exit_frame = TRUE
               sbyts,2        x2,a1,x0,6                  . debug_cff_frame = FALSE
.                                                           ada_critical_frame = FALSE
.                                                           ada_critical_frame_count=0
.
.     set critical_frame_flag
.
               ente           x2,0e1(16)
               cpyxs          x2,x2
.
.     set on_condition_flag
.
               ente           x2,0e3(16)
               cpyxs          x2,x2
.
.     Allocate an interblock_reference (pmt$interblock_reference) and
.     record_intercepted_call
.
.     'NEXT' an interblock_reference into the loader_seq_descriptor
.
               la             a6,a7,last_interblock_seg_offset . A6 = ptr to current
.                                                           interblock_references_hdr
               lbyts,8        x2,a7,x0,6+last_interblock_seg_offset . get upper_limit
.                                                           and offset of interblock
.                                                           _references
               addxq          x3,x2,interblock_reference_size . increment offset by
.                                                           size of pmt$interblock_
.                                                           reference
               shfx           x4,x2,x0,-32                . get max segment length
               brrge          x4,x3,update_seq_ptr        . check for size greater
.                                                           than max segment length
.
.     Segment would overflow if interblock_reference is added to it.
.     Instead, open a new segment for more interblock references.
.
.     Set up call to PMP$OPEN_NEW_INTERBLOCK_SEGMENT
.
               sa             a7,a4,0                     . Store ptr to loader_seq_
.                                                           descriptor in parameter_
.                                                           list
               enta           x0,070(16)                  . Save registers A0 - A7
               callseg        ptr_open_new_seg,a5,a4      . on call to pmp$open_new
.                                                           _interblock_segment
.
               la             a6,a7,last_interblock_seg_offset . A6 = ptr to current
.                                                           interblock_references_hdr
               lbyts,4        x2,a7,x0,10+last_interblock_seg_offset . get offset of
.                                                           interblock_references
               addxq          x3,x2,interblock_reference_size . increment offset by
.                                                           size of pmt$interblock_
.                                                           reference
.
update_seq_ptr sbyts,4        x3,a7,x0,10+last_interblock_seg_offset . update offset
.                                                           in adaptable sequence
.                                                           pointer
               cpyaa          a8,a6                       . A8= ptr to pmt$interblock
               addax          a8,x2                       . _reference
.
.     update loader_seq_descriptor.number_of_interblock_references
.
               lbyts,4        x2,a6,x0,no_of_interblock_ref_offset . A7 points to
.                                                           interblock_references_hdr
               incx           x2,1
               sbyts,4        x2,a6,x0,no_of_interblock_ref_offset
.
.     fill in fields of interblock_reference
.
.     pmt$reference_type
.
               entp           x2,0                        . reference_type = pmc$call
               sbyts,1        x2,a8,x0,0                  . A8 - points to interblock
.                                                           _reference
.     pmt$block_identifier
.
               addaq          a9,a3,word_size             . get block_id from binding
.                                                           section entry
               lbyts,5        x2,a9,x0,3
               sbyts,5        x2,a8,x0,block_id_offset    . put in interblock_reference
.
.     fill in interblock_reference.reference_time
.
               lbyts,6        x2,a7,x0,accum_intercept_time_offset
               addaq          a9,a1,ws_jobmode_stats      . access job statistics
               lbyts,8        x3,a9,x0,0                  . pick up current_cptime
               subx           x3,x2                       . X2 has accumulate_
.                                                           intercept_time (current_
.                                                           cptime - accumulated_
.                                                           intercept_time)
               sbyts,6        x3,a8,x0,reference_time_offset   . store reference_time
.                                                           in interblock_reference
.
.     Move paging statistics from jobmode_stats to the interblock reference.
.
               addaq          aa,a9,jobmode_stats_paging_stats
               addaq          ab,a8,paging_statistics_offset
               movb,aa,x0 ab,x1 0,9,paging_statistics_size,0 0,9,paging_statistics_size,0
.
.     check if this intercepted call is to be timed
.
               lbyts,4        x3,a7,x0,no_of_calls_offset . current number of calls
               lbytp,8        x2,maximum_timed_intercepts . threshhold for timed calls
               brxge          x3,x2,add_avg_call          . if call is not to be timed
               incx           x3,1                        . increment number of calls
               sbyts,4        x3,a7,x0,no_of_calls_offset
               subx           x2,x3                       . (threshhold - current)
.
.     save initial intercept cptime value in X3
.
               lbyts,8        x3,a9,x0,0                  . X3 = initial jobmode_cptime
.
.     set up parameter list for call to pmp$get_apd_task_jobmode_stats
.
.     pmt$apd_task_jobmode_statistics
.
               sa             a9,a4,0                     . store jobmode statistics
.                                                           in parameter list
               ente           x0,093(16)                  . save A0 - A9, X0 - X3
               callseg        ptr_get_job_stats,a5,a4
.
.     calculate and update loader_seq_descriptor.accumulated_intercept_time for
.     a timed call
.
.     accumulated_intercept_time = accumulated_intercept_time +
.           timed_call_overhead + (current cptime - initial cptime)
.
.     get current cptime value
.
               lbyts,8        x4,a9,x0,0                  . get current jobmode_cptime
               subx           x4,x3                       . (current cptime - initial
.                                                           cptime)
.     get timed_call_overhead for a timed call
.
               lbyts,6        x3,a7,x0,timed_call_overhead_offset
               addx           x3,x4
.
.     get accumulated_intercept_time value
.
               lbyts,6        x5,a7,x0,accum_intercept_time_offset
               addx           x3,x5                       . calculate new accumulated_
.                                                           intercept_time
               sbyts,6        x3,a7,x0,accum_intercept_time_offset
.
.     calculate and update loader_seq_descriptor.accum_intercept_call_time for
.     intercepted calls
.
.     accum_intercept_call_time = accum_intercept_call_time +
.           untimed_call_overhead + (current cptime - initial cptime)
.
.     get untimed_call_overhead for an intercepted call
.
               lbyts,6        x3,a7,x0,untimed_call_overhead_offset
               addx           x4,x3
.
.     update the accum_intercept_call_time
.
               lbyts,6        x5,a7,x0,accum_call_time_offset
               addx           x4,x5
               sbyts,6        x4,a7,x0,accum_call_time_offset
               brxne          x0,x2,save_registers        . if not last call to time
.
.     calculate the average_intercept_call_time required to process an intercepted call
.     which will be used as the estimate for all subsequent untimed calls
.
.     average_intercept_call_time = ((accum_intercept_call_time +
.           .5 * maximum_timed_intercepts ) / maximum_timed_intercepts ) -
.           average_stats_request_time
.
               lbytp,8        x3,maximum_timed_intercepts . maximum_timed_intercepts
               shfx           x5,x3,x0,-1                 . .5 * maximum_timed_intercepts
               addx           x4,x5
               divx           x4,x3
               lbyts,6        x2,a7,x0,avg_stats_request_time_offset
               subx           x4,x2
.
               sbyts,6        x4,a7,x0,avg_call_time_offset
               brxeq          x0,x0,save_registers        . continue processing
.
.     update the loader_seq_descriptor.accumulated_intercept_time for an untimed call
.     using the average_intercept_call_time value
.
.     accumulated_intercept_time = accumulated_intercept_time +
.           average_intercept_call_time
.
add_avg_call   lbyts,6        x2,a7,x0,avg_call_time_offset
               lbyts,6        x3,a7,x0,accum_intercept_time_offset
               addx           x2,x3
               sbyts,6        x2,a7,x0,accum_intercept_time_offset
.
. NOTE: start of code simulated in pmp$simulate_call_overhead procedure
.
.     save intercept_call_procedures's A3 and A4 registers
.
save_registers enta           x0,3040(16)
               smult          x0,a1,ws_save_a3a4
.
.     restore original callers environment except registers A0, A1, and A2
.
               enta           x0,31ff(16)
               lmult          x0,a1,ws_callers_regs
.
. ******************************************************************************
. *   insert a no-op to align PMP$APD_CALL_TO_USERS_PROGRAM on a word boundary *
. *   These may have to changed (commented or uncommented) every time this     *
. *   procedure is changed.  Note that instructions must be used for padding   *
. *   instead of the "align" statement because whatever is in the pad will be  *
. *   executed!                                                                *
               addaq          a0,a0,0                     . 4 byte no-op       *
.              cpyxx          x1,x1                       . 2 byte no-op       *
. ******************************************************************************
.
.     call the original callee
.
               lx             x0,a1,ws_callers_x0         . restore orginal X0
               callseg        0,a3,a4
.
. ***************************************************************************
. *   insert a dummy entry point for the condition handling mechanism       *
. *   NOTE: this entry point MUST be word aligned                           *
. *         comment or un-comment the addaq and cpyxx instructions above to *
. *         provide the required padding                                    *
. ***************************************************************************
.
.     save registers again before call to record_intercepted_return
.
pmp$apd_call_to_users_procedure     sx             x0,a1,ws_callers_x0 . save X0
.
               enta           x0,31ff(16)
               smult          x0,a1,ws_callers_regs
.
.     restore intercept_call_procedure's A3 and A4 registers
.
               enta           x0,3040(16)
               lmult          x0,a1,ws_save_a3a4
.
. NOTE: end of code simulated in pmp$simulate_call_overhead procedure
.
.     Allocate an interblock reference (pmt$interblock_reference) and
.     record_intercepted_return
.
.     set up parameter for pmp$get_apd_task_jobmode_stats
.
.     pmt$apd_task_jobmode_statistics
.
               addaq          a8,a1,ws_jobmode_stats      . set A8 pointing to current_
.                                                           jobmode_statistics
               sa             a8,a4,0
               la             a5,a3,2
               ente           x0,050(16)                  . save registers A3 - A5
               callseg        ptr_get_job_stats,a5,a4
.
.     'NEXT' an interblock_reference into the loader_seq_descriptor
.
               addaq          a6,a3,2*word_size+2         . pick up pointer to loader_
.                                                           seq_descriptor
               la             a7,a6,0                     . A7 - address of loader_seq_
.                                                           descriptor
               la             a6,a7,last_interblock_seg_offset . A6 - ptr to current
.                                                           interblock_references_hdr
               lbyts,8        x2,a7,x0,6+last_interblock_seg_offset . pick up upper_
.                                                           limit and offset of
.                                                           interblock_references_hdr
               addxq          x3,x2,interblock_reference_size . increase offset
.                                                           by size of
.                                                           pmt$interblock_reference
               shfx           x4,x2,x0,-32                . get max segment length
               brrge          x4,x3,update_offset         . check for size greater
.                                                           than max segment length
.
.     Segment would overflow if interblock_reference is added to it.
.     Instead, open a new segment for more interblock references.
.
.     Set up call to PMP$OPEN_NEW_INTERBLOCK_SEGMENT
.
               sa             a7,a4,0                     . Store ptr to loader_seq_
.                                                           descriptor in parameter_
.                                                           list
               enta           x0,070(16)                  . Save registers A0 - A7
               callseg        ptr_open_new_seg,a5,a4      . on call to pmp$open_new
.                                                           _interblock_segment
.
               la             a6,a7,last_interblock_seg_offset . A6 = ptr to current
.                                                           interblock_references_hdr
               lbyts,4        x2,a7,x0,10+last_interblock_seg_offset . pick up offset
.                                                           interblock_references seg
               addxq          x3,x2,interblock_reference_size . increment offset by
.                                                           size of pmt$interblock_
.                                                           reference
.
update_offset  sbyts,4        x3,a7,x0,10+last_interblock_seg_offset . update offset
.                                                           in adaptable sequence
.                                                           pointer
.                                                           (.interblock_references)
               cpyaa          a8,a6                       . A8 = address of interblock
               addax          a8,x2                       . reference
.
.     update loader_seq_descriptor.number_of_interblock_references
.
               lbyts,4        x2,a6,x0,no_of_interblock_ref_offset . A7 - address of
.                                                           loader_seq_descriptor
               incx           x2,1
               sbyts,4        x2,a6,x0,no_of_interblock_ref_offset
.
.     fill in fields of interblock_reference
.
.     pmt$reference_type
.
               entp           x2,1                        . reference_type = pmc$return
               sbyts,1        x2,a8,x0,0
.
.     pmt$reference_time
.
.     get current jobmode_cptime
.
               addaq          a9,a1,ws_jobmode_stats
               lbyts,8        x3,a9,x0,0                  . pick up jobmode_cptime
.
.     get loader_seq_descriptor.accumulated_intercept_time
.
               lbyts,6        x2,a7,x0,accum_intercept_time_offset
               subx           x3,x2                       . current.jobmode_cptime -
.                                                           accumulated_intercept_time
               sbyts,6        x3,a8,x0,reference_time_offset
.
.     Move paging statistics from jobmode_stats to the interblock reference.
.
               addaq          aa,a9,jobmode_stats_paging_stats
               addaq          ab,a8,paging_statistics_offset
               movb,aa,x0 ab,x1 0,9,paging_statistics_size,0 0,9,paging_statistics_size,0
.
.     check if this intercepted return is to be timed
.
               lbyts,4        x3,a7,x0,no_of_returns_offset . current number of returns
               lbytp,8        x2,maximum_timed_intercepts . threshhold for timed returns
               brxge          x3,x2,add_avg_return        . if return is not to be timed
               incx           x3,1                        . increment number of returns
               sbyts,4        x3,a7,x0,no_of_returns_offset
               subx           x2,x3                       . (threshhold - current)
.
.     save the initial intercept cptime value
.
               lbyts,8        x3,a9,x0,0                  . initial jobmode_cptime
.
.     set up parameter list for call to pmp$get_apd_task_jobmode_stats
.
.     pmt$apd_task_jobmode_statistics
.
               sa             a9,a4,0
               ente           x0,093(16)                  . save A0 - A9, X0 - X3
               callseg        ptr_get_job_stats,a5,a4
.
.     calculate and update loader_seq_descriptor.accumulated_intercept_time
.     for a timed return
.
.     accumulated_intercept_time = accumulated_intercept_time +
.           timed_return_overhead + (current cptime - initial cptime)
.
.     get current cptime value
.
               lbyts,8        x4,a9,x0,0                  . .jobmode_cptime
               subx           x4,x3                       . (current cptime - initial
.                                                           cptime)
.
.     get timed_return_overhead for a timed return
.
               lbyts,6        x3,a7,x0,timed_return_overhead_offset
               addx           x3,x4
.
.     get accumulated_intercept_time value
.
               lbyts,6        x5,a7,x0,accum_intercept_time_offset
               addx           x3,x5                       . calculate new accumulated_
.                                                           intercept_time
               sbyts,6        x3,a7,x0,accum_intercept_time_offset
.
.     calculate and update loader_seq_descriptor.accum_intercept_return_time for
.     intercepted returns
.
.     accum_intercept_return_time = accum_intercept_return_time +
.           untimed_return_overhead + (current cptime - initial cptime)
.
.     get untimed_return_overhead for an intercepted return
.
               lbyts,6        x3,a7,x0,untimed_return_overhead_offset
               addx           x4,x3
.
.     update the accum_intercept_return_time
.
               lbyts,6        x5,a7,x0,accum_return_time_offset
               addx           x4,x5
               sbyts,6        x4,a7,x0,accum_return_time_offset
               brxne          x0,x2,process_exit          . if not last return to time
.
.     calculate the average_intercept_return_time required to process an intercepted
.     return which will be used as the estimate for all subsequent untimed returns
.
.     average_intercept_return_time = ((accum_intercept_return_time +
.           .5 * maximum_timed_intercepts ) / maximum_timed_intercepts ) -
.           average_stats_request_time
.
               lbytp,8        x3,maximum_timed_intercepts . maximum_timed_intercepts
               shfx           x5,x2,x0,-1                 . .5 * maximum_timed_intercepts
               addx           x4,x5
               divx           x4,x3
               lbyts,6        x2,a7,x0,avg_stats_request_time_offset
               subx           x4,x2
.
               sbyts,6        x4,a7,x0,avg_return_time_offset
               brxeq          x0,x0,process_exit          . continue processing
.
.     update the loader_seq_descriptor.accumulated_intercept_time for an untimed return
.     using the average_intercept_return_time value
.
.     accumulated_intercept_time = accumulated_intercept_time +
.           average_intercept_return_time
.
add_avg_return lbyts,6        x2,a7,x0,avg_return_time_offset
               lbyts,6        x3,a7,x0,accum_intercept_time_offset
               addx           x2,x3
               sbyts,6        x2,a7,x0,accum_intercept_time_offset
.
. NOTE: start of code simulated in pmp$simulate_return_overhead procedure
.
.     disestablish block exit condition handler
.
process_exit   la             a5,a1,0
               ente           x2,0                        . set established to FALSE
               sbyts,1        x2,a5,x0,0
.
               ente           x2,0e0(16)                  . clear critical_frame_flag
               cpyxs          x2,x2
.
.     restore all registers
.
               enta           x0,31ff(16)
               lmult          x0,a1,ws_callers_regs
               lx             x0,a1,ws_callers_x0
.
. NOTE: end of code simulated in pmp$simulate_return_overhead procedure
.
               return
.
               title          c'record_trapped_block_exit'
               align          0,8
.
RECORD_TRAPPED_BLOCK_EXIT     addaq          a0,a0,word_size . 8 bytes reserved for
.                                                           condition handler
               addaq          a6,a4,word_size+2           . get address to
.                                                           pmt$condition_information
               sa             a5,a6,0                     . store pointer to
.                                                           apd_seq_descriptor in
.                                                           pmt$condition_information;
.                                                           seq_ptr in static link
               enta           x0,0ff(16)                  . save all registers for call
.
.    call process_block_exit
.
               callseg        ptr_proc_block_exit,a3,a4
.
               return
.
               title          c'clean_up_condition_handler'
               align          0,8
.
CLEAN_UP_CONDITION_HANDLER      addaq          a0,a0,word_size  . 8 bytes reserved for
.                                                                 condition handler
               addaq          a6,a4,word_size+2           . get address to
.                                                           pmt$condition_information
               sa             a5,a6,0                     . store pointer to
.                                                           apd_seq_descriptor in
.                                                           pmt$condition_information
               enta           x0,0ff(16)                  . save all registers for call
.
.    call process_exit_from_apd
.
               callseg        ptr_proc_exit_apd,a3,a4
.
               return
.
               title          c'pmp$simulate_call_overhead'
.
. PURPOSE:
.   The purpose of this request is to simulate the instructions that save and
.   restore registers after processing a call intercept and before processing
.   a return intercept in pmp$intercept_call_procedure.
. NOTES:
.   Modifications to the above sequence of instructions in
.   pmp$intercept_call_procedure may require modifications to this procedure.
.   The instructions in this procedure are for timing purposes only and do
.   not necessarily produce the same result as the instructions they simulate.
.
               align          0,8
.
PMP$SIMULATE_CALL_OVERHEAD    enta      x0,3040(16)       . simulate save of registers
               smult          x0,a1,ws_save_a3a4
.
               enta           x0,31ff(16)
               smult          x0,a1,ws_callers_regs
.
.     simulate restore of registers
.
               enta           x0,31ff(16)
               lmult          x0,a1,ws_callers_regs
.
               enta           x0,3040(16)
               lmult          x0,a1,ws_save_a3a4
.
               addaq          a0,a0,0                     . alignment no-op
.              cpyxx          x1,x1                       . alignment no-op

               lx             x0,a1,ws_callers_x0         . restore orginal X0
.
               return
.
.
               title          c'pmp$simulate_return_overhead'
.
. PURPOSE:
.   The purpose of this request is to simulate the instructions that save and
.   restore registers before processing a call intercept and after processing
.   a return intercept in pmp$intercept_call_procedure.
. NOTES:
.   Modifications to the above sequence of instructions in
.   pmp$intercept_call_procedure may require modifications to this procedure.
.   The instructions in this procedure are for timing purposes only and do
.   not necessarily produce the same result as the instructions they simulate.
.
               align          0,8
.
PMP$SIMULATE_RETURN_OVERHEAD  addaq       a0,a0,stack_workspace . reserve bytes
.
.    simulate save of registers and other initialization before processing call
.
               sx             x0,a1,ws_callers_x0         . save X0 register
               enta           x0,31ff(16)
               smult          x0,a1,ws_callers_regs
.
               addaq          a6,a3,0
               la             a5,a3,2
               addaq          a5,a5,word_size
.
.    simulate setting of flags and restore of registers after processing a return
.
               la             a5,a3,2
               ente           x2,0
               sbyts,1        x2,a1,x0,0
.
               ente           x2,0e0(16)                  . clear critical_frame_flag
               cpyxx          x3,x2                       . dummy store
.
               enta           x0,31ff(16)
               lmult          x0,a1,ws_callers_regs
               lx             x0,a1,ws_callers_x0         . restore original X0
.
               return
               end
