Data flow resolution code assumed that basic blocks always contain
some struct insn, before which spill/reload instructions can be
put. When data flow resolution was run on empty basic block, it worked
on invalid struct insn pointer and entered infinite loop.
This patch also fixes another bug. Data flow resolution didn't work
correctly when basic block in which spill occurs was not ended by a
branch. That's because in this case spill was inserted after the last
instruction, which is correct, but the corresponding spill slot moves
(push+pop) were inserted before last instruction - before the slot was
assigned a correct value.
Empty basic blocks can be a result of the following bytecode:
iconst_0
iconst_1
ifeq lab
pop // This and the next instruction generate no
iconst_1 // LIR instructions.
lab:
Cc: Arthur HUILLET arthur.huil...@free.fr
Signed-off-by: Tomek Grabiec tgrab...@gmail.com
---
include/jit/basic-block.h |1 -
jit/basic-block.c | 19
jit/spill-reload.c| 70 ++---
3 files changed, 53 insertions(+), 37 deletions(-)
diff --git a/include/jit/basic-block.h b/include/jit/basic-block.h
index e50f6c6..d5452a4 100644
--- a/include/jit/basic-block.h
+++ b/include/jit/basic-block.h
@@ -81,7 +81,6 @@ struct basic_block *bb_split(struct basic_block *, unsigned
long);
void bb_add_stmt(struct basic_block *, struct statement *);
void bb_add_insn(struct basic_block *, struct insn *);
struct insn *bb_first_insn(struct basic_block *);
-struct insn *bb_last_insn(struct basic_block *);
int bb_add_successor(struct basic_block *, struct basic_block *);
int bb_add_mimic_stack_expr(struct basic_block *, struct expression *);
struct statement *bb_remove_last_stmt(struct basic_block *bb);
diff --git a/jit/basic-block.c b/jit/basic-block.c
index 6de5c3f..bcb2866 100644
--- a/jit/basic-block.c
+++ b/jit/basic-block.c
@@ -174,25 +174,6 @@ struct insn *bb_first_insn(struct basic_block *bb)
return list_entry(bb-insn_list.next, struct insn, insn_list_node);
}
-struct insn *bb_last_insn(struct basic_block *bb)
-{
- struct insn *this = list_entry(bb-insn_list.prev, struct insn,
insn_list_node);
-
- /*
-* We want to return the last real instruction of the basic block.
Taking the
-* last of the insn_list will not work in case a live interval has been
spilled
-* right after the final jump of the basic block.
-* This is a side effect of the linear scan algorithm.
-*
-* As a result, we browse instructions starting from the last, in order
to find the one
-* that has a LIR position matching the position for the end of the
block.
-*/
- while (this-lir_pos != bb-end_insn - 1) {
- this = list_entry(this-insn_list_node.prev, struct insn,
insn_list_node);
- }
- return this;
-}
-
static int __bb_add_neighbor(void *new, void **array, unsigned long *nb)
{
unsigned long new_size;
diff --git a/jit/spill-reload.c b/jit/spill-reload.c
index 5ced80e..af50046 100644
--- a/jit/spill-reload.c
+++ b/jit/spill-reload.c
@@ -25,6 +25,7 @@
*/
#include jit/compilation-unit.h
+#include jit/instruction.h
#include jit/stack-slot.h
#include jit/compiler.h
@@ -65,10 +66,40 @@ static struct insn *last_insn(struct compilation_unit *cu,
struct live_interval
return ret;
}
+/**
+ * Returns the node before which spill instructions should be inserted
+ * when they are supposed to be executed just before control leaves
+ * given basic block. When basic block is ended with a branch
+ * instruction it returns node of that branch; otherwise it returns
+ * the next node.
+ */
+static struct list_head *bb_last_spill_node(struct basic_block *bb)
+{
+ struct insn *last;
+
+ /*
+* basic block's instruction list might not be empty and
+* contain only spill/reload instructions. In this situation
+* we also consider basic block as empty (no lir positions) so
+* we don't rely on -insn_list here.
+*/
+ if (bb-end_insn == bb-start_insn)
+ return bb-insn_list;
+
+ last = radix_tree_lookup(bb-b_parent-lir_insn_map, bb-end_insn - 1);
+ assert(last);
+
+ if (insn_is_branch(last))
+ return last-insn_list_node;
+
+ return last-insn_list_node.next;
+}
+
static struct stack_slot *
spill_interval(struct live_interval *interval,
struct compilation_unit *cu,
- struct insn *last)
+ struct list_head *spill_before,
+ unsigned long bc_offset)
{
struct stack_slot *slot;
struct insn *spill;
@@ -81,21 +112,23 @@ spill_interval(struct live_interval *interval,
if (!spill)
return NULL;
- spill-bytecode_offset = last-bytecode_offset;
-
- if (insn_is_branch(last))
- list_add_tail(spill-insn_list_node,