2
0
mirror of https://github.com/m-labs/artiq.git synced 2024-12-22 18:04:03 +08:00

compiler: Catch escaping numpy.{array, full, transpose}() results

Function calls in general can still be used to hide escaping
allocations from the compiler (issue ), but these calls in
particular always allocate, so we can easily and accurately handle
them.
This commit is contained in:
David Nadlinger 2023-10-08 14:10:00 +01:00 committed by Sébastien Bourdeauducq
parent 7ab52af603
commit 08eea09d44
5 changed files with 62 additions and 20 deletions

View File

@ -102,8 +102,20 @@ class RegionOf(algorithm.Visitor):
if types.is_external_function(node.func.type, "cache_get"): if types.is_external_function(node.func.type, "cache_get"):
# The cache is borrow checked dynamically # The cache is borrow checked dynamically
return Global() return Global()
else:
self.visit_sometimes_allocating(node) if (types.is_builtin_function(node.func.type, "array")
or types.is_builtin_function(node.func.type, "make_array")
or types.is_builtin_function(node.func.type, "numpy.transpose")):
# While lifetime tracking across function calls in general is currently
# broken (see below), these special builtins that allocate an array on
# the stack of the caller _always_ allocate regardless of the parameters,
# and we can thus handle them without running into the precision issue
# mentioned in commit ae999db.
return self.visit_allocating(node)
# FIXME: Return statement missing here, but see m-labs/artiq#1497 and
# commit ae999db.
self.visit_sometimes_allocating(node)
# Value lives as long as the object/container, if it's mutable, # Value lives as long as the object/container, if it's mutable,
# or else forever # or else forever

View File

@ -0,0 +1,15 @@
# RUN: %python -m artiq.compiler.testbench.embedding +diag %s 2>%t
# RUN: OutputCheck %s --file-to-check=%t
from artiq.experiment import *
import numpy as np
@kernel
def a():
# CHECK-L: ${LINE:+2}: error: cannot return an allocated value that does not live forever
# CHECK-L: ${LINE:+1}: note: ... to this point
return np.array([0, 1])
@kernel
def entrypoint():
a()

View File

@ -0,0 +1,16 @@
# RUN: %python -m artiq.compiler.testbench.embedding +diag %s 2>%t
# RUN: OutputCheck %s --file-to-check=%t
from artiq.experiment import *
import numpy as np
@kernel
def a():
# CHECK-L: ${LINE:+2}: error: cannot return an allocated value that does not live forever
# CHECK-L: ${LINE:+1}: note: ... to this point
return np.full(10, 42.0)
@kernel
def entrypoint():
a()

View File

@ -0,0 +1,17 @@
# RUN: %python -m artiq.compiler.testbench.embedding +diag %s 2>%t
# RUN: OutputCheck %s --file-to-check=%t
from artiq.experiment import *
import numpy as np
data = np.array([[0, 1], [2, 3]])
@kernel
def a():
# CHECK-L: ${LINE:+2}: error: cannot return an allocated value that does not live forever
# CHECK-L: ${LINE:+1}: note: ... to this point
return np.transpose(data)
@kernel
def entrypoint():
a()

View File

@ -95,24 +95,6 @@ tracked across function calls (see `#1497 <https://github.com/m-labs/artiq/issue
def run(self): def run(self):
# results in memory corruption # results in memory corruption
return func([1, 2, 3]) return func([1, 2, 3])
or if the return value is obfuscated by an if-statement like here: ::
class ProblemReturn2(EnvExperiment):
def build(self):
self.setattr_device("core")
@kernel
def meth(self):
# if statement for obfuscation
if self.core.get_rtio_counter_mu() % 2:
return np.array([1,2,3])
else:
return np.array([4,5,6])
@kernel
def run(self):
# also results in memory corrption
return self.meth()
This results in memory corruption at runtime. This results in memory corruption at runtime.