forked from M-Labs/artiq
compiler: fixed dead code eliminator
Instead of removing basic blocks with no predecessor, we will now mark and remove all blocks that are unreachable from the entry block. This can handle loops that are dead code. This is needed as we will now generate more complicated code for exception handling which the old dead code eliminator failed to handle.
This commit is contained in:
parent
da4ff44377
commit
6ec003c1c9
|
@ -135,6 +135,7 @@ class NamedValue(Value):
|
||||||
def __init__(self, typ, name):
|
def __init__(self, typ, name):
|
||||||
super().__init__(typ)
|
super().__init__(typ)
|
||||||
self.name, self.function = name, None
|
self.name, self.function = name, None
|
||||||
|
self.is_removed = False
|
||||||
|
|
||||||
def set_name(self, new_name):
|
def set_name(self, new_name):
|
||||||
if self.function is not None:
|
if self.function is not None:
|
||||||
|
@ -235,7 +236,7 @@ class Instruction(User):
|
||||||
self.drop_references()
|
self.drop_references()
|
||||||
# Check this after drop_references in case this
|
# Check this after drop_references in case this
|
||||||
# is a self-referencing phi.
|
# is a self-referencing phi.
|
||||||
assert not any(self.uses)
|
assert all(use.is_removed for use in self.uses)
|
||||||
|
|
||||||
def replace_with(self, value):
|
def replace_with(self, value):
|
||||||
self.replace_all_uses_with(value)
|
self.replace_all_uses_with(value)
|
||||||
|
@ -370,7 +371,7 @@ class BasicBlock(NamedValue):
|
||||||
self.remove_from_parent()
|
self.remove_from_parent()
|
||||||
# Check this after erasing instructions in case the block
|
# Check this after erasing instructions in case the block
|
||||||
# loops into itself.
|
# loops into itself.
|
||||||
assert not any(self.uses)
|
assert all(use.is_removed for use in self.uses)
|
||||||
|
|
||||||
def prepend(self, insn):
|
def prepend(self, insn):
|
||||||
assert isinstance(insn, Instruction)
|
assert isinstance(insn, Instruction)
|
||||||
|
@ -1360,14 +1361,6 @@ class LandingPad(Terminator):
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
return self.operands[0]
|
return self.operands[0]
|
||||||
|
|
||||||
def erase(self):
|
|
||||||
self.remove_from_parent()
|
|
||||||
# we should erase all clauses as well
|
|
||||||
for block in set(self.operands):
|
|
||||||
block.uses.remove(self)
|
|
||||||
block.erase()
|
|
||||||
assert not any(self.uses)
|
|
||||||
|
|
||||||
def clauses(self):
|
def clauses(self):
|
||||||
return zip(self.operands[1:], self.types)
|
return zip(self.operands[1:], self.types)
|
||||||
|
|
||||||
|
|
|
@ -15,13 +15,26 @@ class DeadCodeEliminator:
|
||||||
self.process_function(func)
|
self.process_function(func)
|
||||||
|
|
||||||
def process_function(self, func):
|
def process_function(self, func):
|
||||||
modified = True
|
# defer removing those blocks, so our use checks will ignore deleted blocks
|
||||||
while modified:
|
preserve = [func.entry()]
|
||||||
modified = False
|
work_list = [func.entry()]
|
||||||
for block in list(func.basic_blocks):
|
while any(work_list):
|
||||||
if not any(block.predecessors()) and block != func.entry():
|
block = work_list.pop()
|
||||||
|
for succ in block.successors():
|
||||||
|
if succ not in preserve:
|
||||||
|
preserve.append(succ)
|
||||||
|
work_list.append(succ)
|
||||||
|
|
||||||
|
to_be_removed = []
|
||||||
|
for block in func.basic_blocks:
|
||||||
|
if block not in preserve:
|
||||||
|
block.is_removed = True
|
||||||
|
to_be_removed.append(block)
|
||||||
|
for insn in block.instructions:
|
||||||
|
insn.is_removed = True
|
||||||
|
|
||||||
|
for block in to_be_removed:
|
||||||
self.remove_block(block)
|
self.remove_block(block)
|
||||||
modified = True
|
|
||||||
|
|
||||||
modified = True
|
modified = True
|
||||||
while modified:
|
while modified:
|
||||||
|
@ -42,6 +55,8 @@ class DeadCodeEliminator:
|
||||||
def remove_block(self, block):
|
def remove_block(self, block):
|
||||||
# block.uses are updated while iterating
|
# block.uses are updated while iterating
|
||||||
for use in set(block.uses):
|
for use in set(block.uses):
|
||||||
|
if use.is_removed:
|
||||||
|
continue
|
||||||
if isinstance(use, ir.Phi):
|
if isinstance(use, ir.Phi):
|
||||||
use.remove_incoming_block(block)
|
use.remove_incoming_block(block)
|
||||||
if not any(use.operands):
|
if not any(use.operands):
|
||||||
|
@ -56,6 +71,8 @@ class DeadCodeEliminator:
|
||||||
|
|
||||||
def remove_instruction(self, insn):
|
def remove_instruction(self, insn):
|
||||||
for use in set(insn.uses):
|
for use in set(insn.uses):
|
||||||
|
if use.is_removed:
|
||||||
|
continue
|
||||||
if isinstance(use, ir.Phi):
|
if isinstance(use, ir.Phi):
|
||||||
use.remove_incoming_value(insn)
|
use.remove_incoming_value(insn)
|
||||||
if not any(use.operands):
|
if not any(use.operands):
|
||||||
|
|
Loading…
Reference in New Issue