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: 27a697920ac5
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: 0e26cfb66eef
Choose a head ref
  • 2 commits
  • 7 files changed
  • 1 contributor

Commits on Aug 22, 2015

  1. Remove debug print.

    whitequark committed Aug 22, 2015
    Copy the full SHA
    b39e76a View commit details
  2. Copy the full SHA
    0e26cfb View commit details
2 changes: 1 addition & 1 deletion artiq/compiler/module.py
Original file line number Diff line number Diff line change
@@ -60,7 +60,7 @@ def __init__(self, src):
escape_validator.visit(src.typedtree)
self.artiq_ir = artiq_ir_generator.visit(src.typedtree)
dead_code_eliminator.process(self.artiq_ir)
# local_access_validator.process(self.artiq_ir)
local_access_validator.process(self.artiq_ir)

def build_llvm_ir(self, target):
"""Compile the module to LLVM IR for the specified target."""
2 changes: 0 additions & 2 deletions artiq/compiler/transforms/artiq_ir_generator.py
Original file line number Diff line number Diff line change
@@ -714,8 +714,6 @@ def visit_AttributeT(self, node):
if node.attr not in obj.type.find().attributes:
# A class attribute. Get the constructor (class object) and
# extract the attribute from it.
print(node)
print(obj)
constr_type = obj.type.constructor
constr = self.append(ir.GetConstructor(self._env_for(constr_type.name),
constr_type.name, constr_type,
9 changes: 5 additions & 4 deletions artiq/compiler/transforms/dead_code_eliminator.py
Original file line number Diff line number Diff line change
@@ -14,10 +14,11 @@ def process(self, functions):
self.process_function(func)

def process_function(self, func):
for block in func.basic_blocks:
if not any(block.predecessors()) and \
not any([isinstance(use, ir.SetLocal) for use in block.uses]) and \
block != func.entry():
for block in list(func.basic_blocks):
if not any(block.predecessors()) and block != func.entry():
for use in set(block.uses):
if isinstance(use, ir.SetLocal):
use.erase()
self.remove_block(block)

def remove_block(self, block):
48 changes: 34 additions & 14 deletions artiq/compiler/validators/local_access.py
Original file line number Diff line number Diff line change
@@ -16,11 +16,13 @@ def process(self, functions):
self.process_function(func)

def process_function(self, func):
# Find all environments allocated in this func.
environments = []
# Find all environments and closures allocated in this func.
environments, closures = [], []
for insn in func.instructions():
if isinstance(insn, ir.Alloc) and ir.is_environment(insn.type):
environments.append(insn)
elif isinstance(insn, ir.Closure):
closures.append(insn)

# Compute initial state of interesting environments.
# Environments consisting only of internal variables (containing a ".")
@@ -82,6 +84,7 @@ def pred_at_fault(env, var_name):
# It's the entry block and it was never initialized.
return None

set_local_in_this_frame = False
if isinstance(insn, (ir.SetLocal, ir.GetLocal)) and \
"." not in insn.var_name:
env, var_name = insn.environment(), insn.var_name
@@ -91,24 +94,41 @@ def pred_at_fault(env, var_name):
if isinstance(insn, ir.SetLocal):
# We've just initialized it.
block_state[env][var_name] = True
set_local_in_this_frame = True
else: # isinstance(insn, ir.GetLocal)
if not block_state[env][var_name]:
# Oops, accessing it uninitialized.
self._uninitialized_access(insn, var_name,
pred_at_fault(env, var_name))

# Creating a closure has no side effects. However, using a closure does.
for operand in insn.operands:
if isinstance(operand, ir.Closure):
env = operand.environment()
# Make sure this environment has any interesting variables.
if env in block_state:
for var_name in block_state[env]:
if not block_state[env][var_name]:
# A closure would capture this variable while it is not always
# initialized. Note that this check is transitive.
self._uninitialized_access(operand, var_name,
pred_at_fault(env, var_name))
closures_to_check = []

if (isinstance(insn, (ir.SetLocal, ir.SetAttr, ir.SetElem)) and
not set_local_in_this_frame):
# Closures may escape via these mechanisms and be invoked elsewhere.
if isinstance(insn.value(), ir.Closure):
closures_to_check.append(insn.value())

if isinstance(insn, (ir.Call, ir.Invoke)):
# We can't always trace the flow of closures from point of
# definition to point of call; however, we know that, by transitiveness
# of this analysis, only closures defined in this function can contain
# uninitialized variables.
#
# Thus, enumerate the closures, and check all of them during any operation
# that may eventually result in the closure being called.
closures_to_check = closures

for closure in closures_to_check:
env = closure.environment()
# Make sure this environment has any interesting variables.
if env in block_state:
for var_name in block_state[env]:
if not block_state[env][var_name]:
# A closure would capture this variable while it is not always
# initialized. Note that this check is transitive.
self._uninitialized_access(closure, var_name,
pred_at_fault(env, var_name))

# Save the state.
state[block] = block_state
44 changes: 22 additions & 22 deletions lit-test/test/integration/finally.py
Original file line number Diff line number Diff line change
@@ -13,11 +13,6 @@ def f():
print("f-finally")
print("f-out")

# CHECK-L: f-try
# CHECK-L: f-finally
# CHECK-L: f-out
f()

def g():
x = True
while x:
@@ -29,11 +24,6 @@ def g():
print("g-finally")
print("g-out")

# CHECK-L: g-try
# CHECK-L: g-finally
# CHECK-L: g-out
g()

def h():
try:
print("h-try")
@@ -43,12 +33,6 @@ def h():
print("h-out")
return 20

# CHECK-L: h-try
# CHECK-L: h-finally
# CHECK-NOT-L: h-out
# CHECK-L: h 10
print("h", h())

def i():
try:
print("i-try")
@@ -59,19 +43,35 @@ def i():
print("i-out")
return 20

# CHECK-L: i-try
# CHECK-L: i-finally
# CHECK-NOT-L: i-out
# CHECK-L: i 30
print("i", i())

def j():
try:
print("j-try")
finally:
print("j-finally")
print("j-out")

# CHECK-L: f-try
# CHECK-L: f-finally
# CHECK-L: f-out
f()

# CHECK-L: g-try
# CHECK-L: g-finally
# CHECK-L: g-out
g()

# CHECK-L: h-try
# CHECK-L: h-finally
# CHECK-NOT-L: h-out
# CHECK-L: h 10
print("h", h())

# CHECK-L: i-try
# CHECK-L: i-finally
# CHECK-NOT-L: i-out
# CHECK-L: i 30
print("i", i())

# CHECK-L: j-try
# CHECK-L: j-finally
# CHECK-L: j-out
15 changes: 15 additions & 0 deletions lit-test/test/local_access/invalid_closure.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# RUN: %python -m artiq.compiler.testbench.signature +diag %s >%t
# RUN: OutputCheck %s --file-to-check=%t

if False:
t = 1

# CHECK-L: ${LINE:+1}: error: variable 't' can be captured in a closure uninitialized
l = lambda: t

# CHECK-L: ${LINE:+1}: error: variable 't' can be captured in a closure uninitialized
def f():
return t

l()
f()
Original file line number Diff line number Diff line change
@@ -18,10 +18,3 @@
t = 1
# CHECK-L: ${LINE:+1}: error: variable 't' is not always initialized
-t

# CHECK-L: ${LINE:+1}: error: variable 't' can be captured in a closure uninitialized
l = lambda: t

# CHECK-L: ${LINE:+1}: error: variable 't' can be captured in a closure uninitialized
def f():
return t