diff mbox series

[4/8] cfg: adjust phi-sources when merging BBs

Message ID 20201116222927.51939-5-luc.vanoostenryck@gmail.com (mailing list archive)
State Mainlined, archived
Headers show
Series cfg: early CFG simplification | expand

Commit Message

Luc Van Oostenryck Nov. 16, 2020, 10:29 p.m. UTC
When merging two basic blocks, it may happen that both of theses
blocks contain a phi-source for the same phi-node. In this case,
only the phi-source from the bottom BB must be taken in account,
it kinda overwrites the value from the top BB and the phi-source
from the top BB must be ignored, in fact it must be removed.

However, it is not the case and this extra phi-source creates
different kind of problems. Among other things, it hinders
further simplifications. For example, the following code:

	extern int array[2];
	static inline int stupid_select(int idx)
	{
		if (idx)
			idx = 0;
		return array[idx];
	}
	int select(void)
	{
		int d = stupid_select(-1);
		return d;
	}

should boil down to a simple dereference of the array with
an index of zero, like:
	select:
		load.32     %r8 <- 0[array]
		ret.32      %r8

but currently gives:
	select:
		phisrc.32   %phi3(idx) <- $0xffffffff
		phisrc.32   %phi4(idx) <- $0
		phi.32      %r12(idx) <- %phi3(idx), %phi4(idx)
		sext.64     %r5 <- (32) %r12(idx)
		mul.64      %r6 <- %r5, $4
		add.64      %r7 <- %r6, array
		load.32     %r8 <- 0[%r7]
		ret.32      %r8

This patch takes care of the problem by:
* when merging 2 BBs, check when reaching a phi-source in the bottom BB
* if one is found, look after sibling phi-sources
* remove such sibling if belonging to the top BB.
With this change, the code above gives:
	select:
		phisrc.32   %phi4(idx) <- $0
		phi.32      %r12(idx) <- %phi4(idx)
		sext.64     %r5 <- (32) %r12(idx)
		mul.64      %r6 <- %r5, $4
		add.64      %r7 <- %r6, array
		load.32     %r8 <- 0[%r7]
		ret.32      %r8

which can the be simplified into the expected result.

Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
---
 flow.c                                  | 42 +++++++++++++++++++++++++
 validation/optim/merge_bbe-adjust_phi.c |  1 -
 2 files changed, 42 insertions(+), 1 deletion(-)

Comments

Linus Torvalds Nov. 16, 2020, 10:53 p.m. UTC | #1
On Mon, Nov 16, 2020 at 2:30 PM Luc Van Oostenryck
<luc.vanoostenryck@gmail.com> wrote:
>
> +static void adjust_phisrc(struct basic_block *top, struct instruction *insn)

My only issue is that this is a really odd name, and calling convention.

"adjust"?

Wouldn't it be more sensible to call this "remove_phisrc()", because
that's what it does. It removes the matching phisrc instruction in the
specified basic block.

(I also think it might be a bit more obvious to do the get_phinode()
in the caller, and simply pass in the OP_PHI instruction, but maybe
you had reasons to do it that way).

             Linus
Luc Van Oostenryck Nov. 16, 2020, 11:47 p.m. UTC | #2
On Mon, Nov 16, 2020 at 02:53:21PM -0800, Linus Torvalds wrote:
> On Mon, Nov 16, 2020 at 2:30 PM Luc Van Oostenryck
> <luc.vanoostenryck@gmail.com> wrote:
> >
> > +static void adjust_phisrc(struct basic_block *top, struct instruction *insn)
> 
> My only issue is that this is a really odd name, and calling convention.
> 
> "adjust"?
> 
> Wouldn't it be more sensible to call this "remove_phisrc()", because
> that's what it does. It removes the matching phisrc instruction in the
> specified basic block.

Yes, the name is horrible and doesn't mean much. I'll change it.
 
> (I also think it might be a bit more obvious to do the get_phinode()
> in the caller, and simply pass in the OP_PHI instruction, but maybe
> you had reasons to do it that way).

In fact, I wrote it first like that but then I've another piece
of code that need exactly the same info so I change it into
a separate function.

This code is a bit weird though, because it actively use the fact
that each phi-sources feeds a single phi-node while the underlying
data structures are designed so that phi-sources can be shared
between phi-nodes (but I think that allowing them the be effectively
shared would bring too much problems).

-- Luc
Linus Torvalds Nov. 17, 2020, 12:16 a.m. UTC | #3
On Mon, Nov 16, 2020 at 3:47 PM Luc Van Oostenryck
<luc.vanoostenryck@gmail.com> wrote:
>
> This code is a bit weird though, because it actively use the fact
> that each phi-sources feeds a single phi-node while the underlying
> data structures are designed so that phi-sources can be shared
> between phi-nodes (but I think that allowing them the be effectively
> shared would bring too much problems).

Yeah, the phisource nodes are strange

 I was confused when I wrote the original phi code, and you fixed it
all up (to the point where I suspect none of my original confusion
exists ;^), but they are still a bit strange.

Could the phi_users list be just replaced by the target (OP_PHI)
pseudo (or instruction)?

           Linus

          Linus
Luc Van Oostenryck Nov. 17, 2020, 12:42 a.m. UTC | #4
On Mon, Nov 16, 2020 at 04:16:01PM -0800, Linus Torvalds wrote:
> 
> Could the phi_users list be just replaced by the target (OP_PHI)
> pseudo (or instruction)?

Yes, trivially if set liveness is calculated. But this
information could be useful earlier, then it would need some
work to track it (not sure it would be worth, though).

-- Luc
diff mbox series

Patch

diff --git a/flow.c b/flow.c
index 9ae8612a2312..e9c8f6b7355e 100644
--- a/flow.c
+++ b/flow.c
@@ -43,6 +43,25 @@  static int rewrite_branch(struct basic_block *bb,
 	return 1;
 }
 
+///
+// returns the phi-node corresponding to a phi-source
+static struct instruction *get_phinode(struct instruction *phisrc)
+{
+	struct pseudo_user *pu;
+
+	FOR_EACH_PTR(phisrc->target->users, pu) {
+		struct instruction *user;
+
+		if (!pu)
+			continue;
+		user = pu->insn;
+		assert(user->opcode == OP_PHI);
+		return user;
+	} END_FOR_EACH_PTR(pu);
+	assert(0);
+}
+
+
 /*
  * Return the known truth value of a pseudo, or -1 if
  * it's not known.
@@ -723,6 +742,24 @@  void vrfy_flow(struct entrypoint *ep)
 	assert(!entry);
 }
 
+static void adjust_phisrc(struct basic_block *top, struct instruction *insn)
+{
+	struct instruction *user = get_phinode(insn);
+	pseudo_t phi;
+
+	FOR_EACH_PTR(user->phi_list, phi) {
+		struct instruction *phisrc;
+
+		if (phi == VOID)
+			continue;
+		phisrc = phi->def;
+		if (phisrc->bb != top)
+			continue;
+		REPLACE_CURRENT_PTR(phi, VOID);
+		kill_instruction(phisrc);
+	} END_FOR_EACH_PTR(phi);
+}
+
 ///
 // merge two BBs
 // @top: the first BB to be merged
@@ -748,6 +785,11 @@  static int merge_bb(struct basic_block *top, struct basic_block *bot)
 		if (!insn->bb)
 			continue;
 		assert(insn->bb == bot);
+		switch (insn->opcode) {
+		case OP_PHISOURCE:
+			adjust_phisrc(top, insn);
+			break;
+		}
 		insn->bb = top;
 		add_instruction(&top->insns, insn);
 	} END_FOR_EACH_PTR(insn);
diff --git a/validation/optim/merge_bbe-adjust_phi.c b/validation/optim/merge_bbe-adjust_phi.c
index de4c54cc6d49..6a8ebb73a62d 100644
--- a/validation/optim/merge_bbe-adjust_phi.c
+++ b/validation/optim/merge_bbe-adjust_phi.c
@@ -16,7 +16,6 @@  int select(void)
 /*
  * check-name: merge_bbe-adjust_phi
  * check-command: test-linearize -Wno-decl $file
- * check-known-to-fail
  *
  * check-output-ignore
  * check-output-excludes: phisrc\\.