]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
* config/spu/mfc_tag_release.c: New file.
authorbje <bje@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 20 Nov 2007 05:12:09 +0000 (05:12 +0000)
committerbje <bje@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 20 Nov 2007 05:12:09 +0000 (05:12 +0000)
* config/spu/mfc_multi_tag_release.c: Likewise.
* config/spu/mfc_multi_tag_reserve.c: Likewise.
* config/spu/mfc_tag_release.c: Likewise.
* config/spu/mfc_tag_reserve.c: Likewise.
* config/spu/mfc_tag_table.c: Likewise.
* config/spu/t-spu-elf (LIB2FUNCS_STATIC_EXTRA): Compile them.
(TARGET_LIBGCC2_CFLAGS): Define __IN_LIBGCC2.
* config/spu/spu_mfcio.h (uint64_t): Typedef if __IN_LIBGCC2
is defined and avoid <stdint.h>.
(MFC_TAG_INVALID, MFC_TAG_VALID): New macros.
(mfc_tag_reserve, mfc_tag_release, mfc_multi_tag_reserve,
mfc_multi_tag_release): Likewise.
(__mfc_tag_reserve, __mfc_tag_release, __mfc_multi_tag_reserve,
__mfc_multi_tag_release): Declare.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@130306 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/config/spu/mfc_multi_tag_release.c [new file with mode: 0644]
gcc/config/spu/mfc_multi_tag_reserve.c [new file with mode: 0644]
gcc/config/spu/mfc_tag_release.c [new file with mode: 0644]
gcc/config/spu/mfc_tag_reserve.c [new file with mode: 0644]
gcc/config/spu/mfc_tag_table.c [new file with mode: 0644]
gcc/config/spu/spu_mfcio.h
gcc/config/spu/t-spu-elf
gcc/testsuite/gcc.target/spu/tag_manager.c [new file with mode: 0644]

index f3f9f3ff87e328d8b0aa51dd2fb78ba90a2324ea..12c6945f998bde2b3231522f2a3e8c889c149bb1 100644 (file)
@@ -1,3 +1,22 @@
+2007-11-20  Ulrich Weigand  <Ulrich.Weigand@de.ibm.com>
+           Ben Elliston  <bje@au.ibm.com>
+
+       * config/spu/mfc_tag_release.c: New file.
+       * config/spu/mfc_multi_tag_release.c: Likewise.
+       * config/spu/mfc_multi_tag_reserve.c: Likewise.
+       * config/spu/mfc_tag_release.c: Likewise.
+       * config/spu/mfc_tag_reserve.c: Likewise.
+       * config/spu/mfc_tag_table.c: Likewise.
+       * config/spu/t-spu-elf (LIB2FUNCS_STATIC_EXTRA): Compile them.
+       (TARGET_LIBGCC2_CFLAGS): Define __IN_LIBGCC2.
+       * config/spu/spu_mfcio.h: Avoid <stdint.h>.
+       (uint64_t): Typedef if __IN_LIBGCC2 is defined.
+       (MFC_TAG_INVALID, MFC_TAG_VALID): New macros.
+       (mfc_tag_reserve, mfc_tag_release, mfc_multi_tag_reserve,
+       mfc_multi_tag_release): Likewise.
+       (__mfc_tag_reserve, __mfc_tag_release, __mfc_multi_tag_reserve,
+       __mfc_multi_tag_release): Declare.
+
 2007-11-19  Jason Merrill  <jason@redhat.com>
 
        PR debug/28834, debug/29436, c/32326
diff --git a/gcc/config/spu/mfc_multi_tag_release.c b/gcc/config/spu/mfc_multi_tag_release.c
new file mode 100644 (file)
index 0000000..a809784
--- /dev/null
@@ -0,0 +1,77 @@
+/* Copyright (C) 2007 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file.  (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING.  If not, write to the Free
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.  */
+
+#include <spu_mfcio.h>
+extern vector unsigned int __mfc_tag_table;
+
+/* Release a sequential group of tags from exclusive use. The sequential
+   group of tags is the range starting from <first_tag> through
+   <first_tag>+<number_of_tags>-1. Upon sucessful release, MFC_DMA_TAG_VALID
+   is returned and the tags become available for future reservation.
+
+   If the specified tags were not previously reserved, no action is
+   taken and MFC_DMA_TAG_INVALID is returned.  */
+
+unsigned int
+__mfc_multi_tag_release (unsigned int first_tag, unsigned int number_of_tags)
+{
+  vector unsigned int table_copy, tmp, tmp1;
+  vector unsigned int one = (vector unsigned int)
+        { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+  vector unsigned int is_invalid;
+  unsigned int last_tag;
+  vector unsigned int has_been_reserved;
+
+  last_tag = first_tag + number_of_tags;
+
+  table_copy = spu_sl (one, number_of_tags);
+  table_copy = spu_rl (table_copy, -last_tag);
+  table_copy = spu_xor (table_copy, -1);
+
+  /* Make sure the tags are in range and valid.  */
+  tmp = spu_cmpgt (spu_promote(last_tag, 0), 32);
+  tmp1 = spu_cmpgt (spu_promote(number_of_tags, 0), 32);
+  is_invalid =  spu_cmpgt (spu_promote(first_tag, 0), 31);
+
+  /* All bits are set to 1 if invalid, 0 if valid.  */
+  is_invalid = spu_or (tmp, is_invalid);
+  is_invalid = spu_or (tmp1, is_invalid);
+
+  /* check whether these tags have been reserved */
+  tmp = spu_rlmask (one, (int)-number_of_tags);
+  tmp1 = spu_sl (__mfc_tag_table, first_tag);
+  has_been_reserved = spu_cmpgt(tmp1, tmp);
+
+  is_invalid = spu_or (has_been_reserved, is_invalid);
+
+  table_copy = spu_sel (__mfc_tag_table, table_copy, table_copy);
+  __mfc_tag_table = spu_sel (table_copy, __mfc_tag_table, is_invalid);
+
+  return spu_extract (is_invalid, 0);
+}
+
diff --git a/gcc/config/spu/mfc_multi_tag_reserve.c b/gcc/config/spu/mfc_multi_tag_reserve.c
new file mode 100644 (file)
index 0000000..ce3b24d
--- /dev/null
@@ -0,0 +1,89 @@
+/* Copyright (C) 2007 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file.  (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING.  If not, write to the Free
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.  */
+
+#include <spu_mfcio.h>
+extern vector unsigned int __mfc_tag_table;
+
+/* Reserve a sequential group of tags for exclusive use.  The number of
+   tags to be reserved is specified by the <number_of_tags> parameter.
+   This routine returns the first tag ID for a sequential list of
+   available tags and marks them as reserved. The reserved group
+   of tags is in the range starting from the returned tag through
+   the returned tag + <number_of_tags>-1.
+
+   If the number of tags requested exceeds the number of available
+   sequential tags, then MFC_DMA_TAG_INVALID is returned indicating
+   that the request could not be serviced.  */
+
+unsigned int
+__mfc_multi_tag_reserve (unsigned int number_of_tags)
+{
+  vector unsigned int table_copy;
+  vector unsigned int one = (vector unsigned int)
+        { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+  vector unsigned int count_busy, is_valid;
+  vector unsigned int count_total;
+  vector unsigned int count_avail = (vector unsigned int) { 0, 0, 0, 0 };
+  vector unsigned int index = (vector unsigned int) { 0, 0, 0, 0 };
+
+  table_copy = __mfc_tag_table;
+
+
+  /* count_busy: number of consecutive busy tags
+     count_avail: number of consecutive free tags
+     table_copy: temporary copy of the tag table
+     count_total: sum of count_busy and count_avail
+     index: index of the current working tag  */
+  do
+    {
+      table_copy = spu_sl (table_copy, count_avail);
+
+      count_busy = spu_cntlz (table_copy);
+      table_copy = spu_sl (table_copy, count_busy);
+      count_avail = spu_cntlz (spu_xor(table_copy, -1));
+      count_total = spu_add (count_busy, count_avail);
+      index = spu_add (index, count_total);
+    }
+  while (spu_extract (count_avail, 0) < number_of_tags
+        && spu_extract (table_copy, 0) != 0);
+
+  index = spu_sub (index, count_avail);
+
+  /* is_valid is set to 0xFFFFFFFF if table_copy == 0, 0 otherwise.  */
+  is_valid = spu_cmpeq (table_copy, 0);
+  index = spu_sel (index, is_valid, is_valid);
+
+  /* Now I need to actually mark the tags as used.  */
+  table_copy = spu_sl (one, number_of_tags);
+  table_copy = spu_rl (table_copy, -number_of_tags - spu_extract (index, 0));
+  table_copy = spu_sel (table_copy, __mfc_tag_table, table_copy);
+  __mfc_tag_table = spu_sel (table_copy, __mfc_tag_table, is_valid);
+
+  return spu_extract (index, 0);
+}
+
diff --git a/gcc/config/spu/mfc_tag_release.c b/gcc/config/spu/mfc_tag_release.c
new file mode 100644 (file)
index 0000000..0585a3f
--- /dev/null
@@ -0,0 +1,64 @@
+/* Copyright (C) 2007 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file.  (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING.  If not, write to the Free
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.  */
+
+#include <spu_mfcio.h>
+extern vector unsigned int __mfc_tag_table;
+
+/* Release the specified DMA tag from exclusive use.  Once released, the
+   tag is available for future reservation.  Upon sucessful release,
+   MFC_DMA_TAG_VALID is returned.  If the specified tag is not in the
+   range 0 to 31, or had not been reserved, no action is taken and
+   MFC_DMA_TAG_INVALID is returned.  */
+
+unsigned int
+__mfc_tag_release (unsigned int tag)
+{
+  vector unsigned int is_invalid;
+  vector unsigned int mask = (vector unsigned int)
+       { 0x80000000, 0x80000000, 0x80000000, 0x80000000 };
+  vector signed int zero = (vector signed int) { 0, 0, 0, 0 };
+
+  vector signed int has_been_reserved;
+
+  /* Check if the tag is out of range.  */
+  is_invalid = spu_cmpgt (spu_promote (tag, 0), 31);
+
+  /* Check whether the tag has been reserved, set to all 1 if has not
+     been reserved, 0 otherwise.  */
+  has_been_reserved = (vector signed int) spu_rl (__mfc_tag_table, tag);
+  has_been_reserved = (vector signed int) spu_cmpgt (zero, has_been_reserved);
+
+  /* Set invalid.  */
+  is_invalid = spu_or ((vector unsigned int) has_been_reserved, is_invalid);
+
+  mask = spu_rlmask (mask, (int)(-tag));
+  __mfc_tag_table = spu_or (__mfc_tag_table, mask);
+
+  return spu_extract(is_invalid, 0);
+}
+
diff --git a/gcc/config/spu/mfc_tag_reserve.c b/gcc/config/spu/mfc_tag_reserve.c
new file mode 100644 (file)
index 0000000..cad2810
--- /dev/null
@@ -0,0 +1,56 @@
+/* Copyright (C) 2007 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file.  (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING.  If not, write to the Free
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.  */
+
+#include <spu_mfcio.h>
+extern vector unsigned int __mfc_tag_table;
+
+/* Reserves a DMA tag for exclusive use.  This routine returns an available
+   tag id in the range 0 to 31 and marks the tag as reserved.  If no tags
+   are available, MFC_DMA_TAG_INVALID is returned indicating that all tags
+   are already reserved.  */
+
+unsigned int
+__mfc_tag_reserve (void)
+{
+  vector unsigned int mask = (vector unsigned int)
+       { 0x80000000, 0x80000000, 0x80000000, 0x80000000 };
+  vector unsigned int count_zeros, is_valid;
+  vector signed int count_neg;
+
+  count_zeros = spu_cntlz (__mfc_tag_table);
+  count_neg = spu_sub (0, (vector signed int) count_zeros);
+
+  mask = spu_rlmask (mask, (vector signed int) count_neg);
+  __mfc_tag_table = spu_andc (__mfc_tag_table, mask);
+
+  is_valid = spu_cmpeq (count_zeros, 32);
+  count_zeros = spu_sel (count_zeros, is_valid, is_valid);
+
+  return spu_extract (count_zeros, 0);
+}
+
diff --git a/gcc/config/spu/mfc_tag_table.c b/gcc/config/spu/mfc_tag_table.c
new file mode 100644 (file)
index 0000000..aea8ffd
--- /dev/null
@@ -0,0 +1,45 @@
+/* Copyright (C) 2007 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file.  (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING.  If not, write to the Free
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.  */
+
+
+/* The free tag table used by the MFC tag manager, with tag0
+   reserved for the overlay manager.  */
+__vector unsigned int
+__mfc_tag_table = (__vector unsigned int) { 0x7FFFFFFF, -1, -1, -1 };
+
+/* Arrange to release tag0 if overlays are not present.  */
+static void __mfc_tag_init (void) __attribute__ ((constructor));
+
+static void
+__mfc_tag_init (void)
+{
+  extern void _ovly_table __attribute__ ((weak));
+
+  if (&_ovly_table == 0)
+    __mfc_tag_table = (__vector unsigned int) { -1, -1, -1, -1 };
+}
index bd651912306f84e290dcc064e892a62c443310d1..e32132bc12cda336961181ec52a7934179ee0264 100644 (file)
 #define __SPU_MFCIO_H__ 1
 
 #include <spu_intrinsics.h>
+#ifdef __IN_LIBGCC2
+unsigned long long uint64_t;
+#else
 #include <stdint.h>
+#endif
 
 
 /****************************************************************/
@@ -267,4 +271,24 @@ typedef struct mfc_list_element {
 #define spu_write_srr0(srr0)          spu_writech(SPU_WrSRR0,srr0)
 #define spu_read_srr0()               spu_readch(SPU_RdSRR0)
 
+
+/* MFC Tag Manager */
+
+#define MFC_TAG_INVALID 0xFFFFFFFF
+#define MFC_TAG_VALID   0x00000000
+
+#define mfc_tag_reserve() \
+       __mfc_tag_reserve()
+#define mfc_tag_release(tag) \
+       __mfc_tag_release((tag))
+#define mfc_multi_tag_reserve(nr_tags) \
+       __mfc_multi_tag_reserve((nr_tags))
+#define mfc_multi_tag_release(tag, nr_tags) \
+       __mfc_multi_tag_release((tag),(nr_tags))
+
+extern unsigned int __mfc_tag_reserve (void);
+extern unsigned int __mfc_tag_release (unsigned int);
+extern unsigned int __mfc_multi_tag_reserve (unsigned int);
+extern unsigned int __mfc_multi_tag_release (unsigned int, unsigned int);
+
 #endif /* __SPU_MFCIO_H__ */
index 43fdd8f7fb3145e8fced5d88fdcc6f0a5fc4ee35..b9550a3d082f9da937eceb7a857a82809f23b314 100644 (file)
@@ -21,10 +21,15 @@ CROSS_LIBGCC1 =
 
 # On SPU __word__ is TImode which is too inefficient and incomplete for
 # implementing libgcc routines.
-TARGET_LIBGCC2_CFLAGS = -fPIC -D__word__=SI -mwarn-reloc
+TARGET_LIBGCC2_CFLAGS = -fPIC -D__word__=SI -mwarn-reloc -D__IN_LIBGCC2
 
 LIB2FUNCS_STATIC_EXTRA = $(srcdir)/config/spu/float_unssidf.c \
-                        $(srcdir)/config/spu/float_unsdidf.c
+                        $(srcdir)/config/spu/float_unsdidf.c \
+                        $(srcdir)/config/spu/mfc_tag_table.c \
+                        $(srcdir)/config/spu/mfc_tag_reserve.c \
+                        $(srcdir)/config/spu/mfc_tag_release.c \
+                        $(srcdir)/config/spu/mfc_multi_tag_reserve.c \
+                        $(srcdir)/config/spu/mfc_multi_tag_release.c
 
 LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde.c \
    $(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c
diff --git a/gcc/testsuite/gcc.target/spu/tag_manager.c b/gcc/testsuite/gcc.target/spu/tag_manager.c
new file mode 100644 (file)
index 0000000..a90e37e
--- /dev/null
@@ -0,0 +1,312 @@
+/* Copyright (C) 2007 Free Software Foundation, Inc.
+
+   This file is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 2 of the License, or (at your option)
+   any later version.
+
+   This file is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this file; see the file COPYING.  If not, write to the Free
+   Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+/* { dg-do run } */
+
+#include <spu_mfcio.h>
+
+/* This test directly accesses the internal table used
+   by the MFC tag manager.  */
+extern vector unsigned int __mfc_tag_table;
+
+
+/* This tag tests invalid tag release.  Invalid tag release does
+   nothing to the tag table.  */
+void
+test_tag_release01 (void)
+{
+  unsigned int copy;
+  copy = spu_extract (__mfc_tag_table, 0);
+
+  mfc_tag_release (35);
+  if (copy != spu_extract (__mfc_tag_table, 0))
+    abort ();
+}
+
+/* More invalid release tests.  */
+void
+test_tag_release_invalid (void)
+{
+  unsigned int copy;
+  copy = spu_extract (__mfc_tag_table, 0);
+
+  if (mfc_tag_release (32) != MFC_TAG_INVALID)
+    abort ();
+  if (copy != spu_extract (__mfc_tag_table, 0))
+    abort ();
+
+  if (mfc_tag_release (17) != MFC_TAG_INVALID)
+    abort ();
+  if (copy != spu_extract (__mfc_tag_table, 0))
+    abort ();
+}
+
+/* Invalid multiple-tag release tests.  */
+void
+test_tag_group_release_invalid (void)
+{
+  unsigned int copy;
+  copy = spu_extract (__mfc_tag_table, 0);
+
+  if (mfc_multi_tag_release (32, 10) != MFC_TAG_INVALID)
+    abort ();
+  if (copy != spu_extract (__mfc_tag_table, 0))
+    abort ();
+
+  if (mfc_multi_tag_release (28, 10) != MFC_TAG_INVALID)
+    abort ();
+  if (copy != spu_extract (__mfc_tag_table, 0))
+    abort ();
+
+  if (mfc_multi_tag_release (17, 10) != MFC_TAG_INVALID)
+    abort ();
+  if (copy != spu_extract (__mfc_tag_table, 0))
+    abort ();
+
+  if (mfc_multi_tag_release (32, 10) != MFC_TAG_INVALID)
+    abort ();
+  if (copy != spu_extract (__mfc_tag_table, 0))
+    abort ();
+}
+
+/* The tag table should be in a pristine mode to run this test.  */
+void
+test_tag_reserve01 (void)
+{
+  unsigned int correct_table[32] =
+    {
+                 0x80000000, 0xC0000000, 0xE0000000,
+      0xF0000000, 0xF8000000, 0xFC000000, 0xFE000000,
+      0xFF000000, 0xFF800000, 0xFFC00000, 0xFFE00000,
+      0xFFF00000, 0xFFF80000, 0xFFFC0000, 0xFFFE0000,
+      0xFFFF0000, 0xFFFF8000, 0xFFFFC000, 0xFFFFE000,
+      0xFFFFF000, 0xFFFFF800, 0xFFFFFC00, 0xFFFFFE00,
+      0xFFFFFF00, 0xFFFFFF80, 0xFFFFFFC0, 0xFFFFFFE0,
+      0xFFFFFFF0, 0xFFFFFFF8, 0xFFFFFFFC, 0xFFFFFFFE,
+      0xFFFFFFFF
+    };
+
+  unsigned int tag;
+  unsigned int i;
+
+  for (i = 0; i < 32; i++)
+    {
+      tag = mfc_tag_reserve ();
+      if (tag != i)
+       abort ();
+    }
+
+  for (i = 0; i < 32; i++)
+    {
+      tag = mfc_tag_reserve ();
+      if (tag != MFC_TAG_INVALID)
+       abort ();
+    }
+
+  for (i = 0; i < 32; i++)
+    {
+      mfc_tag_release (i);
+      if (spu_extract (__mfc_tag_table, 0) != correct_table[i])
+       abort ();
+    }
+}
+
+/* The tag table should be in a pristine mode to run this test.  */
+void
+test_tag_reserve02 (void)
+{
+  unsigned int correct_table[32] =
+    {
+      0x80000000, 0xC0000000, 0xA0000000, 0xF0000000,
+      0xA8000000, 0xFC000000, 0xAA000000, 0xFF000000,
+      0xAA800000, 0xFFC00000, 0xAAA00000, 0xFFF00000,
+      0xAAA80000, 0xFFFC0000, 0xAAAA0000, 0xFFFF0000,
+      0xAAAA8000, 0xFFFFC000, 0xAAAAA000, 0xFFFFF000,
+      0xAAAAA800, 0xFFFFFC00, 0xAAAAAA00, 0xFFFFFF00,
+      0xAAAAAA80, 0xFFFFFFC0, 0xAAAAAAA0, 0xFFFFFFF0,
+      0xAAAAAAA8, 0xFFFFFFFC, 0xAAAAAAAA, 0xFFFFFFFF
+    };
+
+  unsigned int correct_table2[32] =
+    {
+      0x80000000, 0xEAAAAAAA, 0xA0000000, 0xFAAAAAAA,
+      0xA8000000, 0xFEAAAAAA, 0xAA000000, 0xFFAAAAAA,
+      0xAA800000, 0xFFEAAAAA, 0xAAA00000, 0xFFFAAAAA,
+      0xAAA80000, 0xFFFEAAAA, 0xAAAA0000, 0xFFFFAAAA,
+      0xAAAA8000, 0xFFFFEAAA, 0xAAAAA000, 0xFFFFFAAA,
+      0xAAAAA800, 0xFFFFFEAA, 0xAAAAAA00, 0xFFFFFFAA,
+      0xAAAAAA80, 0xFFFFFFEA, 0xAAAAAAA0, 0xFFFFFFFA,
+      0xAAAAAAA8, 0xFFFFFFFE, 0xAAAAAAAA, 0xFFFFFFFF
+    };
+
+  unsigned int tag;
+  unsigned int i;
+
+  /* Reserve all 32 tags.  */
+  for (i = 0; i < 32; i++)
+    {
+      tag = mfc_tag_reserve();
+      if (tag != i)
+       abort ();
+    }
+
+  for (i = 0; i < 32; i++)
+    {
+      tag = mfc_tag_reserve();
+      if (tag != MFC_TAG_INVALID)
+       abort ();
+    }
+
+  /* Release only 16 tags with a stride of 2.  */
+  for (i = 0; i < 32; i += 2)
+    {
+      mfc_tag_release (i);
+      if (spu_extract (__mfc_tag_table, 0) != correct_table[i])
+       abort ();
+    }
+
+  /* Release the other 16 tags with a stride of 2.  */
+  for (i = 1; i < 32; i += 2)
+    {
+      mfc_tag_release (i);
+      if (spu_extract (__mfc_tag_table, 0) != correct_table2[i])
+       abort ();
+    }
+}
+
+/* The tag table should be in a pristine mode to run this test.  */
+void
+test_tag_reserve03 (void)
+{
+  unsigned int tag;
+  unsigned int i;
+
+  /* Reserve all 32 tags.  */
+  for (i = 0; i < 32; i++)
+    {
+      tag = mfc_tag_reserve ();
+      if (tag != i)
+       abort ();
+    }
+
+  for (i = 0; i < 32; i++)
+    {
+      tag = mfc_tag_reserve ();
+      if (tag != MFC_TAG_INVALID)
+       abort ();
+    }
+
+  /* Release only 16 tags with a stride of 2.  */
+  for (i = 0; i < 32; i += 2)
+    mfc_tag_release (i);
+
+  /* Now let's re-reserve those tags.  */
+  for (i = 0; i < 32; i += 2)
+    {
+      tag = mfc_tag_reserve ();
+      if (tag != i)
+       abort ();
+    }
+
+  /* Release all tags.  */
+  for (i = 0; i < 32; i++)
+    mfc_tag_release (i);
+
+  if (spu_extract (__mfc_tag_table,0) != 0xFFFFFFFF)
+    abort ();
+}
+
+
+void
+test_tag_group_reserve (void)
+{
+  unsigned int tag;
+  unsigned int i;
+  unsigned int copy;
+
+  /* Reserve all tags.  */
+  for (i = 0; i < 32; i++)
+    mfc_tag_reserve();
+
+  /* Release the first 4. */
+  for (i = 0; i < 4; i++)
+    mfc_tag_release (i);
+
+  /* Release tag 5 to 7.  */
+  for (i = 5; i < 8; i++)
+    mfc_tag_release (i);
+
+  /* Release tag 9 to 19.  */
+  for (i = 9; i < 20; i++)
+    mfc_tag_release (i);
+
+  /* Tag table should be 0xF77FF000.  */
+  if (spu_extract (__mfc_tag_table, 0) != 0xF77FF000)
+    abort ();
+
+
+  /* Verify invalid release is detected.  */
+  copy = spu_extract (__mfc_tag_table, 0);
+  if (mfc_multi_tag_release (1, 5) != MFC_TAG_INVALID)
+    abort ();
+  if (copy != spu_extract (__mfc_tag_table, 0))
+    abort ();
+
+
+  /* Reserve multiple tags.  */
+  tag = mfc_multi_tag_reserve (5);
+  if (tag != 9)
+    abort ();
+
+  /* Tag table should be 0xF703F000.  */
+  if (spu_extract (__mfc_tag_table, 0) != 0xF703F000)
+    abort ();
+
+
+  /* Release 5 tags in the group.  */
+  mfc_multi_tag_release (tag, 5);
+
+  /* Tag table should be 0xF77FF000.  */
+  if (spu_extract (__mfc_tag_table, 0) != 0xF77FF000)
+    abort ();
+
+
+  /* This call should not do anything.  */
+  mfc_multi_tag_release (32, 5);
+
+  /* Tag table should be 0xF77FF000.  */
+  if (spu_extract (__mfc_tag_table, 0) != 0xF77FF000)
+    abort ();
+}
+
+
+int
+main (void)
+{
+  test_tag_release01 ();
+  test_tag_release_invalid ();
+  test_tag_group_release_invalid ();
+
+  test_tag_reserve01 ();
+  test_tag_reserve02 ();
+  test_tag_reserve03 ();
+
+  test_tag_group_reserve ();
+
+  return 0;
+}
+