OSDN Git Service

2008-11-27 Ken Werner <ken.werner@de.ibm.com>
authorjjohnstn <jjohnstn>
Thu, 27 Nov 2008 20:57:45 +0000 (20:57 +0000)
committerjjohnstn <jjohnstn>
Thu, 27 Nov 2008 20:57:45 +0000 (20:57 +0000)
        * libc/machine/spu/Makefile.am: Add spu-mcount.S spu-gmon.c.
        * libc/machine/spu/Makefile.in: Regenerated.
        * libc/machine/spu/spu-gmon.c: New file.
        * libc/machine/spu/spu-mcount.S: New file.

newlib/ChangeLog
newlib/libc/machine/spu/Makefile.am
newlib/libc/machine/spu/Makefile.in
newlib/libc/machine/spu/spu-gmon.c [new file with mode: 0644]
newlib/libc/machine/spu/spu-mcount.S [new file with mode: 0644]

index 56b53c0..6d4895b 100644 (file)
@@ -1,3 +1,10 @@
+2008-11-27  Ken Werner  <ken.werner@de.ibm.com>
+
+       * libc/machine/spu/Makefile.am: Add spu-mcount.S spu-gmon.c.
+       * libc/machine/spu/Makefile.in: Regenerated.
+       * libc/machine/spu/spu-gmon.c: New file.
+       * libc/machine/spu/spu-mcount.S: New file.
+
 2008-11-27  Joel Sherrill <joel.sherrill@oarcorp.com>
 
        * configure.host (*-rtems*): Turn on using portion of unix subdirectory.
index 8a192dd..6083145 100644 (file)
@@ -31,7 +31,8 @@ lib_a_SOURCES += calloc_ea.c free_ea.c malloc_ea.c memchr_ea.c memcmp_ea.c \
        munmap_ea.c posix_memalign_ea.c realloc_ea.c strcat_ea.c strchr_ea.c \
        strcmp_ea.c strcpy_ea.c strcspn_ea.c strlen_ea.c strncat_ea.c strncmp_ea.c \
        strncpy_ea.c strpbrk_ea.c strrchr_ea.c strspn_ea.c strstr_ea.c read_ea.c \
-       pread_ea.c readv_ea.c write_ea.c pwrite_ea.c writev_ea.c
+       pread_ea.c readv_ea.c write_ea.c pwrite_ea.c writev_ea.c spu-mcount.S \
+       spu-gmon.c
 endif
 
 lib_a_CCASFLAGS = $(AM_CCASFLAGS)
index 723ec40..2862f1f 100644 (file)
@@ -41,7 +41,8 @@ host_triplet = @host@
 @HAVE_SPU_EA_TRUE@     munmap_ea.c posix_memalign_ea.c realloc_ea.c strcat_ea.c strchr_ea.c \
 @HAVE_SPU_EA_TRUE@     strcmp_ea.c strcpy_ea.c strcspn_ea.c strlen_ea.c strncat_ea.c strncmp_ea.c \
 @HAVE_SPU_EA_TRUE@     strncpy_ea.c strpbrk_ea.c strrchr_ea.c strspn_ea.c strstr_ea.c read_ea.c \
-@HAVE_SPU_EA_TRUE@     pread_ea.c readv_ea.c write_ea.c pwrite_ea.c writev_ea.c
+@HAVE_SPU_EA_TRUE@     pread_ea.c readv_ea.c write_ea.c pwrite_ea.c writev_ea.c spu-mcount.S \
+@HAVE_SPU_EA_TRUE@     spu-gmon.c
 
 DIST_COMMON = $(srcdir)/../../../../config.guess \
        $(srcdir)/../../../../config.sub $(srcdir)/Makefile.in \
@@ -102,7 +103,7 @@ DIST_COMMON = $(srcdir)/../../../../config.guess \
        $(srcdir)/../../../../compile $(srcdir)/../../../../compile \
        $(srcdir)/../../../../compile $(srcdir)/../../../../compile \
        $(srcdir)/../../../../compile $(srcdir)/../../../../compile \
-       $(srcdir)/../../../../compile
+       $(srcdir)/../../../../compile $(srcdir)/../../../../compile
 subdir = .
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/../../../acinclude.m4 \
@@ -149,7 +150,9 @@ lib_a_LIBADD =
 @HAVE_SPU_EA_TRUE@     lib_a-readv_ea.$(OBJEXT) \
 @HAVE_SPU_EA_TRUE@     lib_a-write_ea.$(OBJEXT) \
 @HAVE_SPU_EA_TRUE@     lib_a-pwrite_ea.$(OBJEXT) \
-@HAVE_SPU_EA_TRUE@     lib_a-writev_ea.$(OBJEXT)
+@HAVE_SPU_EA_TRUE@     lib_a-writev_ea.$(OBJEXT) \
+@HAVE_SPU_EA_TRUE@     lib_a-spu-mcount.$(OBJEXT) \
+@HAVE_SPU_EA_TRUE@     lib_a-spu-gmon.$(OBJEXT)
 am_lib_a_OBJECTS = lib_a-setjmp.$(OBJEXT) lib_a-assert.$(OBJEXT) \
        lib_a-clearerr.$(OBJEXT) lib_a-creat.$(OBJEXT) \
        lib_a-fclose.$(OBJEXT) lib_a-feof.$(OBJEXT) \
@@ -506,6 +509,12 @@ lib_a-spu_timer_flih.o: spu_timer_flih.S
 lib_a-spu_timer_flih.obj: spu_timer_flih.S
        $(CCAS) $(lib_a_CCASFLAGS) $(CCASFLAGS) -c -o lib_a-spu_timer_flih.obj `if test -f 'spu_timer_flih.S'; then $(CYGPATH_W) 'spu_timer_flih.S'; else $(CYGPATH_W) '$(srcdir)/spu_timer_flih.S'; fi`
 
+lib_a-spu-mcount.o: spu-mcount.S
+       $(CCAS) $(lib_a_CCASFLAGS) $(CCASFLAGS) -c -o lib_a-spu-mcount.o `test -f 'spu-mcount.S' || echo '$(srcdir)/'`spu-mcount.S
+
+lib_a-spu-mcount.obj: spu-mcount.S
+       $(CCAS) $(lib_a_CCASFLAGS) $(CCASFLAGS) -c -o lib_a-spu-mcount.obj `if test -f 'spu-mcount.S'; then $(CYGPATH_W) 'spu-mcount.S'; else $(CYGPATH_W) '$(srcdir)/spu-mcount.S'; fi`
+
 .c.o:
        $(COMPILE) -c $<
 
@@ -1177,6 +1186,12 @@ lib_a-writev_ea.o: writev_ea.c
 
 lib_a-writev_ea.obj: writev_ea.c
        $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-writev_ea.obj `if test -f 'writev_ea.c'; then $(CYGPATH_W) 'writev_ea.c'; else $(CYGPATH_W) '$(srcdir)/writev_ea.c'; fi`
+
+lib_a-spu-gmon.o: spu-gmon.c
+       $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-spu-gmon.o `test -f 'spu-gmon.c' || echo '$(srcdir)/'`spu-gmon.c
+
+lib_a-spu-gmon.obj: spu-gmon.c
+       $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-spu-gmon.obj `if test -f 'spu-gmon.c'; then $(CYGPATH_W) 'spu-gmon.c'; else $(CYGPATH_W) '$(srcdir)/spu-gmon.c'; fi`
 uninstall-info-am:
 
 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
diff --git a/newlib/libc/machine/spu/spu-gmon.c b/newlib/libc/machine/spu/spu-gmon.c
new file mode 100644 (file)
index 0000000..527dfc6
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+(C) Copyright IBM Corp. 2008
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+* Neither the name of IBM nor the names of its contributors may be
+used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+Author: Ken Werner <ken.werner@de.ibm.com>
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <fcntl.h>
+#include <ea.h>
+#include <spu_intrinsics.h>
+#include <spu_mfcio.h>
+#include <spu_timer.h>
+#include <limits.h>
+
+/* Magic cookie.  */
+#define GMON_MAGIC_COOKIE "gmon"
+
+/* Version number.  */
+#define GMON_VERSION 1
+
+/* Fraction of text space to allocate for histogram counters.  */
+#define HISTFRACTION 4
+
+/* Histogram counter type.  */
+#define HISTCOUNTER unsigned short
+
+/* Fraction of text space to allocate for "from" hash buckets. HASHFRACTION is
+   based on the minimum number of bytes of separation between two subroutine
+   call points in the object code.  */
+#define HASHFRACTION 4
+
+/* Percent of text space to allocate for tostructs with a minimum.  */
+#define ARCDENSITY 3
+
+/* Minimal amount of arcs.  */
+#define MINARCS 50
+
+/* Rounding macros.  */
+#define ROUNDDOWN(x,y) (((x)/(y))*(y))
+#define ROUNDUP(x,y)   ((((x)+(y)-1)/(y))*(y))
+
+/* Sampling rate in Hertz.  */
+#define SAMPLE_INTERVAL 100
+
+/* Tag definitions for the gmon.out sub headers.  */
+#define GMON_TAG_TIME_HIST 0
+#define GMON_TAG_CG_ARC 1
+
+struct tostruct
+{
+  uintptr_t selfpc;
+  long count;
+  unsigned short link;
+};
+
+struct gmon_hdr
+{
+  char cookie[4];
+  int32_t version;
+  char spare[3 * 4];
+};
+
+struct gmon_hist_hdr
+{
+  uintptr_t low_pc;
+  uintptr_t high_pc;
+  int32_t hist_size;
+  int32_t prof_rate;
+  char dimen[15];
+  char dimen_abbrev;
+} __attribute__ ((packed));
+
+struct rawarc
+{
+  uintptr_t raw_frompc;
+  uintptr_t raw_selfpc;
+  long raw_count;
+} __attribute__ ((packed));
+
+/* start and end of the text section */
+extern char _start;
+extern char _etext;
+
+/* froms are indexing tos */
+static __ea unsigned short *froms;
+static __ea struct tostruct *tos = 0;
+static long tolimit = 0;
+static uintptr_t s_lowpc = 0;
+static uintptr_t s_highpc = 0;
+static unsigned long s_textsize = 0;
+
+static int fd;
+static int hist_size;
+static int timer_id;
+
+void
+__sample (int id)
+{
+  unsigned int pc;
+  unsigned int pc_backup;
+  off_t offset;
+  unsigned short val;
+
+  if (id != timer_id)
+    return;
+
+  /* Fetch program counter.  */
+  pc = spu_read_srr0 () & ~3;
+  pc_backup = pc;
+  if (pc < s_lowpc || pc > s_highpc)
+    return;
+  pc -= (uintptr_t) & _start;
+  offset = pc / HISTFRACTION * sizeof (HISTCOUNTER) + sizeof (struct gmon_hdr)
+             + 1 + sizeof (struct gmon_hist_hdr);
+
+  /* Read, increment and write the counter.  */
+  if (pread (fd, &val, 2, offset) != 2)
+    {
+      perror ("can't read the histogram");
+      return;
+    }
+  if (val < USHRT_MAX)
+    ++val;
+  if (pwrite (fd, &val, 2, offset) != 2)
+    {
+      perror ("can't write the histogram");
+    }
+}
+
+static void
+write_histogram (int fd)
+{
+  struct gmon_hist_hdr hist_hdr;
+  u_char tag = GMON_TAG_TIME_HIST;
+  hist_hdr.low_pc = s_lowpc;
+  hist_hdr.high_pc = s_highpc;
+  hist_hdr.hist_size = hist_size / sizeof (HISTCOUNTER); /* Amount of bins.  */
+  hist_hdr.prof_rate = 100; /* Hertz.  */
+  strncpy (hist_hdr.dimen, "seconds", sizeof (hist_hdr.dimen));
+  hist_hdr.dimen_abbrev = 's';
+  struct iovec iov[2] = {
+    {&tag, sizeof (tag)},
+    {&hist_hdr, sizeof (struct gmon_hist_hdr)}
+  };
+  if (writev (fd, iov, 2) != sizeof (struct gmon_hist_hdr) + sizeof (tag))
+    perror ("can't write the histogram header");
+
+  /* Skip the already written histogram data.  */
+  lseek (fd, hist_size, SEEK_CUR);
+}
+
+static void
+write_callgraph (int fd)
+{
+  int fromindex, endfrom;
+  uintptr_t frompc;
+  int toindex;
+  struct rawarc rawarc;
+  u_char tag = GMON_TAG_CG_ARC;
+  endfrom = s_textsize / (HASHFRACTION * sizeof (*froms));
+  for (fromindex = 0; fromindex < endfrom; ++fromindex)
+    {
+      if (froms[fromindex])
+       {
+         frompc = s_lowpc + (fromindex * HASHFRACTION * sizeof (*froms));
+         for (toindex = froms[fromindex]; toindex != 0;
+              toindex = tos[toindex].link)
+           {
+             rawarc.raw_frompc = frompc;
+             rawarc.raw_selfpc = tos[toindex].selfpc;
+             rawarc.raw_count = tos[toindex].count;
+             struct iovec iov[2] = {
+               {&tag, sizeof (tag)},
+               {&rawarc, sizeof (struct rawarc)}
+             };
+             if (writev (fd, iov, 2) != sizeof (tag) + sizeof (struct rawarc))
+                perror ("can't write the callgraph");
+           }
+       }
+    }
+}
+
+void
+__mcleanup (void)
+{
+  struct gmon_hdr ghdr;
+
+  /* Disable sampling.  */
+  spu_timer_stop (timer_id);
+  spu_timer_free (timer_id);
+  spu_clock_stop ();
+
+  /* Jump to the beginning of the gmon.out file.  */
+  if (lseek (fd, 0, SEEK_SET) == -1)
+    {
+      perror ("Cannot seek to the beginning of the gmon.out file.");
+      close (fd);
+      return;
+    }
+
+  /* Write the gmon.out header.  */
+  memset (&ghdr, '\0', sizeof (struct gmon_hdr));
+  memcpy (&ghdr.cookie[0], GMON_MAGIC_COOKIE, sizeof (ghdr.cookie));
+  ghdr.version = GMON_VERSION;
+  if (write (fd, &ghdr, sizeof (struct gmon_hdr)) == -1)
+    {
+      perror ("Cannot write the gmon header to the gmon.out file.");
+      close (fd);
+      return;
+    }
+
+  /* Write the sampling buffer (histogram).  */
+  write_histogram (fd);
+
+  /* Write the call graph.  */
+  write_callgraph (fd);
+
+  close (fd);
+}
+
+void
+__monstartup (void)
+{
+  s_lowpc =
+    ROUNDDOWN ((uintptr_t) & _start, HISTFRACTION * sizeof (HISTCOUNTER));
+  s_highpc =
+    ROUNDUP ((uintptr_t) & _etext, HISTFRACTION * sizeof (HISTCOUNTER));
+  s_textsize = s_highpc - s_lowpc;
+
+  hist_size = s_textsize / HISTFRACTION * sizeof (HISTCOUNTER);
+
+  /* Allocate froms.  */
+  froms = malloc_ea (s_textsize / HASHFRACTION);
+  if (froms == NULL)
+    {
+      fprintf (stderr, "Cannot allocate ea memory for the froms array.\n");
+      return;
+    }
+  memset_ea (froms, 0, s_textsize / HASHFRACTION);
+
+  /* Determine tolimit.  */
+  tolimit = s_textsize * ARCDENSITY / 100;
+  if (tolimit < MINARCS)
+    tolimit = MINARCS;
+
+  /* Allocate tos. */
+  tos = malloc_ea (tolimit * sizeof (struct tostruct));
+  if (tos == NULL)
+    {
+      fprintf (stderr, "Cannot allocate ea memory for the tos array.\n");
+      return;
+    }
+  memset_ea (tos, 0, tolimit * sizeof (struct tostruct));
+
+  /* Open the gmon.out file.  */
+  fd = open ("gmon.out", O_RDWR | O_CREAT | O_TRUNC, 0644);
+  if (fd == -1)
+    {
+      perror ("can't open gmon.out file");
+      return;
+    }
+  /* Truncate the file up to the size where the histogram fits in.  */
+  if (ftruncate (fd,
+       sizeof (struct gmon_hdr) + 1 + sizeof (struct gmon_hist_hdr) + hist_size) ==
+       -1)
+    perror ("can't truncate the gmon.out file");
+
+  /* Start the histogram sampler.  */
+  spu_slih_register (MFC_DECREMENTER_EVENT, spu_clock_slih);
+  timer_id = spu_timer_alloc (spu_timebase () / SAMPLE_INTERVAL,
+                              __sample);
+  spu_clock_start ();
+  spu_timer_start (timer_id);
+
+  atexit (__mcleanup);
+}
+
+void
+__mcount_internal (uintptr_t frompc, uintptr_t selfpc)
+{
+  /* sefpc: the address of the function just entered.  */
+  /* frompc: the caller of the function just entered.  */
+  unsigned int mach_stat;
+  __ea unsigned short *frompcindex;
+  unsigned short toindex;
+  __ea struct tostruct *top;
+  __ea struct tostruct *prevtop;
+
+  /* Save current state and disable interrupts.  */
+  mach_stat = spu_readch(SPU_RdMachStat);
+  spu_idisable ();
+
+  /* Sanity checks.  */
+  if (frompc < s_lowpc || frompc > s_highpc)
+    goto done;
+  frompc -= s_lowpc;
+  if (frompc > s_textsize)
+    goto done;
+
+  /* frompc indexes into the froms array the value at that position indexes
+     into the tos array.  */
+  frompcindex = &froms[(frompc) / (HASHFRACTION * sizeof (*froms))];
+  toindex = *frompcindex;
+  if (toindex == 0)
+    {
+      /* First time traversing this arc link of tos[0] incremented.  */
+      toindex = ++tos[0].link;
+      /* Sanity check.  */
+      if (toindex >= tolimit)
+       {
+         --tos[0].link;
+         goto done;
+       }
+      /* Save the index into the froms array for the next time we traverse this arc.  */
+      *frompcindex = toindex;
+      top = &tos[toindex];
+      /* Sets the address of the function just entered.  */
+      top->selfpc = selfpc;
+      top->count = 1;
+      top->link = 0;
+      goto done;
+    }
+
+  /* toindex points to a tostruct */
+  top = &tos[toindex];
+  if (top->selfpc == selfpc)
+    {
+      /* The arc is at front of the chain. This is the most common case.  */
+      top->count++;
+      goto done;
+    }
+
+  /* top->selfpc != selfpc
+     The pc we have got is not the pc we already stored (i.e. multiple function
+     calls to the same fuction within a function. The arc is not at front of
+     the chain.  */
+  for (;;)
+    {
+      if (top->link == 0)
+       {
+         /* We are at the end of the chain and selfpc was not found. Thus we create
+            a new tostruct and link it to the head of the chain.  */
+         toindex = ++tos[0].link;
+         /* Sanity check.  */
+         if (toindex >= tolimit)
+           {
+             --tos[0].link;
+             goto done;
+           }
+         top = &tos[toindex];
+         top->selfpc = selfpc;
+         top->count = 1;
+         /* Link back to the old tos entry.  */
+         top->link = *frompcindex;
+         /* Store a link to the new top in the froms array which makes the
+            current tos head of the chain.  */
+         *frompcindex = toindex;
+         goto done;
+       }
+      else
+       {
+         /* Otherwise check the next arc on the chain.  */
+         prevtop = top;
+         top = &tos[top->link];
+         if (top->selfpc == selfpc)
+           {
+             /* selfpc matches; increment its count.  */
+             top->count++;
+             /* Move it to the head of the chain.  */
+             /* Save previous tos index.  */
+             toindex = prevtop->link;
+             /* Link the former to to the current tos.  */
+             prevtop->link = top->link;
+             /* Link back to the old tos entry.  */
+             top->link = *frompcindex;
+             /* Store a link to the new top in the froms array which makes the
+                current tos head of the chain.  */
+             *frompcindex = toindex;
+             goto done;
+           }
+       }
+    }
+done:
+  /* Enable interrupts if necessary.  */
+  if (__builtin_expect (mach_stat & 1, 0))
+    spu_ienable ();
+}
diff --git a/newlib/libc/machine/spu/spu-mcount.S b/newlib/libc/machine/spu/spu-mcount.S
new file mode 100644 (file)
index 0000000..624dc76
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+(C) Copyright IBM Corp. 2008
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+* Neither the name of IBM nor the names of its contributors may be
+used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+Author: Ken Werner <ken.werner@de.ibm.com>
+*/
+
+/* _mcount extracts the address of the function just entered and the address
+    of the caller of that function and then calls __mcount_internal. The
+    prologue calls mcount without saving any registers. The return address is
+    stored in $75. The _mcount function has to:
+     - create a new stack frame
+     - save registers $2 to $75 on the stack
+     - copy the two addresses ($0 and $75) into the argument registers $3 and $4
+     - call __mcount_internal
+     - restore registers
+     - return to $75  */
+
+/* The following two convenience macros assist in the coding of the
+   saving and restoring the register.
+
+   saveregs     first, last    Saves registers from first to the last.
+   restoreregs  first, last    Restores registers from last down to first.
+
+   Note:       first must be less than or equal to last.  */
+.macro  saveregs        first, last
+        stqd            $\first, \first*16($SP)
+.if     \last-\first
+        saveregs        "(\first+1)",\last
+.endif
+.endm
+
+.macro  restoreregs     first, last
+        lqd             $\last, \last*16($SP)
+.if     \last-\first
+        restoreregs     \first,"(\last-1)"
+.endif
+.endm
+
+/* _mcount needs to be resident since the overlay manager uses the scratch
+   registers too.  */
+.text
+  .align 3 /* 8 byte alignment.  */
+  .global _mcount
+  .type _mcount, @function
+
+_mcount:
+  stqd $lr, 16($sp)    /* Save link register in the callers stack frame.  */
+  stqd $lr, -1216($sp) /* Store back pointer.  */
+  il   $lr, -1216      /* Push a new stack frame.  */
+  a    $sp, $sp, $lr   /* Frame size: 16 * (74 + 2) = 1216.  */
+
+  /* Save registers $2 to $75 on the stack.  */
+  saveregs 2, 75
+
+  /* Bring the __mcount_internal arguments in place.  */
+  lqd $3, 1232($sp) /* frompc (the link register).  */
+  ori $4, $75, 0    /* selfpc (the gcc prologue puts "brsl $75, _mcount" in
+                       front of every function).  */
+  brsl  $lr, __mcount_internal
+
+  /* Restore register $2 to $75 from the stack.  */
+  restoreregs 2, 75
+
+  il   $lr, 1216
+  a    $sp, $sp, $lr   /* Pop the stack frame.  */
+  lqd  $lr, 16($sp)    /* Restore link register.  */
+  bi   $75             /* Branch to the called function.  */