public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* PATCH: named address space support (2/2: SPU backend)
@ 2008-08-21  6:24 Ben Elliston
  2008-08-29  3:17 ` Trevor_Smigiel
  0 siblings, 1 reply; 7+ messages in thread
From: Ben Elliston @ 2008-08-21  6:24 UTC (permalink / raw)
  To: gcc-patches
  Cc: Trevor_Smigiel, andrew_pinski, David Edelsohn, Joseph S. Myers

This patch follows on from the target-independent patch I posted
yesterday at:

  http://gcc.gnu.org/ml/gcc-patches/2008-08/msg01353.html

The patch below uses the infrastructure introduced by yesterday's patch
to implement one named address space for the spu-elf target: __ea.  The
patch includes an implementation of a software-managed cache to improve
performance of programs accessing variables in the PPU address space.

Tested for no effects on powerpc-linux and x86_64-linux.  There are no
regressions on spu-elf, although the gcc.target/spu/cache.c test is
currently failing due to missing newlib support that will be committed
in the next week or so.  If there is a strong desire to do so, we can
xfail that test in the meantime.

The documentation changes were tested with "make info dvi" and visual
inspection of the resulting .dvi files.

Okay for mainline?

Thanks, Ben

	* config.gcc (spu-*-elf*): Add spu_cache.h to extra_headers.
	* config/spu/spu-c.c (spu_cpu_cpp_builtins): Define __EA32__ or
	__EA64__, depending on the ea pointer size.  *
	* config/spu/spu-elf.h (DRIVER_SELF_SPECS): Link the right
	gcc_cache library depending on the -mcache-size and
	-matomic-updates option given.
	(LIB_SPEC): Link gcc_cachemgr library.
	* config/spu/spu.c (struct spu_address_space): New.
	(spu_address_spaces): New table.
	(TARGET_ADDR_SPACE_POINTER_MODE): Define.
	(TARGET_ADDR_SPACE_NUMBER): Likewise.
	(TARGET_ADDR_SPACE_CONVERSION_RTL): Likewise.
	(TARGET_VALID_POINTER_MODE): Likewise.
	(TARGET_VALID_ADDR_SPACE): Likewise.
	(TARGET_ASM_UNALIGNED_DI_OP): Remove.
	(TARGET_ASM_ALIGNED_DI_OP): Define instead.
	(ea_symbol_ref): New.
	(spu_legitimate_constant_p): Reject __ea qualified references.
	(spu_legitimate_address): Keep __ea references until reload.
	(EAmode): Define.
	(cache_fetch, cache_fetch_dirty, ea_alias_set): New variables.
	(ea_load_store): New function.
	(ea_load_store_inline): Likewise.
	(expand_ea_mem): Likewise.
	(spu_expand_mov): Handle __ea memory operands.
	(spu_ea_pointer_mode): New function.
	(spu_valid_pointer_mode): Likewise.
	(spu_addr_space_name): Likewise.
	(spu_addr_space_conversion_rtl): Likewise.
	(spu_valid_addr_space): Likewise.
	(spu_addr_space_number): Likewise.
	* config/spu/spu.h (ASM_OUTPUT_SYMBOL_REF): New macro.
	* config/spu/spu.md (to_ea): New expander.
	(from_ea): Likewise.
	* config/spu/spu.opt (mea32, mea64): New options.
	* config/spu/spu_mfcio.h: New typedef.
	* config/spu/t-spu-elf (MULTILIB_OPTIONS): Add mea64.
	(EXTRA_MULTILIB_PARTS): Add cache libraries.
	(cachemgr.o, %/cachemgr.o): New targets.
	(cachemgr_nonatomic.o, %/cachemgr_nonatomic.o): Likewise.
	(libgcc_%.a, %/libgcc_%.a): Likewise.
	(cache8k.o, cache16k.o, cache32k.o, etc): Likewise.
	(%/cache8k.o, %/cache16k.o, %/cache32k.o, etc): Likewise.
	* config/spu/cache.S: New file.
	* config/spu/cachemgr.c: Likewise.
	* config/spu/spu_cache.h: Likewise.
	* doc/invoke.texi (SPU Options): Document -mea32, -mea64,
	-mcache-size and -matomic-updates options.

testsuite/
	* gcc.target/spu/cache.c: New test.
	* gcc.target/spu/ea/ea.exp: New test driver.
	* gcc.target/spu/ea/cast1.c: New test.
	* gcc.target/spu/ea/compile.c: Likewise.
	* gcc.target/spu/ea/cppdefine32.c: Likewise.
	* gcc.target/spu/ea/cppdefine64.c: Likewise.
	* gcc.target/spu/ea/errors.c: Likewise.
	* gcc.target/spu/ea/options1.c: Likewise.


--- gcc-clean/gcc/config.gcc	2008-08-19 20:13:33.000000000 +1000
+++ gcc-nas/gcc/config.gcc	2008-08-19 20:49:53.000000000 +1000
@@ -2277,7 +2277,7 @@ sparc64-*-netbsd*)
 spu-*-elf*)
 	tm_file="dbxelf.h elfos.h spu/spu-elf.h spu/spu.h"
 	tmake_file="spu/t-spu-elf"
-	extra_headers="spu_intrinsics.h spu_internals.h vmx2spu.h spu_mfcio.h vec_types.h"
+	extra_headers="spu_intrinsics.h spu_internals.h vmx2spu.h spu_mfcio.h vec_types.h spu_cache.h"
 	extra_modes=spu/spu-modes.def
 	c_target_objs="${c_target_objs} spu-c.o"
 	cxx_target_objs="${cxx_target_objs} spu-c.o"
--- gcc-clean/gcc/config/spu/spu-c.c	2008-07-24 14:04:43.000000000 +1000
+++ gcc-nas/gcc/config/spu/spu-c.c	2008-07-25 10:28:15.000000000 +1000
@@ -198,6 +198,17 @@ spu_cpu_cpp_builtins (struct cpp_reader 
   if (spu_arch == PROCESSOR_CELLEDP)
     builtin_define_std ("__SPU_EDP__");
   builtin_define_std ("__vector=__attribute__((__spu_vector__))");
+  switch (spu_ea_model)
+    {
+    case 32:
+      builtin_define_std ("__EA32__");
+      break;
+    case 64:
+      builtin_define_std ("__EA64__");
+      break;
+    default:
+       gcc_unreachable ();
+    }
 
   if (!flag_iso)
     {
--- gcc-clean/gcc/config/spu/spu-elf.h	2008-03-12 15:21:52.000000000 +1100
+++ gcc-nas/gcc/config/spu/spu-elf.h	2008-04-04 12:13:40.000000000 +1100
@@ -49,10 +49,26 @@
 
 #define EH_FRAME_IN_DATA_SECTION 1
 
+#define DRIVER_SELF_SPECS "\
+  %{mcache-size=128   : -lgcc_cache128k ; \
+    mcache-size=64    : -lgcc_cache64k ; \
+    mcache-size=32    : -lgcc_cache32k ; \
+    mcache-size=16    : -lgcc_cache16k ; \
+    mcache-size=8     : -lgcc_cache8k ; \
+    		      : -lgcc_cache64k } \
+  %<mcache-size=* \
+  %{mno-atomic-updates:-lgcc_cachemgr_nonatomic; :-lgcc_cachemgr} \
+  %<matomic-updates %<mno-atomic-updates"
+
 #define LINK_SPEC "%{mlarge-mem: --defsym __stack=0xfffffff0 }"
 
-#define LIB_SPEC \
-	"-( %{!shared:%{g*:-lg}} -lc -lgloss -)"
+/* Match each of the mutually exclusive cache<n>k libraries because
+   lgcc_cache* did not seem to work -- perhaps a bug in the specs
+   handling?  */
+#define LIB_SPEC "-( %{!shared:%{g*:-lg}} -lc -lgloss -) \
+    %{lgcc_cachemgr*:-lgcc_cachemgr%*} \
+    %{lgcc_cache128k} %{lgcc_cache64k} %{lgcc_cache32k} \
+    %{lgcc_cache16k} %{lgcc_cache8k}"
 
 /* Turn off warnings in the assembler too. */
 #undef ASM_SPEC
--- gcc-clean/gcc/config/spu/spu.c	2008-08-19 20:13:12.000000000 +1000
+++ gcc-nas/gcc/config/spu/spu.c	2008-08-20 14:34:08.000000000 +1000
@@ -61,6 +61,19 @@ struct spu_builtin_range
   int low, high;
 };
 
+struct spu_address_space
+{
+  const char *name;
+  rtx (*to_generic_insn) (rtx, rtx);
+  rtx (*from_generic_insn) (rtx, rtx);
+};
+
+static struct spu_address_space spu_address_spaces[] = {
+  {"generic", NULL, NULL },
+  {"__ea", gen_from_ea, gen_to_ea },
+  {NULL, NULL, NULL},
+};
+
 static struct spu_builtin_range spu_builtin_range[] = {
   {-0x40ll, 0x7fll},		/* SPU_BTI_7     */
   {-0x40ll, 0x3fll},		/* SPU_BTI_S7    */
@@ -189,6 +202,30 @@ tree spu_builtin_types[SPU_BTI_MAX];
 \f
 /*  TARGET overrides.  */
 
+static enum machine_mode spu_ea_pointer_mode (int);
+#undef TARGET_ADDR_SPACE_POINTER_MODE
+#define TARGET_ADDR_SPACE_POINTER_MODE spu_ea_pointer_mode
+
+static const char *spu_addr_space_name (int);
+#undef TARGET_ADDR_SPACE_NAME
+#define TARGET_ADDR_SPACE_NAME spu_addr_space_name
+
+static unsigned char spu_addr_space_number (const tree);
+#undef TARGET_ADDR_SPACE_NUMBER
+#define TARGET_ADDR_SPACE_NUMBER spu_addr_space_number
+
+static rtx (* spu_addr_space_conversion_rtl (int, int)) (rtx, rtx);
+#undef TARGET_ADDR_SPACE_CONVERSION_RTL
+#define TARGET_ADDR_SPACE_CONVERSION_RTL spu_addr_space_conversion_rtl
+
+static bool spu_valid_pointer_mode (enum machine_mode mode);
+#undef TARGET_VALID_POINTER_MODE
+#define TARGET_VALID_POINTER_MODE spu_valid_pointer_mode
+
+static bool spu_valid_addr_space (const tree);
+#undef TARGET_VALID_ADDR_SPACE
+#define TARGET_VALID_ADDR_SPACE spu_valid_addr_space
+
 #undef TARGET_INIT_BUILTINS
 #define TARGET_INIT_BUILTINS spu_init_builtins
 
@@ -198,10 +235,8 @@ tree spu_builtin_types[SPU_BTI_MAX];
 #undef TARGET_UNWIND_WORD_MODE
 #define TARGET_UNWIND_WORD_MODE spu_unwind_word_mode
 
-/* The .8byte directive doesn't seem to work well for a 32 bit
-   architecture. */
-#undef TARGET_ASM_UNALIGNED_DI_OP
-#define TARGET_ASM_UNALIGNED_DI_OP NULL
+#undef TARGET_ASM_ALIGNED_DI_OP
+#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t"
 
 #undef TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS spu_rtx_costs
@@ -2826,6 +2861,17 @@ arith_immediate_p (rtx op, enum machine_
   return val >= low && val <= high;
 }
 
+/* Return true if X is a SYMBOL_REF to an __ea qualified variable.  */
+
+static int
+ea_symbol_ref (rtx x)
+{
+  return (GET_CODE (x) == SYMBOL_REF
+	  && SYMBOL_REF_DECL (x)
+	  && TREE_CODE (SYMBOL_REF_DECL (x)) == VAR_DECL
+	  && TYPE_ADDR_SPACE (TREE_TYPE (SYMBOL_REF_DECL (x))));
+}
+
 /* We accept:
    - any 32-bit constant (SImode, SFmode)
    - any constant that can be generated with fsmbi (any mode)
@@ -2837,19 +2883,28 @@ spu_legitimate_constant_p (rtx x)
 {
   if (GET_CODE (x) == HIGH)
     x = XEXP (x, 0);
-  /* V4SI with all identical symbols is valid. */
-  if (!flag_pic
-      && GET_MODE (x) == V4SImode
-      && (GET_CODE (CONST_VECTOR_ELT (x, 0)) == SYMBOL_REF
-	  || GET_CODE (CONST_VECTOR_ELT (x, 0)) == LABEL_REF
-	  || GET_CODE (CONST_VECTOR_ELT (x, 0)) == CONST))
-    return CONST_VECTOR_ELT (x, 0) == CONST_VECTOR_ELT (x, 1)
-	   && CONST_VECTOR_ELT (x, 1) == CONST_VECTOR_ELT (x, 2)
-	   && CONST_VECTOR_ELT (x, 2) == CONST_VECTOR_ELT (x, 3);
 
-  if (GET_CODE (x) == CONST_VECTOR
-      && !const_vector_immediate_p (x))
+  /* Reject any __ea qualified reference.  These can't appear in
+     instructions but must be forced to the constant pool.  */
+  if (ea_symbol_ref (x))
     return 0;
+
+  if (GET_CODE (x) == CONST_VECTOR)
+    {
+      /* V4SI with all identical symbols is valid. */
+      if (GET_CODE (CONST_VECTOR_ELT (x, 0)) == SYMBOL_REF
+ 	  || GET_CODE (CONST_VECTOR_ELT (x, 0)) == LABEL_REF
+ 	  || GET_CODE (CONST_VECTOR_ELT (x, 0)) == CONST)
+ 	return (!flag_pic
+ 		&& GET_MODE (x) == V4SImode
+ 		&& CONST_VECTOR_ELT (x, 0) == CONST_VECTOR_ELT (x, 1)
+ 		&& CONST_VECTOR_ELT (x, 1) == CONST_VECTOR_ELT (x, 2)
+ 		&& CONST_VECTOR_ELT (x, 2) == CONST_VECTOR_ELT (x, 3)
+ 		&& !ea_symbol_ref (CONST_VECTOR_ELT (x, 0)));
+
+      if (!const_vector_immediate_p (x))
+	return 0;
+    }
   return 1;
 }
 
@@ -2871,10 +2926,16 @@ spu_legitimate_address (enum machine_mod
     x = XEXP (x, 0);
   switch (GET_CODE (x))
     {
-    case SYMBOL_REF:
     case LABEL_REF:
       return !TARGET_LARGE_MEM;
 
+    case SYMBOL_REF:
+      /* Keep __ea references until reload so that spu_expand_mov
+         can see them in MEMs.  */
+      if (ea_symbol_ref (x))
+        return !reload_in_progress && !reload_completed;
+      return !TARGET_LARGE_MEM;
+
     case CONST:
       if (!TARGET_LARGE_MEM && GET_CODE (XEXP (x, 0)) == PLUS)
 	{
@@ -3491,6 +3552,227 @@ store_with_one_insn_p (rtx mem)
   return 0;
 }
 
+#define EAmode (spu_ea_model != 32 ? DImode : SImode)
+
+rtx cache_fetch;
+rtx cache_fetch_dirty;
+int ea_alias_set = -1;
+
+/* MEM is known to be an __ea qualified memory access.  Emit a call to
+   fetch the ppu memory to local store, and return its address in local
+   store.  */
+
+static void
+ea_load_store (rtx mem, bool is_store, rtx ea_addr, rtx data_addr)
+{
+  if (is_store)
+    {
+      rtx ndirty = GEN_INT (GET_MODE_SIZE (GET_MODE (mem)));
+      if (!cache_fetch_dirty)
+	cache_fetch_dirty = init_one_libfunc ("__cache_fetch_dirty");
+      emit_library_call_value (cache_fetch_dirty, data_addr, LCT_NORMAL, Pmode,
+			       2, ea_addr, EAmode, ndirty, SImode);
+    }
+  else
+    {
+      if (!cache_fetch)
+	cache_fetch = init_one_libfunc ("__cache_fetch");
+      emit_library_call_value (cache_fetch, data_addr, LCT_NORMAL, Pmode,
+			       1, ea_addr, EAmode);
+    }
+}
+
+/* Like ea_load_store, but do the cache tag comparison and, for stores,
+   dirty bit marking, inline.
+
+   The cache control data structure is an array of
+
+   struct __cache_tag_array
+     {
+        unsigned int tag_lo[4];
+        unsigned int tag_hi[4];
+        void *data_pointer[4];
+        int reserved[4];
+        vector unsigned short dirty_bits[4];
+     }  */
+
+static void
+ea_load_store_inline (rtx mem, bool is_store, rtx ea_addr, rtx data_addr)
+{
+  rtx ea_addr_si;
+  HOST_WIDE_INT v;
+  rtx tag_size_sym = gen_rtx_SYMBOL_REF (Pmode, "__cache_tag_array_size");
+  rtx tag_arr_sym = gen_rtx_SYMBOL_REF (Pmode, "__cache_tag_array");
+  rtx index_mask = gen_reg_rtx (SImode);
+  rtx tag_arr = gen_reg_rtx (Pmode);
+  rtx splat_mask = gen_reg_rtx (TImode);
+  rtx splat = gen_reg_rtx (V4SImode);
+  rtx splat_hi = NULL_RTX;
+  rtx tag_index = gen_reg_rtx (Pmode);
+  rtx block_off = gen_reg_rtx (SImode);
+  rtx tag_addr = gen_reg_rtx (Pmode);
+  rtx tag = gen_reg_rtx (V4SImode);
+  rtx cache_tag = gen_reg_rtx (V4SImode);
+  rtx cache_tag_hi = NULL_RTX;
+  rtx cache_ptrs = gen_reg_rtx (TImode);
+  rtx cache_ptrs_si = gen_reg_rtx (SImode);
+  rtx tag_equal = gen_reg_rtx (V4SImode);
+  rtx tag_equal_hi = NULL_RTX;
+  rtx tag_eq_pack = gen_reg_rtx (V4SImode);
+  rtx tag_eq_pack_si = gen_reg_rtx (SImode);
+  rtx eq_index = gen_reg_rtx (SImode);
+  rtx bcomp, hit_label, hit_ref, cont_label, insn;
+
+  if (spu_ea_model != 32)
+    {
+      splat_hi = gen_reg_rtx (V4SImode);
+      cache_tag_hi = gen_reg_rtx (V4SImode);
+      tag_equal_hi = gen_reg_rtx (V4SImode);
+    }
+
+  emit_move_insn (index_mask, plus_constant (tag_size_sym, -128));
+  emit_move_insn (tag_arr, tag_arr_sym);
+  v = 0x0001020300010203LL;
+  emit_move_insn (splat_mask, immed_double_const (v, v, TImode));
+  ea_addr_si = ea_addr;
+  if (spu_ea_model != 32)
+    ea_addr_si = convert_to_mode (SImode, ea_addr, 1);
+
+  /* tag_index = ea_addr & (tag_array_size - 128)  */
+  emit_insn (gen_andsi3 (tag_index, ea_addr_si, index_mask));
+
+  /* splat ea_addr to all 4 slots.  */
+  emit_insn (gen_shufb (splat, ea_addr_si, ea_addr_si, splat_mask));
+  /* Similarly for high 32 bits of ea_addr.  */
+  if (spu_ea_model != 32)
+    emit_insn (gen_shufb (splat_hi, ea_addr, ea_addr, splat_mask));
+
+  /* block_off = ea_addr & 127  */
+  emit_insn (gen_andsi3 (block_off, ea_addr_si, spu_const (SImode, 127)));
+
+  /* tag_addr = tag_arr + tag_index  */
+  emit_insn (gen_addsi3 (tag_addr, tag_arr, tag_index));
+
+  /* Read cache tags.  */
+  emit_move_insn (cache_tag, gen_rtx_MEM (V4SImode, tag_addr));
+  if (spu_ea_model != 32)
+    emit_move_insn (cache_tag_hi, gen_rtx_MEM (V4SImode,
+					       plus_constant (tag_addr, 16)));
+
+  /* tag = ea_addr & -128  */
+  emit_insn (gen_andv4si3 (tag, splat, spu_const (V4SImode, -128)));
+
+  /* Read all four cache data pointers.  */
+  emit_move_insn (cache_ptrs, gen_rtx_MEM (TImode,
+					   plus_constant (tag_addr, 32)));
+
+  /* Compare tags.  */
+  emit_insn (gen_ceq_v4si (tag_equal, tag, cache_tag));
+  if (spu_ea_model != 32)
+    {
+      emit_insn (gen_ceq_v4si (tag_equal_hi, splat_hi, cache_tag_hi));
+      emit_insn (gen_andv4si3 (tag_equal, tag_equal, tag_equal_hi));
+    }
+
+  /* At most one of the tags compare equal, so tag_equal has one
+     32-bit slot set to all 1's, with the other slots all zero.
+     gbb picks off low bit from each byte in the 128-bit registers,
+     so tag_eq_pack is one of 0xf000, 0x0f00, 0x00f0, 0x000f, assuming
+     we have a hit.  */
+  emit_insn (gen_spu_gbb (tag_eq_pack, spu_gen_subreg (V16QImode, tag_equal)));
+  emit_insn (gen_spu_convert (tag_eq_pack_si, tag_eq_pack));
+
+  /* So counting leading zeros will set eq_index to 16, 20, 24 or 28.  */
+  emit_insn (gen_clzsi2 (eq_index, tag_eq_pack_si));
+
+  /* Allowing us to rotate the corresponding cache data pointer to slot0.
+     (rotating eq_index mod 16 bytes).  */
+  emit_insn (gen_rotqby_ti (cache_ptrs, cache_ptrs, eq_index));
+  emit_insn (gen_spu_convert (cache_ptrs_si, cache_ptrs));
+
+  /* Add block offset to form final data address.  */
+  emit_insn (gen_addsi3 (data_addr, cache_ptrs_si, block_off));
+
+  /* Check that we did hit.  */
+  hit_label = gen_label_rtx ();
+  hit_ref = gen_rtx_LABEL_REF (VOIDmode, hit_label);
+  bcomp = gen_rtx_NE (SImode, tag_eq_pack_si, const0_rtx);
+  insn = emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
+				      gen_rtx_IF_THEN_ELSE (VOIDmode, bcomp,
+							    hit_ref, pc_rtx)));
+  /* Say that this branch is very likely to happen.  */
+  v = REG_BR_PROB_BASE - REG_BR_PROB_BASE / 100 - 1;
+  REG_NOTES (insn)
+    = gen_rtx_EXPR_LIST (REG_BR_PROB, GEN_INT (v), REG_NOTES (insn));
+
+  ea_load_store (mem, is_store, ea_addr, data_addr);
+  cont_label = gen_label_rtx ();
+  emit_jump_insn (gen_jump (cont_label));
+  emit_barrier ();
+
+  emit_label (hit_label);
+
+  if (is_store)
+    {
+      HOST_WIDE_INT v_hi;
+      rtx dirty_bits = gen_reg_rtx (TImode);
+      rtx dirty_off = gen_reg_rtx (SImode);
+      rtx dirty_128 = gen_reg_rtx (TImode);
+      rtx neg_block_off = gen_reg_rtx (SImode);
+
+      /* Set up mask with one dirty bit per byte of the mem we are
+	 writing, starting from top bit.  */
+      v_hi = v = -1;
+      v <<= (128 - GET_MODE_SIZE (GET_MODE (mem))) & 63;
+      if ((128 - GET_MODE_SIZE (GET_MODE (mem))) >= 64)
+	{
+	  v_hi = v;
+	  v = 0;
+	}
+      emit_move_insn (dirty_bits, immed_double_const (v, v_hi, TImode));
+
+      /* Form index into cache dirty_bits.  eq_index is one of
+	 0x10, 0x14, 0x18 or 0x1c.  Multiplying by 4 gives us
+	 0x40, 0x50, 0x60 or 0x70 which just happens to be the
+	 offset to each of the four dirty_bits elements.  */
+      emit_insn (gen_ashlsi3 (dirty_off, eq_index, spu_const (SImode, 2)));
+
+      emit_insn (gen_spu_lqx (dirty_128, tag_addr, dirty_off));
+
+      /* Rotate bit mask to proper bit.  */
+      emit_insn (gen_negsi2 (neg_block_off, block_off));
+      emit_insn (gen_rotqbybi_ti (dirty_bits, dirty_bits, neg_block_off));
+      emit_insn (gen_rotqbi_ti (dirty_bits, dirty_bits, neg_block_off));
+
+      /* Or in the new dirty bits.  */
+      emit_insn (gen_iorti3 (dirty_128, dirty_bits, dirty_128));
+
+      /* Store.  */
+      emit_insn (gen_spu_stqx (dirty_128, tag_addr, dirty_off));
+    }
+
+  emit_label (cont_label);
+}
+
+static rtx
+expand_ea_mem (rtx mem, bool is_store)
+{
+  rtx ea_addr;
+  rtx data_addr = gen_reg_rtx (Pmode);
+
+  ea_addr = force_reg (EAmode, XEXP (mem, 0));
+  if (optimize_size || optimize == 0)
+    ea_load_store (mem, is_store, ea_addr, data_addr);
+  else
+    ea_load_store_inline (mem, is_store, ea_addr, data_addr);
+
+  if (ea_alias_set == -1)
+    ea_alias_set = new_alias_set ();
+  set_mem_alias_set (mem, 0);
+  set_mem_alias_set (mem, ea_alias_set);
+  return change_address (mem, VOIDmode, data_addr);
+}
+
 int
 spu_expand_mov (rtx * ops, enum machine_mode mode)
 {
@@ -3540,6 +3822,8 @@ spu_expand_mov (rtx * ops, enum machine_
     {
       if (GET_CODE (ops[0]) == MEM)
 	{
+ 	  if (MEM_ADDR_SPACE (ops[0]))
+ 	    ops[0] = expand_ea_mem (ops[0], true);
 	  if (!spu_valid_move (ops))
 	    {
 	      emit_insn (gen_store (ops[0], ops[1], gen_reg_rtx (TImode),
@@ -3549,6 +3833,8 @@ spu_expand_mov (rtx * ops, enum machine_
 	}
       else if (GET_CODE (ops[1]) == MEM)
 	{
+ 	  if (MEM_ADDR_SPACE (ops[1]))
+ 	    ops[1] = expand_ea_mem (ops[1], false);
 	  if (!spu_valid_move (ops))
 	    {
 	      emit_insn (gen_load
@@ -5543,6 +5829,26 @@ spu_vector_alignment_reachable (const_tr
   return true;
 }
 
+static enum machine_mode
+spu_ea_pointer_mode (int addrspace)
+{
+  switch (addrspace)
+    {
+    case 0:
+      return ptr_mode;
+    case 1:
+      return (spu_ea_model == 64 ? DImode : ptr_mode);
+    default:
+      gcc_unreachable ();
+    }
+}
+
+static bool
+spu_valid_pointer_mode (enum machine_mode mode)
+{
+  return (mode == ptr_mode || mode == Pmode || mode == spu_ea_pointer_mode (1));
+}
+
 /* Count the total number of instructions in each pipe and return the
    maximum, which is used as the Minimum Iteration Interval (MII)
    in the modulo scheduler.  get_pipe() will return -2, -1, 0, or 1.
@@ -5601,3 +5907,50 @@ spu_libgcc_shift_count_mode (void)
    for shift counts.  */
   return SImode;
 }
+
+const char *
+spu_addr_space_name (int addrspace)
+{
+  gcc_assert (addrspace > 0 && addrspace <= 1);
+  return (spu_address_spaces [addrspace].name);
+}
+
+static
+rtx (* spu_addr_space_conversion_rtl (int from, int to)) (rtx, rtx)
+{
+  gcc_assert ((from == 0 && to == 1) || (from == 1 && to == 0));
+
+  if (to == 0)
+    return spu_address_spaces[1].to_generic_insn;
+  else if (to == 1)
+    return spu_address_spaces[1].from_generic_insn;
+
+  return 0;
+}
+
+static
+bool spu_valid_addr_space (tree value)
+{
+  int i;
+  if (!value)
+    return false;
+
+  for (i = 0; spu_address_spaces[i].name; i++)
+    if (strcmp (IDENTIFIER_POINTER (value), spu_address_spaces[i].name) == 0)
+      return true;
+  return false;
+}
+
+static
+unsigned char spu_addr_space_number (tree ident)
+{
+  int i;
+  if (!ident)
+    return 0;
+
+  for (i = 0; spu_address_spaces[i].name; i++)
+    if (strcmp (IDENTIFIER_POINTER (ident), spu_address_spaces[i].name) == 0)
+      return i;
+
+  gcc_unreachable ();
+}
--- gcc-clean/gcc/config/spu/spu.h	2008-08-19 20:13:12.000000000 +1000
+++ gcc-nas/gcc/config/spu/spu.h	2008-08-19 20:49:52.000000000 +1000
@@ -485,6 +485,16 @@ targetm.resolve_overloaded_builtin = spu
 #define ASM_OUTPUT_LABELREF(FILE, NAME) \
   asm_fprintf (FILE, "%U%s", default_strip_name_encoding (NAME))
 
+#define ASM_OUTPUT_SYMBOL_REF(FILE, X) \
+  do								\
+    {								\
+      assemble_name (FILE, XSTR (X, 0));			\
+      if (SYMBOL_REF_DECL (X)					\
+	  && TREE_CODE (SYMBOL_REF_DECL (X)) == VAR_DECL	\
+	  && TYPE_ADDR_SPACE (TREE_TYPE (SYMBOL_REF_DECL (X))))	\
+	fputs ("@ppu", FILE);					\
+    } while (0)
+
 \f
 /* Instruction Output */
 #define REGISTER_NAMES \
--- gcc-clean/gcc/config/spu/spu.md	2008-08-19 20:13:12.000000000 +1000
+++ gcc-nas/gcc/config/spu/spu.md	2008-08-19 20:49:52.000000000 +1000
@@ -4757,6 +4757,45 @@ DONE;
 DONE;
 })
 
+(define_expand "to_ea"
+  [(use (match_operand 0 "" ""))
+   (use (match_operand 1 "" ""))]
+  ""
+{
+  rtx ls_mem, op0, op1;
+  enum machine_mode mode = (spu_ea_model == 32) ? Pmode : DImode;
 
+  ls_mem = gen_rtx_MEM (DImode, gen_rtx_SYMBOL_REF (Pmode, "__ea_local_store"));
 
+  op0 = force_reg (mode, operands[0]);
+  op1 = force_reg (Pmode, operands[1]);
 
+  if (mode == Pmode)
+    emit_insn (gen_addsi3 (op0, op1, force_reg (mode, gen_lowpart (mode, ls_mem))));
+  else
+    {
+      rtx tmp = gen_reg_rtx (DImode);
+      emit_move_insn (tmp, gen_rtx_ZERO_EXTEND (DImode, op1));
+      emit_insn (gen_adddi3 (op0, tmp, force_reg (mode, ls_mem)));
+    }
+  DONE;
+})
+
+(define_expand "from_ea"
+  [(use (match_operand 0 "" ""))
+   (use (match_operand 1 "" ""))]
+  ""
+{
+  rtx ls_mem, ls, op0, op1, tmp;
+  enum machine_mode mode = (spu_ea_model == 32) ? Pmode : DImode;
+
+  ls_mem = gen_rtx_MEM (DImode, gen_rtx_SYMBOL_REF (Pmode, "__ea_local_store"));
+  ls = force_reg (Pmode, gen_lowpart (Pmode, ls_mem));
+
+  op0 = force_reg (Pmode, operands[0]);
+  op1 = force_reg (mode, operands[1]);
+  tmp = (mode == Pmode) ? op1 : force_reg (Pmode, gen_lowpart (Pmode, op1));
+
+  emit_insn (gen_subsi3 (op0, tmp, ls));
+  DONE;
+})
--- gcc-clean/gcc/config/spu/spu.opt	2008-03-12 15:21:52.000000000 +1100
+++ gcc-nas/gcc/config/spu/spu.opt	2008-04-04 12:13:40.000000000 +1100
@@ -62,3 +62,11 @@ Generate code for given CPU
 mtune=
 Target RejectNegative Joined Var(spu_tune_string)
 Schedule code for given CPU
+
+mea32
+Target Report RejectNegative Var(spu_ea_model,32) Init(32)
+Access variables in 32-bit PPU objects
+
+mea64
+Target Report RejectNegative Var(spu_ea_model,64) VarExists
+Access variables in 64-bit PPU objects
--- gcc-clean/gcc/config/spu/t-spu-elf	2008-08-19 20:13:12.000000000 +1000
+++ gcc-nas/gcc/config/spu/t-spu-elf	2008-08-19 20:49:52.000000000 +1000
@@ -59,13 +59,64 @@ fp-bit.c: $(srcdir)/config/fp-bit.c $(sr
 CRTSTUFF_T_CFLAGS =
 
 #MULTILIB_OPTIONS=mlarge-mem/mtest-abi
+MULTILIB_OPTIONS=mea64
 #MULTILIB_DIRNAMES=large-mem test-abi
 #MULTILIB_MATCHES=
 
 # Neither gcc or newlib seem to have a standard way to generate multiple
 # crt*.o files.  So we don't use the standard crt0.o name anymore.
 
-EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o
+EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o libgcc_cachemgr.a libgcc_cachemgr_nonatomic.a \
+	libgcc_cache8k.a libgcc_cache16k.a libgcc_cache32k.a libgcc_cache64k.a libgcc_cache128k.a
+
+cachemgr.o: $(srcdir)/config/spu/cachemgr.c
+	$(GCC_FOR_TARGET) $(LIBGCC2_CFLAGS) -c $< -o $@
+
+%/cachemgr.o: $(srcdir)/config/spu/cachemgr.c
+	$(GCC_FOR_TARGET) $(LIBGCC2_CFLAGS) -c $< -o $@
+
+# Specialised rule to add a -D flag.
+cachemgr_nonatomic.o: $(srcdir)/config/spu/cachemgr.c
+	$(GCC_FOR_TARGET) $(LIBGCC2_CFLAGS) -DNONATOMIC -c $< -o $@
+
+%/cachemgr_nonatomic.o: $(srcdir)/config/spu/cachemgr.c
+	$(GCC_FOR_TARGET) $(LIBGCC2_CFLAGS) -DNONATOMIC -c $< -o $@
+
+libgcc_%.a: %.o
+	$(AR_FOR_TARGET) -rcs $@ $<
+
+%/libgcc_%.a: %.o
+	$(AR_FOR_TARGET) -rcs $@ $<
+
+cache8k.o: $(srcdir)/config/spu/cache.S
+	$(GCC_FOR_TARGET) -D__CACHE_SIZE__=8 -o $@ -c $<
+
+cache16k.o: $(srcdir)/config/spu/cache.S
+	$(GCC_FOR_TARGET) -D__CACHE_SIZE__=16 -o $@ -c $<
+
+cache32k.o: $(srcdir)/config/spu/cache.S
+	$(GCC_FOR_TARGET) -D__CACHE_SIZE__=32 -o $@ -c $<
+
+cache64k.o: $(srcdir)/config/spu/cache.S
+	$(GCC_FOR_TARGET) -D__CACHE_SIZE__=64 -o $@ -c $<
+
+cache128k.o: $(srcdir)/config/spu/cache.S
+	$(GCC_FOR_TARGET) -D__CACHE_SIZE__=128 -o $@ -c $<
+
+%/cache8k.o: $(srcdir)/config/spu/cache.S
+	$(GCC_FOR_TARGET) -D__CACHE_SIZE__=8 -o $@ -c $<
+
+%/cache16k.o: $(srcdir)/config/spu/cache.S
+	$(GCC_FOR_TARGET) -D__CACHE_SIZE__=16 -o $@ -c $<
+
+%/cache32k.o: $(srcdir)/config/spu/cache.S
+	$(GCC_FOR_TARGET) -D__CACHE_SIZE__=32 -o $@ -c $<
+
+%/cache64k.o: $(srcdir)/config/spu/cache.S
+	$(GCC_FOR_TARGET) -D__CACHE_SIZE__=64 -o $@ -c $<
+
+%/cache128k.o: $(srcdir)/config/spu/cache.S
+	$(GCC_FOR_TARGET) -D__CACHE_SIZE__=128 -o $@ -c $<
 
 LIBGCC = stmp-multilib
 INSTALL_LIBGCC = install-multilib
--- gcc-clean/gcc/config/spu/cache.S	1970-01-01 10:00:00.000000000 +1000
+++ gcc-nas/gcc/config/spu/cache.S	2008-08-20 14:42:19.000000000 +1000
@@ -0,0 +1,47 @@
+/* Copyright (C) 2008  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.  */
+
+.data
+.p2align 7
+.global __cache
+__cache:
+.rept __CACHE_SIZE__ * 8
+.fill 128
+.endr
+
+.p2align 7
+.global __cache_tag_array
+__cache_tag_array:
+.rept __CACHE_SIZE__ * 2
+.long 1, 1, 1, 1
+.fill 128-16
+.endr
+__end_cache_tag_array:
+
+.globl __cache_tag_array_size
+.set __cache_tag_array_size, __end_cache_tag_array-__cache_tag_array
--- gcc-clean/gcc/config/spu/cachemgr.c	1970-01-01 10:00:00.000000000 +1000
+++ gcc-nas/gcc/config/spu/cachemgr.c	2008-08-20 14:42:19.000000000 +1000
@@ -0,0 +1,425 @@
+/* Copyright (C) 2008  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>
+#include <spu_internals.h>
+#include <spu_intrinsics.h>
+#include <spu_cache.h>
+
+extern unsigned long long __ea_local_store;
+extern char __cache_tag_array_size;
+
+#define LINE_SIZE 128
+#define TAG_MASK (LINE_SIZE - 1)
+
+#define WAYS 4
+#define SET_MASK ((int) &__cache_tag_array_size - LINE_SIZE)
+
+#define CACHE_LINES ((int) &__cache_tag_array_size / \
+  sizeof (struct __cache_tag_array) * WAYS)
+
+struct __cache_tag_array
+{
+  unsigned int tag_lo[WAYS];
+  unsigned int tag_hi[WAYS];
+  void *base[WAYS];
+  int reserved[WAYS];
+  vector unsigned short dirty_bits[WAYS];
+};
+
+extern struct __cache_tag_array __cache_tag_array[];
+extern char __cache[];
+
+/* In order to make the code seem a little cleaner, and to avoid having
+   64/32 bit ifdefs all over the place, we macro.  */
+
+/* It may seem poor taste to define variables within a macro, but
+   it's C99 compliant.  */
+
+#ifdef __EA64__
+#define CHECK_TAG(_entry, _way, _tag) ((_entry->tag_lo[_way] == \
+  (_tag & 0xFFFFFFFF))&&(_entry->tag_hi[_way] == (_tag >> 32)))
+
+#define GET_TAG(_entry, _way) unsigned long long tag = _entry->tag_hi[_way]; \
+  tag = tag << 32;                                                           \
+  tag |= (_entry->tag_lo[_way]);
+
+#define SET_TAG(_entry, _way, _tag)             \
+  _entry->tag_lo[_way] = (_tag & 0xFFFFFFFF);   \
+  _entry->tag_hi[_way] = (_tag >> 32);
+
+#define addr unsigned long long
+#define si_from_eavoid(_x) si_from_ullong (eavoid_to_eanum(_x))
+#else /*__EA32__*/
+#define CHECK_TAG(_entry, _way, _tag) (_entry->tag_lo[_way] == _tag)
+
+#define GET_TAG(_entry, _way) unsigned long tag = _entry->tag_lo[_way]
+
+#define SET_TAG(_entry, _way, _tag)     \
+  _entry->tag_lo[_way] = _tag;
+
+#define addr unsigned long
+#define si_from_eavoid(_x) si_from_uint (eavoid_to_eanum(_x))
+#endif
+
+/* In GET_ENTRY, we cast away the high 32 bits,
+   as the tag is only in the low 32.  */
+
+#define GET_ENTRY(_addr) ((struct __cache_tag_array *)                  \
+        si_to_ptr(si_a                                                  \
+                   (si_and(si_from_uint((unsigned int) (addr) _addr),   \
+                           si_from_uint(SET_MASK)),                     \
+                    si_from_uint((unsigned int) __cache_tag_array))));
+
+#define GET_CACHE_LINE(_addr, _way)  ((void *) (__cache +       \
+  (_addr & SET_MASK) * WAYS) + (_way * LINE_SIZE));
+
+#define eavoid_to_eanum(_ea) ((addr) _ea)
+
+#define CHECK_DIRTY(_vec) (si_to_uint (si_orx ((qword) _vec)))
+#define SET_EMPTY(_entry, _way) (_entry->tag_lo[_way] = 1)
+#define CHECK_EMPTY(_entry, _way) (_entry->tag_lo[_way] == 1)
+
+#define LS_FLAG 0x80000000
+#define SET_IS_LS(_entry, _way) (_entry->reserved[_way] |= LS_FLAG)
+#define CHECK_IS_LS(_entry, _way) (_entry->reserved[_way] & LS_FLAG)
+#define GET_LRU(_entry, _way) (_entry->reserved[_way] & ~(LS_FLAG))
+
+static void __cache_flush_stub (void) __attribute__ ((destructor));
+static int dma_tag = 32;
+
+static void
+__cache_evict_entry (struct __cache_tag_array *entry, int way)
+{
+
+  GET_TAG (entry, way);
+
+  if ((CHECK_DIRTY (entry->dirty_bits[way])) && (!CHECK_IS_LS (entry, way)))
+    {
+/* Non-atomic writes.  */
+#ifdef NONATOMIC
+      char *line = ((void *) 0);
+
+      line = GET_CACHE_LINE (entry->tag_lo[way], way);
+      mfc_put (line, tag, LINE_SIZE, dma_tag, 0, 0);
+
+      /* Wait for DMA completion.  */
+      mfc_write_tag_mask (1 << dma_tag);
+      mfc_read_tag_status_all ();
+#else
+      /* Allocate a buffer large enough that we know it has 128 bytes
+         that are 128 byte aligned (for DMA). */
+
+      char buffer[LINE_SIZE + 127];
+      qword *buf_ptr = (qword *) (((unsigned int) (buffer) + 127) & ~127);
+      qword *line = GET_CACHE_LINE (entry->tag_lo[way], way);
+      qword bits;
+
+      do
+	{
+	  /* We atomically read the current memory into a buffer
+	     modify the dirty bytes in the buffer, and write it
+	     back. If writeback fails, loop and try again.  */
+
+	  mfc_getllar (buf_ptr, tag, 0, 0);
+	  mfc_read_atomic_status ();
+
+	  /* The method we're using to write 16 dirty bytes into
+	     the buffer at a time uses fsmb which in turn uses
+	     the least significant 16 bits of word 0, so we
+	     load the bits and rotate so that the first bit of
+	     the bitmap is in the first bit that fsmb will use.  */
+
+	  bits = (qword) entry->dirty_bits[way];
+	  bits = si_rotqbyi (bits, -2);
+
+	  /* Si_fsmb creates the mask of dirty bytes.
+	     Use selb to nab the appropriate bits.  */
+	  buf_ptr[0] = si_selb (buf_ptr[0], line[0], si_fsmb (bits));
+
+	  /* Rotate to next 16 byte section of cache.  */
+	  bits = si_rotqbyi (bits, 2);
+
+	  buf_ptr[1] = si_selb (buf_ptr[1], line[1], si_fsmb (bits));
+	  bits = si_rotqbyi (bits, 2);
+	  buf_ptr[2] = si_selb (buf_ptr[2], line[2], si_fsmb (bits));
+	  bits = si_rotqbyi (bits, 2);
+	  buf_ptr[3] = si_selb (buf_ptr[3], line[3], si_fsmb (bits));
+	  bits = si_rotqbyi (bits, 2);
+	  buf_ptr[4] = si_selb (buf_ptr[4], line[4], si_fsmb (bits));
+	  bits = si_rotqbyi (bits, 2);
+	  buf_ptr[5] = si_selb (buf_ptr[5], line[5], si_fsmb (bits));
+	  bits = si_rotqbyi (bits, 2);
+	  buf_ptr[6] = si_selb (buf_ptr[6], line[6], si_fsmb (bits));
+	  bits = si_rotqbyi (bits, 2);
+	  buf_ptr[7] = si_selb (buf_ptr[7], line[7], si_fsmb (bits));
+	  bits = si_rotqbyi (bits, 2);
+
+	  mfc_putllc (buf_ptr, tag, 0, 0);
+	}
+      while (mfc_read_atomic_status ());
+#endif
+    }
+
+  /* In any case, marking the lo tag with 1 which denotes empty.  */
+  SET_EMPTY (entry, way);
+  entry->dirty_bits[way] = (vector unsigned short) si_from_uint (0);
+}
+
+void
+__cache_evict (__ea void *ea)
+{
+  addr tag = (eavoid_to_eanum (ea) & ~(TAG_MASK));
+  struct __cache_tag_array *entry = GET_ENTRY (ea);
+  int i = 0;
+
+  /* Cycles through all the possible ways an address could be at
+     and evicts the way if found */
+
+  for (i = 0; i < WAYS; i++)
+    {
+      if (CHECK_TAG (entry, i, tag))
+	{
+	  __cache_evict_entry (entry, i);
+	}
+    }
+}
+
+static void *
+__cache_fill (int way, addr tag)
+{
+  char *line = ((void *) 0);
+
+  line = GET_CACHE_LINE (tag, way);
+
+  /* This will use DMA to fill the cache line.  */
+
+  if (dma_tag == 32)
+    dma_tag = mfc_tag_reserve ();
+
+  mfc_get (line, tag, LINE_SIZE, dma_tag, 0, 0);
+  mfc_write_tag_mask (1 << dma_tag);
+  mfc_read_tag_status_all ();
+  return (void *) line;
+}
+
+static void
+__cache_miss (__ea void *ea, struct __cache_tag_array *entry, int way)
+{
+
+  addr tag = (eavoid_to_eanum (ea) & ~(TAG_MASK));
+  unsigned int lru = 0;
+  int i = 0;
+  int idx = 0;
+
+  /* If way > 4, then there are no empty slots, so we must evict
+     the least recently used entry. */
+  if (way >= 4)
+    {
+      for (i = 0; i < WAYS; i++)
+	{
+	  if (GET_LRU (entry, i) > lru)
+	    {
+	      lru = GET_LRU (entry, i);
+	      idx = i;
+	    }
+	}
+      __cache_evict_entry (entry, idx);
+      way = idx;
+    }
+
+  /* Set the empty entry's tag and fill it's cache line. */
+
+  SET_TAG (entry, way, tag);
+  entry->reserved[way] = 0;
+
+  /* Check if the address is just an effective address within the
+     SPU's local store. */
+
+  /* Because the LS is not 256k aligned, we can't do a nice and mask
+     here to compare, so we must check the whole range.  */
+
+  if ((eavoid_to_eanum (ea) >= (addr) __ea_local_store) &&
+      (eavoid_to_eanum (ea) < (addr) (__ea_local_store + 0x40000)))
+    {
+      SET_IS_LS (entry, way);
+      entry->base[way] =
+	(void *) ((unsigned int) (eavoid_to_eanum (ea) -
+				  (addr) __ea_local_store) & ~(0x7f));
+    }
+  else
+    {
+      entry->base[way] = __cache_fill (way, tag);
+    }
+}
+
+void *
+__cache_fetch_dirty (__ea void *ea, int n_bytes_dirty)
+{
+#ifdef __EA64__
+  unsigned int tag_hi;
+  qword etag_hi;
+#endif
+  unsigned int tag_lo;
+  struct __cache_tag_array *entry;
+
+  qword etag_lo;
+  qword equal;
+  qword bit_mask;
+  qword way;
+
+  /* This first chunk, we merely fill the pointer and tag.  */
+
+  entry = GET_ENTRY (ea);
+
+#ifndef __EA64__
+  tag_lo =
+    si_to_uint (si_andc
+		(si_shufb
+		 (si_from_eavoid (ea), si_from_uint (0),
+		  si_from_uint (0x00010203)), si_from_uint (TAG_MASK)));
+#else
+  tag_lo =
+    si_to_uint (si_andc
+		(si_shufb
+		 (si_from_eavoid (ea), si_from_uint (0),
+		  si_from_uint (0x04050607)), si_from_uint (TAG_MASK)));
+
+  tag_hi =
+    si_to_uint (si_shufb
+		(si_from_eavoid (ea), si_from_uint (0),
+		 si_from_uint (0x00010203)));
+#endif
+
+  /* Increment LRU in reserved bytes.  */
+  si_stqd (si_ai (si_lqd (si_from_ptr (entry), 48), 1),
+	   si_from_ptr (entry), 48);
+
+missreturn:
+  /* Check if the entry's lo_tag is equal to the address' lo_tag.  */
+  etag_lo = si_lqd (si_from_ptr (entry), 0);
+  equal = si_ceq (etag_lo, si_from_uint (tag_lo));
+#ifdef __EA64__
+  /* And the high tag too  */
+  etag_hi = si_lqd (si_from_ptr (entry), 16);
+  equal = si_and (equal, (si_ceq (etag_hi, si_from_uint (tag_hi))));
+#endif
+
+  if ((si_to_uint (si_orx (equal)) == 0))
+    goto misshandler;
+
+  if (n_bytes_dirty)
+    {
+      /* way = 0x40,0x50,0x60,0x70 for each way, which is also the
+         offset of the appropriate dirty bits.  */
+      way = si_shli (si_clz (si_gbb (equal)), 2);
+
+      /* To create the bit_mask, we set it to all 1s (uint -1), then we
+         shift it over (128 - n_bytes_dirty) times.  */
+
+      bit_mask = si_from_uint (-1);
+
+      bit_mask =
+	si_shlqby (bit_mask, si_from_uint ((LINE_SIZE - n_bytes_dirty) / 8));
+
+      bit_mask =
+	si_shlqbi (bit_mask, si_from_uint ((LINE_SIZE - n_bytes_dirty) % 8));
+
+      /* Rotate it around to the correct offset.  */
+      bit_mask =
+	si_rotqby (bit_mask,
+		   si_from_uint (-1 * (eavoid_to_eanum (ea) & TAG_MASK) / 8));
+
+      bit_mask =
+	si_rotqbi (bit_mask,
+		   si_from_uint (-1 * (eavoid_to_eanum (ea) & TAG_MASK) % 8));
+
+      /* Update the dirty bits.  */
+      si_stqx (si_or (si_lqx (si_from_ptr (entry), way), bit_mask),
+	       si_from_ptr (entry), way);
+    };
+
+  /* We've definitely found the right entry, set LRU (reserved) to 0
+     maintaining the LS flag (MSB). */
+
+  si_stqd (si_andc
+	   (si_lqd (si_from_ptr (entry), 48),
+	    si_and (equal, si_from_uint (~(LS_FLAG)))),
+	   si_from_ptr (entry), 48);
+
+  return (void *)
+    si_to_ptr (si_a
+	       (si_orx
+		(si_and (si_lqd (si_from_ptr (entry), 32), equal)),
+		si_from_uint (((unsigned int) (addr) ea) & TAG_MASK)));
+
+misshandler:
+  equal = si_ceqi (etag_lo, 1);
+  __cache_miss (ea, entry, (si_to_uint (si_clz (si_gbb (equal))) - 16) >> 2);
+  goto missreturn;
+}
+
+void *
+__cache_fetch (__ea void *ea)
+{
+  return __cache_fetch_dirty (ea, 0);
+}
+
+void
+__cache_touch (__ea void *ea __attribute__ ((unused)))
+{
+  /* NO-OP for now.  */
+}
+
+static void
+__cache_flush_stub (void)
+{
+  __cache_flush ();
+}
+
+void
+__cache_flush (void)
+{
+  struct __cache_tag_array *entry = __cache_tag_array;
+  unsigned int i = 0;
+  int j = 0;
+
+  /* Cycle through each cache entry and evict all used ways.  */
+
+  for (i = 0; i < (CACHE_LINES / WAYS); i++)
+    {
+      for (j = 0; j < WAYS; j++)
+	{
+	  if (!CHECK_EMPTY (entry, j))
+	    {
+	      __cache_evict_entry (entry, j);
+	    }
+	}
+      entry++;
+    }
+}
--- gcc-clean/gcc/config/spu/spu_cache.h	1970-01-01 10:00:00.000000000 +1000
+++ gcc-nas/gcc/config/spu/spu_cache.h	2008-08-20 14:42:19.000000000 +1000
@@ -0,0 +1,41 @@
+/* Copyright (C) 2008 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.  */
+
+/* As a special exception, if you include this header file into source files
+   compiled by GCC, this header file does not by itself cause  the resulting
+   executable to be covered by the GNU General Public License.  This exception
+   does not however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#ifndef SPU_CACHEH
+#define SPU_CACHEH
+
+void *__cache_fetch_dirty (__ea void *ea, int n_bytes_dirty);
+void *__cache_fetch (__ea void *ea);
+void __cache_evict (__ea void *ea);
+void __cache_flush (void);
+void __cache_touch (__ea void *ea);
+
+#define cache_fetch_dirty(_ea, _n_bytes_dirty) \
+     __cache_fetch_dirty(_ea, _n_bytes_dirty)
+
+#define cache_fetch(_ea) __cache_fetch(_ea)
+#define cache_touch(_ea) __cache_touch(_ea)
+#define cache_evict(_ea) __cache_evict(_ea)
+#define cache_flush() __cache_flush()
+
+#endif
--- gcc-clean/gcc/doc/invoke.texi	2008-08-19 20:12:21.000000000 +1000
+++ gcc-nas/gcc/doc/invoke.texi	2008-08-19 20:49:42.000000000 +1000
@@ -775,7 +775,10 @@ See RS/6000 and PowerPC Options.
 -msafe-dma -munsafe-dma @gol
 -mbranch-hints @gol
 -msmall-mem -mlarge-mem -mstdmain @gol
--mfixed-range=@var{register-range}}
+-mfixed-range=@var{register-range} @gol
+-mea32 -mea64 @gol
+-mcache-size=@var{cache-size} @gol
+-matomic-updates -mno-atomic-updates}
 
 @emph{System V Options}
 @gccoptlist{-Qy  -Qn  -YP,@var{paths}  -Ym,@var{dir}}
@@ -14538,6 +14541,34 @@ useful when compiling kernel code.  A re
 two registers separated by a dash.  Multiple register ranges can be
 specified separated by a comma.
 
+@item -mea32
+@itemx -mea64
+@opindex mea32
+@opindex mea64
+
+Compile code assuming that pointers to the __ea address space are either
+32 or 64 bits wide. The default is 32 bits.  As this is an ABI changing
+option, all object code in an executable must be compiled with the same
+option.
+
+@item -mcache-size=@var{cache-size}
+@opindex mcache-size
+
+This option controls the version of libgcc that the compiler links to an
+executable and selects software cache support with a particular software
+cache size.  Possible options for @var{cache-size} are @samp{8},
+@samp{16}, @samp{32}, @samp{64} and @samp{128}.  The default cache size
+is 64KB.
+
+@item -matomic-updates
+@itemx -mno-atomic-updates
+@opindex matomic-updates
+@opindex mno-atomic-updates
+
+This option controls the version of libgcc that the compiler links to an
+executable and selects whether atomic updates to the software cache are
+used.  The default behavior is to use atomic updates.
+
 @end table
 
 @node System V Options
--- gcc-clean/gcc/testsuite/gcc.target/spu/cache.c	1970-01-01 10:00:00.000000000 +1000
+++ gcc-nas/gcc/testsuite/gcc.target/spu/cache.c	2008-08-13 10:55:06.000000000 +1000
@@ -0,0 +1,211 @@
+/* Copyright (C) 2008 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 } */
+/* { dg-options "-mcache-size=8" } */
+
+#include <stdlib.h>
+#include <string.h>
+#include <spu_cache.h>
+
+#ifdef __EA64__
+#define addr unsigned long long
+#else
+#define addr unsigned long
+#endif
+
+#ifdef __EA64__
+#define malloc_ea __malloc_ea64
+#define memset_ea __memset_ea64
+#define memcpy_ea __memcpy_ea64
+
+typedef unsigned long long size_ea_t;
+
+__ea void *__malloc_ea64 (size_ea_t);
+__ea void *__memset_ea64 (__ea void *, int, size_ea_t);
+__ea void *__memcpy_ea64 (__ea void *, __ea const void *, size_ea_t);
+#else
+#define malloc_ea __malloc_ea32
+#define memset_ea __memset_ea32
+#define memcpy_ea __memcpy_ea32
+
+typedef unsigned long size_ea_t;
+
+__ea void *__malloc_ea32 (size_ea_t size);
+__ea void *__memset_ea32 (__ea void *, int, size_ea_t);
+__ea void *__memcpy_ea32 (__ea void *, __ea const void *, size_ea_t);
+#endif
+
+static __ea void *bigblock;
+static __ea void *block;
+static int *ls_block;
+
+void
+init_mem ()
+{
+  bigblock = malloc_ea (10240 + 127);
+  block = malloc_ea (256);
+  ls_block = malloc (128);
+
+  memset_ea (bigblock, 0, 10240 + 127);
+  memset_ea (block, -1, 256);
+  memset (ls_block, -1, 128);
+}
+
+/* Test 1: Simple cache fetching.  */
+void
+test1 ()
+{
+  addr aligned = ((((addr) block) + 128) & ~(127));
+  int *p1 = NULL;
+  int *p2 = NULL;
+  int i = 0;
+
+  /* First, check if the same addr give the same cache ptr.  */
+  p1 = cache_fetch ((__ea void *) aligned);
+  p2 = cache_fetch ((__ea void *) aligned);
+
+  if (p1 != p2)
+    abort ();
+
+  /* Check that the data actually is in the cache. */
+  for (i = 0; i < 32; i++)
+    {
+      if (p1[i] != -1)
+	abort ();
+    }
+
+  /* Check returning within the cache line. */
+  p2 = cache_fetch ((__ea void *) (aligned + 4));
+
+  if (p2 - p1 != 1)
+    abort ();
+
+  /* Finally, check that fetching an LS pointer returns that pointer.  */
+  p1 = cache_fetch ((__ea char *) ls_block);
+  if (p1 != ls_block)
+    abort ();
+}
+
+/* Test 2: Eviction testing. */
+void
+test2 ()
+{
+  addr aligned = ((((addr) block) + 128) & ~(127));
+  int *p = NULL;
+  int i = 0;
+
+  /* First check that clean evictions don't write back.  */
+  p = cache_fetch ((__ea void *) aligned);
+  for (i = 0; i < 32; i++)
+    p[i] = 0;
+
+  cache_evict ((__ea void *) aligned);
+  memcpy_ea ((__ea char *) ls_block, (__ea void *) aligned, 128);
+
+  for (i = 0; i < 32; i++)
+    {
+      if (ls_block[i] == 0)
+	abort ();
+    }
+
+  /* Now check that dirty evictions do write back.  */
+  p = cache_fetch_dirty ((__ea void *) aligned, 128);
+  for (i = 0; i < 32; i++)
+    p[i] = 0;
+
+  cache_evict ((__ea void *) aligned);
+  memcpy_ea ((__ea char *) ls_block, (__ea void *) aligned, 128);
+
+  for (i = 0; i < 32; i++)
+    {
+      if (ls_block[i] != 0)
+	abort ();
+    }
+
+  /* Finally, check that non-atomic writeback only writes dirty bytes.  */
+
+  for (i = 0; i < 32; i++)
+    {
+      p = cache_fetch_dirty ((__ea void *) (aligned + i * 4), (i % 2) * 4);
+      p[0] = -1;
+    }
+
+  cache_evict ((__ea void *) aligned);
+  memcpy_ea ((__ea char *) ls_block, (__ea void *) aligned, 128);
+
+  for (i = 0; i < 32; i++)
+    {
+      if ((ls_block[i] == -1) && (i % 2 == 0))
+	abort ();
+      if ((ls_block[i] == 0) && (i % 2 == 1))
+	abort ();
+    }
+}
+
+/* Test LS forced-eviction. */
+void
+test3 ()
+{
+  addr aligned = ((((addr) bigblock) + 127) & ~(127));
+  char *test = NULL;
+  char *ls = NULL;
+  int i = 0;
+
+  /* Init memory, fill the cache to capacity.  */
+  ls = cache_fetch_dirty ((__ea void *) aligned, 128);
+  for (i = 1; i < (8192 / 128); i++)
+    cache_fetch_dirty ((__ea void *) (aligned + i * 128), 128);
+
+  memset (ls, -1, 128);
+  test = cache_fetch ((__ea void *) (aligned + 8192));
+
+  /* test == ls indicates cache collision.  */
+  if (test != ls)
+    abort ();
+
+  /* Make sure it actually wrote the cache line.  */
+  for (i = 0; i < 128; i++)
+    {
+      if (ls[i] != 0)
+	abort ();
+    }
+
+  ls = cache_fetch ((__ea void *) aligned);
+
+  /* test != ls indicates another entry was evicted.  */
+  if (test == ls)
+    abort ();
+
+  /* Make sure that the previous eviction actually wrote back.  */
+  for (i = 0; i < 128; i++)
+    {
+      if (ls[i] != 0xFF)
+	abort ();
+    }
+}
+
+int
+main (int argc, char **argv)
+{
+  init_mem ();
+  test1 ();
+  test2 ();
+  test3 ();
+
+  return 0;
+}
--- gcc-clean/gcc/testsuite/gcc.target/spu/ea/ea.exp	1970-01-01 10:00:00.000000000 +1000
+++ gcc-nas/gcc/testsuite/gcc.target/spu/ea/ea.exp	2008-08-21 15:04:18.000000000 +1000
@@ -0,0 +1,41 @@
+#   Copyright (C) 2008 Free Software Foundation, Inc.
+
+# This program 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 3 of the License, or
+# (at your option) any later version.
+# 
+# This program 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 COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# Load support procs.
+load_lib gcc-dg.exp
+
+# Exit immediately if this isn't a SPU target.
+if { ![istarget spu-*-*] } then {
+  return
+}
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_CFLAGS
+if ![info exists DEFAULT_CFLAGS] then {
+    set DEFAULT_CFLAGS "-std=gnu89 -pedantic-errors"
+}
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] \
+        "" $DEFAULT_CFLAGS
+
+# All done.
+dg-finish
--- gcc-clean/gcc/testsuite/gcc.target/spu/ea/cast1.c	1970-01-01 10:00:00.000000000 +1000
+++ gcc-nas/gcc/testsuite/gcc.target/spu/ea/cast1.c	2008-08-13 11:14:43.000000000 +1000
@@ -0,0 +1,23 @@
+/* { dg-do run { target spu-*-* } } */
+/* { dg-options "-std=gnu99" } */
+
+extern void abort (void);
+extern unsigned long long __ea_local_store;
+
+__ea int *ppu;
+int x, *spu = &x, *spu2;
+
+int
+main (int argc, char **argv)
+{
+  ppu = (__ea int *) spu;
+  spu2 = (int *) ppu;
+
+  if ((int) ppu != (int) __ea_local_store + (int) spu)
+    abort ();
+
+  if (spu != spu2)
+    abort ();
+
+  return 0;
+}
--- gcc-clean/gcc/testsuite/gcc.target/spu/ea/compile.c	1970-01-01 10:00:00.000000000 +1000
+++ gcc-nas/gcc/testsuite/gcc.target/spu/ea/compile.c	2008-08-15 10:42:02.000000000 +1000
@@ -0,0 +1,60 @@
+/* Valid __ea declarations.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu99 -pedantic-errors" } */
+
+/* Externs.  */
+
+__ea extern int i1;
+extern __ea int i2;
+extern int __ea i3;
+extern int __ea *ppu;
+
+/* Pointers.  */
+__ea int *i4p;
+
+/* Typedefs.  */
+typedef __ea int ea_int_t;
+typedef __ea int *ea_int_star_t;
+
+void
+f1 ()
+{
+  int *spu;
+  ppu = (ea_int_t *) spu;
+  ppu = (ea_int_star_t) spu;
+}
+
+void
+f2 ()
+{
+  int *spu;
+  spu = (int *) ppu;
+  ppu = (__ea int *) spu;
+}
+
+void
+f3 ()
+{
+  int i = sizeof (__ea int);
+}
+
+__ea int *f4 (void)
+{
+  return 0;
+}
+
+void f5 (__ea int *parm)
+{
+  ;
+}
+
+static inline __ea void *f6 (__ea void *start)
+{
+  return 0;
+}
+
+void f7 (void)
+{
+  __ea void *s1;
+  auto __ea void *s2;
+}
--- gcc-clean/gcc/testsuite/gcc.target/spu/ea/cppdefine32.c	1970-01-01 10:00:00.000000000 +1000
+++ gcc-nas/gcc/testsuite/gcc.target/spu/ea/cppdefine32.c	2008-04-04 11:49:49.000000000 +1100
@@ -0,0 +1,9 @@
+/* Test default __EA32__ define.  */
+/* { dg-options "-std=gnu89 -pedantic-errors -mea32" } */
+/* { dg-do compile } */
+
+#ifdef __EA32__
+int x;
+#else
+#error __EA32__ undefined
+#endif
--- gcc-clean/gcc/testsuite/gcc.target/spu/ea/cppdefine64.c	1970-01-01 10:00:00.000000000 +1000
+++ gcc-nas/gcc/testsuite/gcc.target/spu/ea/cppdefine64.c	2008-04-04 11:49:49.000000000 +1100
@@ -0,0 +1,8 @@
+/* { dg-options "-std=gnu89 -mea64" } */
+/* { dg-do compile } */
+
+#ifdef __EA64__
+int x;
+#else
+#error __EA64__ undefined
+#endif
--- gcc-clean/gcc/testsuite/gcc.target/spu/ea/errors.c	1970-01-01 10:00:00.000000000 +1000
+++ gcc-nas/gcc/testsuite/gcc.target/spu/ea/errors.c	2008-04-04 11:49:49.000000000 +1100
@@ -0,0 +1,36 @@
+/* Invalid __ea declarations.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu99 -pedantic-errors" } */
+
+__ea int i0;		 /* { dg-error "'__ea' variable 'i0' must be extern" } */
+int * __ea i1;		 /* { dg-error "'__ea' variable 'i1' must be extern" } */
+static int __ea i2;	 /* { dg-error "'__ea' combined with 'static' qualifier for 'i2'" } */
+extern __ea void f1 ();	 /* { dg-error "'__ea' specified for function 'f1'" } */
+
+void func ()
+{
+  register __ea int local1; /* { dg-error "'__ea' combined with 'register' qualifier for 'local1'" } */
+  auto __ea int local2;     /* { dg-error "'__ea' combined with 'auto' qualifier for 'local2'" } */
+  __ea int local3;	    /* { dg-error "'__ea' specified for auto variable 'local3'" } */
+  static __ea int local4;   /* { dg-error "'__ea' combined with 'static' qualifier for 'local4'" } */
+}
+
+void func2 (__ea int x)	    /* { dg-error "'__ea' specified for parameter 'x'" } */
+{ }
+
+struct st {
+  __ea int x;		    /* { dg-error "'__ea' specified for structure field 'x'" } */
+  __ea int *p;		    /* { dg-error "'__ea' specified for structure field 'p'" } */
+} s;
+
+__ea int func3 (int x) {    /* { dg-error "'__ea' specified for function 'func3'" } */
+  return x;
+}
+
+struct A { int a; };
+
+int func4 ()
+{
+  struct A i = (__ea struct A) { 1 };	/* { dg-error "compound literal qualified by address-space qualifier" } */
+  return i.a;
+}
--- gcc-clean/gcc/testsuite/gcc.target/spu/ea/options1.c	1970-01-01 10:00:00.000000000 +1000
+++ gcc-nas/gcc/testsuite/gcc.target/spu/ea/options1.c	2008-04-04 11:49:49.000000000 +1100
@@ -0,0 +1,5 @@
+/* Test -mcache-size.  */
+/* { dg-options "-mcache-size=128" } */
+/* { dg-do compile } */
+
+int x;


^ permalink raw reply	[flat|nested] 7+ messages in thread
* PATCH: named address space support (2/2: SPU backend)
@ 2008-08-26 15:41 Ben Elliston
  0 siblings, 0 replies; 7+ messages in thread
From: Ben Elliston @ 2008-08-26 15:41 UTC (permalink / raw)
  To: gcc-patches; +Cc: Trevor_Smigiel, andrew_pinski, David Edelsohn

Ping?

  http://gcc.gnu.org/ml/gcc-patches/2008-08/msg01479.html

Thanks, Ben


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2008-09-01 12:04 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-08-21  6:24 PATCH: named address space support (2/2: SPU backend) Ben Elliston
2008-08-29  3:17 ` Trevor_Smigiel
2008-08-29  5:07   ` Ben Elliston
2008-08-29  6:00   ` Ben Elliston
     [not found]     ` <Pine.LNX.4.64.0808311635430.12423@digraph.polyomino.org.uk>
2008-09-01  0:36       ` Ben Elliston
2008-09-01 12:04         ` Joseph S. Myers
2008-08-26 15:41 Ben Elliston

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).