--- gcc/ira-lives.c.orig	2014-05-07 16:59:10.000000000 +0800
+++ gcc/ira-lives.c	2014-05-19 23:00:16.000000000 +0800
@@ -1116,6 +1116,104 @@
   return cheap_reg;
 }  
 
+/* Some built-in functions such as memset, will use the first input argument
+as the return value.  For these functions, the hard register used by
+the first input argument can be assigned to the cheap reg which
+find_call_crossed_cheap_reg has found.  The function pass_through_reg_in_call
+tries to find a hard reg for the cheap_reg.  Callers of this function should
+make sure CALL_P (call_insn) == true.  */
+static rtx
+pass_through_reg_in_call (rtx call_insn)
+{
+	rtx src_reg = NULL_RTX;
+	rtx dest_reg = NULL_RTX;
+	rtx exp = CALL_INSN_FUNCTION_USAGE (call_insn);
+
+	while (exp != NULL)
+	{
+		rtx x = XEXP (exp, 0);
+		if (GET_CODE (x) == SET)
+		{
+			exp = x;
+			break;
+		}
+		exp = XEXP (exp, 1);
+	}
+
+	if (exp != NULL)
+	{
+		src_reg = SET_SRC (exp);
+		dest_reg = SET_DEST (exp);
+	}
+
+	if (rtx_equal_p (src_reg, dest_reg))
+		return dest_reg;
+	else
+		return NULL_RTX;
+}
+
+/* Fetch the ira_object of a pseudo register.  */
+static ira_object_t *
+get_reg_obj (rtx reg, int *num_objects)
+{
+	ira_allocno_t reg_alloc = ira_curr_regno_allocno_map[REGNO (reg)];
+	*num_objects = ALLOCNO_NUM_OBJECTS (reg_alloc);
+	return &(ALLOCNO_OBJECT (reg_alloc, 0));
+}
+
+/* For a single set insn such as (set (reg:SI 0) (reg:SI 115)), we should not
+mark r115 conflict with r0.  The function is_interesting_single_set checks
+whether the set insn has one hard register and one pseudo register
+as operands.  Callers should make sure the single_set_insn is the result of
+a single_set function.  */
+static bool
+is_interesting_single_set (rtx single_set_insn)
+{
+	bool ret = false;
+	if (single_set_insn != NULL_RTX)
+	{
+		rtx dest_reg = SET_DEST (single_set_insn);
+		rtx src_reg = SET_SRC (single_set_insn);
+		if (REG_P (dest_reg) && REG_P (src_reg))
+		{
+			unsigned char hard_reg_count = 0;
+			if (HARD_REGISTER_P (dest_reg))
+				hard_reg_count++;
+
+			if (HARD_REGISTER_P (src_reg))
+				hard_reg_count++;
+
+			if (hard_reg_count == 1)
+			{
+				/* Maybe redundant?  */
+				if (GET_MODE (src_reg) == GET_MODE (dest_reg))
+					ret = true;
+			}
+		}
+	}
+	return ret;
+}
+
+/* This macro clear conflict bits for the hard reg REG in obj_array,
+if in saved_conflict these bits were not set.  */
+#define CLEAR_UNNEEDED_CONFLICT(reg, obj_array, obj_num, saved_conflict) \
+({ \
+	int obj_idx = 0; \
+	for (; obj_idx < obj_num; obj_idx++) \
+	{ \
+		int cur_regno = REGNO (reg); \
+		int last_regno = REGNO (reg) + obj_num; \
+		for (; cur_regno < last_regno; cur_regno++) \
+		{ \
+			if (TEST_HARD_REG_BIT (saved_conflict[obj_idx], cur_regno)) \
+				continue; \
+			else \
+				CLEAR_HARD_REG_BIT (OBJECT_TOTAL_CONFLICT_HARD_REGS ( \
+				obj_array[obj_idx]), cur_regno); \
+		} \
+	} \
+})
+
 /* Process insns of the basic block given by its LOOP_TREE_NODE to
    update allocno live ranges, allocno hard register conflicts,
    intersected calls, and register pressure info for allocnos for the
@@ -1199,6 +1297,61 @@
 		     INSN_UID (insn), loop_tree_node->parent->loop_num,
 		     curr_point);
 
+		/* Record informations about a interesting single set, see
+		comments above is_interesting_single_set for details.  */
+		HARD_REG_SET conflict_before_single_set[2] = {{0}};
+		rtx single_set_insn = single_set (insn);
+		bool found_interesting_single_set =
+			is_interesting_single_set (single_set_insn);
+		ira_object_t *single_set_obj = NULL;
+		int single_set_num_objects = 0;
+		rtx single_set_hard_reg = NULL_RTX;
+		rtx single_set_pseudo_reg = NULL_RTX;
+		if (found_interesting_single_set)
+		{
+			/* Setup the hard register and the pseudo register.  */
+			single_set_hard_reg = SET_SRC (single_set_insn);
+			if (HARD_REGISTER_P (single_set_hard_reg))
+				single_set_pseudo_reg = SET_DEST (single_set_insn);
+			else
+			{
+				single_set_hard_reg = SET_DEST (single_set_insn);
+				single_set_pseudo_reg = SET_SRC (single_set_insn);
+			}
+
+			single_set_obj = get_reg_obj (single_set_pseudo_reg,
+				&single_set_num_objects);
+			gcc_assert (single_set_num_objects <= 2);
+			int obj_idx = 0;
+			for (; obj_idx < single_set_num_objects; obj_idx++)
+				COPY_HARD_REG_SET (conflict_before_single_set[obj_idx],
+				OBJECT_TOTAL_CONFLICT_HARD_REGS (single_set_obj[obj_idx]));
+		}
+
+		/* Record informations about cheap_reg, see
+		comments above pass_through_reg_in_call for details.  */
+		HARD_REG_SET saved_cheap_reg_conflict[2] = {{0}};
+		rtx cheap_reg = NULL_RTX;
+		rtx pass_through_reg = NULL_RTX;
+		ira_object_t *cheap_reg_obj = NULL;
+		int cheap_reg_num_objects = 0;
+		call_p = CALL_P (insn);
+		if (call_p)
+		{
+			cheap_reg = find_call_crossed_cheap_reg (insn);
+			pass_through_reg = pass_through_reg_in_call (insn);
+			if (cheap_reg != NULL_RTX && pass_through_reg != NULL_RTX)
+			{
+				cheap_reg_obj = get_reg_obj (cheap_reg,
+					&cheap_reg_num_objects);
+				gcc_assert (cheap_reg_num_objects <= 2);
+				int obj_idx = 0;
+				for (; obj_idx < cheap_reg_num_objects; obj_idx++)
+					COPY_HARD_REG_SET (saved_cheap_reg_conflict[obj_idx],
+					OBJECT_TOTAL_CONFLICT_HARD_REGS (cheap_reg_obj[obj_idx]));
+			}
+		}
+
 	  /* Mark each defined value as live.  We need to do this for
 	     unused values because they still conflict with quantities
 	     that are live at the time of the definition.
@@ -1208,7 +1361,6 @@
 	     on a call-clobbered register.  Marking the register as
 	     live would stop us from allocating it to a call-crossing
 	     allocno.  */
-	  call_p = CALL_P (insn);
 	  for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
 	    if (!call_p || !DF_REF_FLAGS_IS_SET (*def_rec, DF_REF_MAY_CLOBBER))
 	      mark_ref_live (*def_rec);
@@ -1261,7 +1413,6 @@
 	      /* Try to find a SET in the CALL_INSN_FUNCTION_USAGE, and from
 		 there, try to find a pseudo that is live across the call but
 		 can be cheaply reconstructed from the return value.  */
-	      rtx cheap_reg = find_call_crossed_cheap_reg (insn);
 	      if (cheap_reg != NULL_RTX)
 		add_reg_note (insn, REG_RETURNED, cheap_reg);
 
@@ -1320,6 +1471,21 @@
 	  for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++)
 	    mark_ref_live (*use_rec);
 
+		/* Clear unnecessary conflicts, see comments
+		above is_interesting_single_set for details.  */
+		if (found_interesting_single_set)
+			CLEAR_UNNEEDED_CONFLICT (single_set_hard_reg,
+			single_set_obj, single_set_num_objects,
+			conflict_before_single_set);
+
+		/* Clear unnecessary conflicts, see
+		comments above pass_through_reg_in_call for details.  */
+		if (cheap_reg != NULL_RTX
+			&& pass_through_reg != NULL_RTX)
+			CLEAR_UNNEEDED_CONFLICT (pass_through_reg,
+			cheap_reg_obj, cheap_reg_num_objects,
+			saved_cheap_reg_conflict);
+
 	  process_single_reg_class_operands (true, freq);
 
 	  set_p = mark_hard_reg_early_clobbers (insn, true);
