forked from M-Labs/artiq
LocalAccessValidator: relax restrictions to accept def f(); def g().
This commit is contained in:
parent
b39e76ae28
commit
0e26cfb66e
|
@ -60,7 +60,7 @@ class Module:
|
||||||
escape_validator.visit(src.typedtree)
|
escape_validator.visit(src.typedtree)
|
||||||
self.artiq_ir = artiq_ir_generator.visit(src.typedtree)
|
self.artiq_ir = artiq_ir_generator.visit(src.typedtree)
|
||||||
dead_code_eliminator.process(self.artiq_ir)
|
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):
|
def build_llvm_ir(self, target):
|
||||||
"""Compile the module to LLVM IR for the specified target."""
|
"""Compile the module to LLVM IR for the specified target."""
|
||||||
|
|
|
@ -14,10 +14,11 @@ class DeadCodeEliminator:
|
||||||
self.process_function(func)
|
self.process_function(func)
|
||||||
|
|
||||||
def process_function(self, func):
|
def process_function(self, func):
|
||||||
for block in func.basic_blocks:
|
for block in list(func.basic_blocks):
|
||||||
if not any(block.predecessors()) and \
|
if not any(block.predecessors()) and block != func.entry():
|
||||||
not any([isinstance(use, ir.SetLocal) for use in block.uses]) and \
|
for use in set(block.uses):
|
||||||
block != func.entry():
|
if isinstance(use, ir.SetLocal):
|
||||||
|
use.erase()
|
||||||
self.remove_block(block)
|
self.remove_block(block)
|
||||||
|
|
||||||
def remove_block(self, block):
|
def remove_block(self, block):
|
||||||
|
|
|
@ -16,11 +16,13 @@ class LocalAccessValidator:
|
||||||
self.process_function(func)
|
self.process_function(func)
|
||||||
|
|
||||||
def process_function(self, func):
|
def process_function(self, func):
|
||||||
# Find all environments allocated in this func.
|
# Find all environments and closures allocated in this func.
|
||||||
environments = []
|
environments, closures = [], []
|
||||||
for insn in func.instructions():
|
for insn in func.instructions():
|
||||||
if isinstance(insn, ir.Alloc) and ir.is_environment(insn.type):
|
if isinstance(insn, ir.Alloc) and ir.is_environment(insn.type):
|
||||||
environments.append(insn)
|
environments.append(insn)
|
||||||
|
elif isinstance(insn, ir.Closure):
|
||||||
|
closures.append(insn)
|
||||||
|
|
||||||
# Compute initial state of interesting environments.
|
# Compute initial state of interesting environments.
|
||||||
# Environments consisting only of internal variables (containing a ".")
|
# Environments consisting only of internal variables (containing a ".")
|
||||||
|
@ -82,6 +84,7 @@ class LocalAccessValidator:
|
||||||
# It's the entry block and it was never initialized.
|
# It's the entry block and it was never initialized.
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
set_local_in_this_frame = False
|
||||||
if isinstance(insn, (ir.SetLocal, ir.GetLocal)) and \
|
if isinstance(insn, (ir.SetLocal, ir.GetLocal)) and \
|
||||||
"." not in insn.var_name:
|
"." not in insn.var_name:
|
||||||
env, var_name = insn.environment(), insn.var_name
|
env, var_name = insn.environment(), insn.var_name
|
||||||
|
@ -91,23 +94,40 @@ class LocalAccessValidator:
|
||||||
if isinstance(insn, ir.SetLocal):
|
if isinstance(insn, ir.SetLocal):
|
||||||
# We've just initialized it.
|
# We've just initialized it.
|
||||||
block_state[env][var_name] = True
|
block_state[env][var_name] = True
|
||||||
|
set_local_in_this_frame = True
|
||||||
else: # isinstance(insn, ir.GetLocal)
|
else: # isinstance(insn, ir.GetLocal)
|
||||||
if not block_state[env][var_name]:
|
if not block_state[env][var_name]:
|
||||||
# Oops, accessing it uninitialized.
|
# Oops, accessing it uninitialized.
|
||||||
self._uninitialized_access(insn, var_name,
|
self._uninitialized_access(insn, var_name,
|
||||||
pred_at_fault(env, var_name))
|
pred_at_fault(env, var_name))
|
||||||
|
|
||||||
# Creating a closure has no side effects. However, using a closure does.
|
closures_to_check = []
|
||||||
for operand in insn.operands:
|
|
||||||
if isinstance(operand, ir.Closure):
|
if (isinstance(insn, (ir.SetLocal, ir.SetAttr, ir.SetElem)) and
|
||||||
env = operand.environment()
|
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.
|
# Make sure this environment has any interesting variables.
|
||||||
if env in block_state:
|
if env in block_state:
|
||||||
for var_name in block_state[env]:
|
for var_name in block_state[env]:
|
||||||
if not block_state[env][var_name]:
|
if not block_state[env][var_name]:
|
||||||
# A closure would capture this variable while it is not always
|
# A closure would capture this variable while it is not always
|
||||||
# initialized. Note that this check is transitive.
|
# initialized. Note that this check is transitive.
|
||||||
self._uninitialized_access(operand, var_name,
|
self._uninitialized_access(closure, var_name,
|
||||||
pred_at_fault(env, var_name))
|
pred_at_fault(env, var_name))
|
||||||
|
|
||||||
# Save the state.
|
# Save the state.
|
||||||
|
|
|
@ -13,11 +13,6 @@ def f():
|
||||||
print("f-finally")
|
print("f-finally")
|
||||||
print("f-out")
|
print("f-out")
|
||||||
|
|
||||||
# CHECK-L: f-try
|
|
||||||
# CHECK-L: f-finally
|
|
||||||
# CHECK-L: f-out
|
|
||||||
f()
|
|
||||||
|
|
||||||
def g():
|
def g():
|
||||||
x = True
|
x = True
|
||||||
while x:
|
while x:
|
||||||
|
@ -29,11 +24,6 @@ def g():
|
||||||
print("g-finally")
|
print("g-finally")
|
||||||
print("g-out")
|
print("g-out")
|
||||||
|
|
||||||
# CHECK-L: g-try
|
|
||||||
# CHECK-L: g-finally
|
|
||||||
# CHECK-L: g-out
|
|
||||||
g()
|
|
||||||
|
|
||||||
def h():
|
def h():
|
||||||
try:
|
try:
|
||||||
print("h-try")
|
print("h-try")
|
||||||
|
@ -43,12 +33,6 @@ def h():
|
||||||
print("h-out")
|
print("h-out")
|
||||||
return 20
|
return 20
|
||||||
|
|
||||||
# CHECK-L: h-try
|
|
||||||
# CHECK-L: h-finally
|
|
||||||
# CHECK-NOT-L: h-out
|
|
||||||
# CHECK-L: h 10
|
|
||||||
print("h", h())
|
|
||||||
|
|
||||||
def i():
|
def i():
|
||||||
try:
|
try:
|
||||||
print("i-try")
|
print("i-try")
|
||||||
|
@ -59,12 +43,6 @@ def i():
|
||||||
print("i-out")
|
print("i-out")
|
||||||
return 20
|
return 20
|
||||||
|
|
||||||
# CHECK-L: i-try
|
|
||||||
# CHECK-L: i-finally
|
|
||||||
# CHECK-NOT-L: i-out
|
|
||||||
# CHECK-L: i 30
|
|
||||||
print("i", i())
|
|
||||||
|
|
||||||
def j():
|
def j():
|
||||||
try:
|
try:
|
||||||
print("j-try")
|
print("j-try")
|
||||||
|
@ -72,6 +50,28 @@ def j():
|
||||||
print("j-finally")
|
print("j-finally")
|
||||||
print("j-out")
|
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-try
|
||||||
# CHECK-L: j-finally
|
# CHECK-L: j-finally
|
||||||
# CHECK-L: j-out
|
# CHECK-L: j-out
|
||||||
|
|
|
@ -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()
|
|
@ -18,10 +18,3 @@ else:
|
||||||
t = 1
|
t = 1
|
||||||
# CHECK-L: ${LINE:+1}: error: variable 't' is not always initialized
|
# CHECK-L: ${LINE:+1}: error: variable 't' is not always initialized
|
||||||
-t
|
-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
|
|
Loading…
Reference in New Issue