Skip to content

Commit

Permalink
compiler: implement local variable demotion.
Browse files Browse the repository at this point in the history
  • Loading branch information
whitequark committed May 19, 2018
1 parent fd110e9 commit 9b4ad8b
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 0 deletions.
2 changes: 2 additions & 0 deletions artiq/compiler/module.py
Expand Up @@ -60,6 +60,7 @@ def __init__(self, src, ref_period=1e-6, attribute_writeback=True, remarks=False
ref_period=ref_period)
dead_code_eliminator = transforms.DeadCodeEliminator(engine=self.engine)
local_access_validator = validators.LocalAccessValidator(engine=self.engine)
local_demoter = transforms.LocalDemoter()
devirtualization = analyses.Devirtualization()
interleaver = transforms.Interleaver(engine=self.engine)
invariant_detection = analyses.InvariantDetection(engine=self.engine)
Expand All @@ -77,6 +78,7 @@ def __init__(self, src, ref_period=1e-6, attribute_writeback=True, remarks=False
dead_code_eliminator.process(self.artiq_ir)
interleaver.process(self.artiq_ir)
local_access_validator.process(self.artiq_ir)
local_demoter.process(self.artiq_ir)
if remarks:
invariant_detection.process(self.artiq_ir)

Expand Down
1 change: 1 addition & 0 deletions artiq/compiler/transforms/__init__.py
Expand Up @@ -5,6 +5,7 @@
from .iodelay_estimator import IODelayEstimator
from .artiq_ir_generator import ARTIQIRGenerator
from .dead_code_eliminator import DeadCodeEliminator
from .local_demoter import LocalDemoter
from .llvm_ir_generator import LLVMIRGenerator
from .interleaver import Interleaver
from .typedtree_printer import TypedtreePrinter
51 changes: 51 additions & 0 deletions artiq/compiler/transforms/local_demoter.py
@@ -0,0 +1,51 @@
"""
:class:`LocalDemoter` is a constant propagation transform:
it replaces reads of any local variable with only one write
in a function without closures with the value that was written.
:class:`LocalAccessValidator` must be run before this transform
to ensure that the transformation it performs is sound.
"""

from collections import defaultdict
from .. import ir

class LocalDemoter:
def process(self, functions):
for func in functions:
self.process_function(func)

def process_function(self, func):
env_safe = {}
env_gets = defaultdict(lambda: set())
env_sets = defaultdict(lambda: set())

for insn in func.instructions():
if isinstance(insn, (ir.GetLocal, ir.SetLocal)):
if "$" in insn.var_name:
continue

env = insn.environment()

if env not in env_safe:
for use in env.uses:
if not isinstance(use, (ir.GetLocal, ir.SetLocal)):
env_safe[env] = False
break
else:
env_safe[env] = True

if not env_safe[env]:
continue

if isinstance(insn, ir.SetLocal):
env_sets[(env, insn.var_name)].add(insn)
else:
env_gets[(env, insn.var_name)].add(insn)

for (env, var_name) in env_sets:
if len(env_sets[(env, var_name)]) == 1:
set_insn = next(iter(env_sets[(env, var_name)]))
for get_insn in env_gets[(env, var_name)]:
get_insn.replace_all_uses_with(set_insn.value())
get_insn.erase()
15 changes: 15 additions & 0 deletions artiq/test/lit/local_demotion/closure.py
@@ -0,0 +1,15 @@
# RUN: %python -m artiq.compiler.testbench.irgen %s >%t
# RUN: OutputCheck %s --file-to-check=%t

def x(y): pass

# CHECK-L: NoneType input.a(environment(...) %ARG.ENV, NoneType %ARG.self) {
# CHECK-L: setlocal('self') %ENV, NoneType %ARG.self
# CHECK-NOT-L: call (y:NoneType)->NoneType %LOC.x, NoneType %ARG.self

def a(self):
def b():
pass
x(self)

a(None)
13 changes: 13 additions & 0 deletions artiq/test/lit/local_demotion/demotion.py
@@ -0,0 +1,13 @@
# RUN: %python -m artiq.compiler.testbench.irgen %s >%t
# RUN: OutputCheck %s --file-to-check=%t

def x(y): pass

# CHECK-L: NoneType input.a(environment(...) %ARG.ENV, NoneType %ARG.self) {
# CHECK-NOT-L: getlocal('self') %ENV
# CHECK-L: call (y:NoneType)->NoneType %LOC.x, NoneType %ARG.self

def a(self):
x(self)

a(None)

0 comments on commit 9b4ad8b

Please sign in to comment.