Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 9639a831bcad
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 00ec574d73e0
Choose a head ref
  • 2 commits
  • 4 files changed
  • 1 contributor

Commits on Nov 19, 2015

  1. transforms.llvm_ir_generator: accept delay instructions.

    The delay instruction is just like a branch (discontinuity
    in instruction flow), but it also carries metadata: how long
    did the execution of its basic block take. This metadata only
    matters during inlining and interleaving, so we treat it here
    as a mere branch.
    whitequark committed Nov 19, 2015
    Copy the full SHA
    025bfbe View commit details
  2. transforms.interleaver: implement (without inlining).

    whitequark committed Nov 19, 2015
    Copy the full SHA
    00ec574 View commit details
Showing with 93 additions and 6 deletions.
  1. +6 −0 artiq/compiler/ir.py
  2. +63 −6 artiq/compiler/transforms/interleaver.py
  3. +2 −0 artiq/compiler/transforms/llvm_ir_generator.py
  4. +22 −0 lit-test/test/interleaving/basic.py
6 changes: 6 additions & 0 deletions artiq/compiler/ir.py
Original file line number Diff line number Diff line change
@@ -953,6 +953,9 @@ def opcode(self):
def target(self):
return self.operands[0]

def set_target(self, new_target):
self.operands[0] = new_target

class BranchIf(Terminator):
"""
A conditional branch instruction.
@@ -1213,6 +1216,9 @@ def __init__(self, expr, substs, target, name=""):
def target(self):
return self.operands[0]

def set_target(self, new_target):
self.operands[0] = new_target

def substs(self):
return zip(self.var_names, self.operands[1:])

69 changes: 63 additions & 6 deletions artiq/compiler/transforms/interleaver.py
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
the timestamp would always monotonically nondecrease.
"""

from .. import ir
from .. import ir, iodelay
from ..analyses import domination

class Interleaver:
@@ -15,8 +15,65 @@ def process(self, functions):
self.process_function(func)

def process_function(self, func):
domtree = domination.PostDominatorTree(func)
print(func)
for block in func.basic_blocks:
idom = domtree.immediate_dominator(block)
print(block.name, "->", idom.name if idom else "<exit>")
for insn in func.instructions():
if isinstance(insn, ir.Delay):
if any(insn.expr.free_vars()):
# If a function has free variables in delay expressions,
# that means its IO delay depends on arguments.
# Do not change such functions in any way so that it will
# be successfully inlined and then removed by DCE.
return

postdom_tree = None
for insn in func.instructions():
if isinstance(insn, ir.Parallel):
# Lazily compute dominators.
if postdom_tree is None:
postdom_tree = domination.PostDominatorTree(func)

interleave_until = postdom_tree.immediate_dominator(insn.basic_block)
assert (interleave_until is not None) # no nonlocal flow in `with parallel`

target_block = insn.basic_block
target_time = 0
source_blocks = insn.basic_block.successors()
source_times = [0 for _ in source_blocks]

while len(source_blocks) > 0:
def iodelay_of_block(block):
terminator = block.terminator()
if isinstance(terminator, ir.Delay):
# We should be able to fold everything without free variables.
assert iodelay.is_const(terminator.expr)
return terminator.expr.value
else:
return 0

def time_after_block(pair):
index, block = pair
return source_times[index] + iodelay_of_block(block)

index, source_block = min(enumerate(source_blocks), key=time_after_block)
source_block_delay = iodelay_of_block(source_block)

target_terminator = target_block.terminator()
if isinstance(target_terminator, (ir.Delay, ir.Branch)):
target_terminator.set_target(source_block)
elif isinstance(target_terminator, ir.Parallel):
target_terminator.replace_with(ir.Branch(source_block))
else:
assert False

new_source_block = postdom_tree.immediate_dominator(source_block)
assert (new_source_block is not None)

target_block = source_block
target_time += source_block_delay

if new_source_block == interleave_until:
# We're finished with this branch.
del source_blocks[index]
del source_times[index]
else:
source_blocks[index] = new_source_block
source_times[index] = target_time
2 changes: 2 additions & 0 deletions artiq/compiler/transforms/llvm_ir_generator.py
Original file line number Diff line number Diff line change
@@ -1068,6 +1068,8 @@ def process_Select(self, insn):
def process_Branch(self, insn):
return self.llbuilder.branch(self.map(insn.target()))

process_Delay = process_Branch

def process_BranchIf(self, insn):
return self.llbuilder.cbranch(self.map(insn.condition()),
self.map(insn.if_true()), self.map(insn.if_false()))
22 changes: 22 additions & 0 deletions lit-test/test/interleaving/basic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# RUN: %python -m artiq.compiler.testbench.jit %s >%t
# RUN: OutputCheck %s --file-to-check=%t

def g():
with parallel:
with sequential:
print("A", now_mu())
delay_mu(3)
print("B", now_mu())
with sequential:
print("C", now_mu())
delay_mu(2)
print("D", now_mu())
delay_mu(2)
print("E", now_mu())

# CHECK-L: C 0
# CHECK-L: A 2
# CHECK-L: D 5
# CHECK-L: B 7
# CHECK-L: E 7
g()