/******************************************************************************
*                                                                             *
* License Agreement                                                           *
*                                                                             *
* Copyright (c) 2003-2010 Altera Corporation, San Jose, California, USA.      *
* All rights reserved.                                                        *
*                                                                             *
* Permission is hereby granted, free of charge, to any person obtaining a     *
* copy of this software and associated documentation files (the "Software"),  *
* to deal in the Software without restriction, including without limitation   *
* the rights to use, copy, modify, merge, publish, distribute, sublicense,    *
* and/or sell copies of the Software, and to permit persons to whom the       *
* Software is furnished to do so, subject to the following conditions:        *
*                                                                             *
* The above copyright notice and this permission notice shall be included in  *
* all copies or substantial portions of the Software.                         *
*                                                                             *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,    *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER      *
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING     *
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER         *
* DEALINGS IN THE SOFTWARE.                                                   *
*                                                                             *
* This agreement shall be governed in all respects by the laws of the State   *
* of California and by the laws of the United States of America.              *
*                                                                             *
******************************************************************************/

/*  mcount or _mcount is inserted by GCC before the function prologue of every 
 *  function when a program is compiled for profiling. At the start of mcount,
 *  we guarantee that:
 *  ra = self_pc (an address in the function which called mcount)
 *  r8 = from_pc (an address in the function which called mcount's caller)
 *
 *  Because this is always called at the start of a function we can corrupt
 *  r2,r3 and r11-r15.  We must not corrupt r4-r7 (because they might contain
 *  function arguments for the instrumented function) or r8 (which holds ra
 *  for the instrumented function).
 */

        .global __mcount_fn_head

        .global mcount

        /* _mcount is used by gcc4 */
        .global _mcount

_mcount:        
mcount:
        /* Use a hash to speed up locating fn_entry.  We use bits 5 upwards to choose
         * the bucket because bits 1:0 will always be 0, and because the distribution
         * of values for bits 4:2 won't be even (aligning on cache line boundaries
         * will skew it).  Higher bits should be fairly random.
         */
        /* fn_head = mcount_fn_head + (((unsigned int)self_pc >> 5) & (HASH_BUCKETS - 1)); */

        srli    r2, ra, 3
        movhi   r3, %hiadj(__mcount_fn_head)
        addi    r3, r3, %lo(__mcount_fn_head)
        andi    r2, r2, 0xFC
        add     r11, r2, r3

        /* The fast case is where we have already allocated a function arc, and so
         * also a function pointer.
         */

        /* First find the function being called (using self_pc) */
        mov     r10, r11
0:
        ldw     r10, 0(r10)
        beq     r10, zero, .Lnew_arc
        ldw     r2, 4(r10)
        bne     r2, ra, 0b

        /* Found a function entry for this PC. Now look for an arc with a matching
         * from_pc value. There will always be at least one arc. */
        ldw     r3, 8(r10)
0:
        ldw     r2, 4(r3)
        beq     r2, r8, .Lfound_arc
        ldw     r3, 0(r3)
        bne     r3, zero, 0b

.Lnew_arc:
        addi    sp, sp, -24

.LCFI0:
        stw     ra, 0(sp)
        stw     r4, 4(sp)
        stw     r5, 8(sp)
        stw     r6, 12(sp)
        stw     r7, 16(sp)
        stw     r8, 20(sp)

.LCFI1:
        /* __mcount_record(orig_ra, orig_r8, fn_entry, *fn_head); */
        mov     r4, ra
        mov     r5, r8
        mov     r6, r10
        mov     r7, r11
        call     __mcount_record
        
        /* restore registers from the stack */
        ldw     ra, 0(sp)
        ldw     r4, 4(sp)
        ldw     r5, 8(sp)
        ldw     r6, 12(sp)
        ldw     r7, 16(sp)
        ldw     r8, 20(sp)

        addi    sp, sp, 24

.LCFI2:
        ret

.Lfound_arc:
        /* We've found the correct arc record.  Increment the count and return */
        ldw     r2, 8(r3)
        addi    r2, r2, 1
        stw     r2, 8(r3)
        ret

.Lmcount_end:



/*
 * Dwarf2 debug information for the function.  This provides GDB with the
 * information it needs to backtrace out of this function.
 */

        .section    .debug_frame,"",@progbits
.LCIE:
        .4byte   2f - 1f               /* Length */
1:
        .4byte   0xffffffff            /* CIE id */
        .byte    0x1                   /* Version */
        .string  ""                    /* Augmentation */
        .uleb128 0x1                   /* Code alignment factor */
        .sleb128 -4                    /* Data alignment factor */
        .byte    0x1f                  /* Return address register */

        .byte    0xc                   /* Define CFA */
        .uleb128 0x1b                  /*   Register 27 (sp) */
        .uleb128 0x0                   /*   Offset 0 */

        .align   2                     /* Padding */
2:

.LFDE_mcount:
        .4byte   2f - 1f               /* Length */
1:
        .4byte   .LCIE                 /* Pointer to CIE */
        .4byte   mcount                /* Start of table entry */
        .4byte   .Lmcount_end - mcount /* Size of table entry */

        .byte    0x4                   /* Advance location */
        .4byte   .LCFI0 - mcount       /*   to .LCFI0 */
        .byte    0xe                   /* Define CFA offset */
        .uleb128 24                    /*   to 24     */

        .byte    0x4                   /* Advance location */
        .4byte   .LCFI1 - .LCFI0       /*   to .LCFI1 */
        .byte    0x9f                  /* Store ra    */
        .uleb128 0x6                   /*   at CFA-24 */
        .byte    0x84                  /* Store r4    */
        .uleb128 0x5                   /*   at CFA-20 */
        .byte    0x85                  /* Store r5    */
        .uleb128 0x4                   /*   at CFA-16 */
        .byte    0x86                  /* Store r6    */
        .uleb128 0x3                   /*   at CFA-12 */
        .byte    0x87                  /* Store r7    */
        .uleb128 0x2                   /*   at CFA-8  */
        .byte    0x88                  /* Store r8    */
        .uleb128 0x1                   /*   at CFA-4  */

        .byte    0x4                   /* Advance location */
        .4byte   .LCFI2 - .LCFI1       /*   to .LCFI2 */
        .byte    0xe                   /* Define CFA offset */
        .uleb128 0                     /*   to 0      */
        .byte    0x8                   /* Same value  */
        .uleb128 31                    /*   for ra    */
        .byte    0x8                   /* Same value  */
        .uleb128 4                     /*   for r4    */
        .byte    0x8                   /* Same value  */
        .uleb128 5                     /*   for r5    */
        .byte    0x8                   /* Same value  */
        .uleb128 6                     /*   for r6    */
        .byte    0x8                   /* Same value  */
        .uleb128 7                     /*   for r7    */
        .byte    0x8                   /* Same value  */
        .uleb128 8                     /*   for r8    */

        .align   2
2:

