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):
|
||||
super().__init__(typ)
|
||||
self.name, self.function = name, None
|
||||
self.is_removed = False
|
||||
|
||||
def set_name(self, new_name):
|
||||
if self.function is not None:
|
||||
|
@ -235,7 +236,7 @@ class Instruction(User):
|
|||
self.drop_references()
|
||||
# Check this after drop_references in case this
|
||||
# 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):
|
||||
self.replace_all_uses_with(value)
|
||||
|
@ -370,7 +371,7 @@ class BasicBlock(NamedValue):
|
|||
self.remove_from_parent()
|
||||
# Check this after erasing instructions in case the block
|
||||
# loops into itself.
|
||||
assert not any(self.uses)
|
||||
assert all(use.is_removed for use in self.uses)
|
||||
|
||||
def prepend(self, insn):
|
||||
assert isinstance(insn, Instruction)
|
||||
|
@ -1360,14 +1361,6 @@ class LandingPad(Terminator):
|
|||
def cleanup(self):
|
||||
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):
|
||||
return zip(self.operands[1:], self.types)
|
||||
|
||||
|
|
|
@ -15,13 +15,26 @@ class DeadCodeEliminator:
|
|||
self.process_function(func)
|
||||
|
||||
def process_function(self, func):
|
||||
modified = True
|
||||
while modified:
|
||||
modified = False
|
||||
for block in list(func.basic_blocks):
|
||||
if not any(block.predecessors()) and block != func.entry():
|
||||
self.remove_block(block)
|
||||
modified = True
|
||||
# defer removing those blocks, so our use checks will ignore deleted blocks
|
||||
preserve = [func.entry()]
|
||||
work_list = [func.entry()]
|
||||
while any(work_list):
|
||||
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)
|
||||
|
||||
modified = True
|
||||
while modified:
|
||||
|
@ -42,6 +55,8 @@ class DeadCodeEliminator:
|
|||
def remove_block(self, block):
|
||||
# block.uses are updated while iterating
|
||||
for use in set(block.uses):
|
||||
if use.is_removed:
|
||||
continue
|
||||
if isinstance(use, ir.Phi):
|
||||
use.remove_incoming_block(block)
|
||||
if not any(use.operands):
|
||||
|
@ -56,6 +71,8 @@ class DeadCodeEliminator:
|
|||
|
||||
def remove_instruction(self, insn):
|
||||
for use in set(insn.uses):
|
||||
if use.is_removed:
|
||||
continue
|
||||
if isinstance(use, ir.Phi):
|
||||
use.remove_incoming_value(insn)
|
||||
if not any(use.operands):
|
||||
|
|
Loading…
Reference in New Issue