Skip to content

Static check for noalloc and friends #707

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 15 commits into from

Conversation

gretay-js
Copy link
Contributor

@gretay-js gretay-js commented Jun 29, 2022

This PR adds an experimental new pass on Mach that statically checks certain interprocedural properties of functions and records the results in Cmx. The checks are off by default (except on the standard library) and can be turned on using compiler flags -noalloc-check, -noeffect-check, -noindirect-calls-check.

A function is "noalloc" if it satisfies the following conditions:

  • does not contain any allocations instructions on the heap (local allocations are ignored)
  • does not contain any indirect calls (incl. no indirect tailcalls)
  • raise_notrace is allowed, but no other raise kinds
  • all direct calls (incl. tailcalls and probe handlers) are to functions that satisfy the same conditions.

A function is "noalloc_exn" if it is noalloc except that any raise instructions are allowed and there are no constraints on instructions that are post-dominated by a raise with backtrace (with a somewhat conservative interpretation for raise inside loops and try-with). This is probably the most useful check in practice, as it allows allocation on error paths.

A function is "noffect" if it is noalloc and does not contain any instructions that write to heap-allocated memory. It may be useful to have a version for "noeffect_exn" similarly to "noalloc_exn" above.

A function is "noindirect_calls" if it does not contain any raise except raise_notrace, does not contain any indirect calls, and all direct calls are to functions that are "noindirect_calls". I'm not sure how useful the indirect calls check is, but it came for free.

These checks are combined with source level annotations on functions:

  • [@assert noalloc] on a function that cannot be shown statically to be "noalloc" results in a compilation error when compiled with -alloc-check flag.
  • [@assume noalloc] is an escape hatch that would treat a function as "noalloc" without checking.
    Similarly, for other properties above.

The analysis is implemented on Mach after all optimizations that can possibly remove allocation. It works the same way for all "middle-ends", but adds function symbol names to cmx files. To reduce the overhead, cmx track names that pass the checks and don't duplicate, using the following implication between properties:

"noeffects" => "noalloc" => "noalloc_exn" and "noalloc" => "noindirect_calls"

@gretay-js gretay-js force-pushed the alloc-check branch 2 times, most recently from 8eda9ac to db8a39f Compare June 30, 2022 17:35
@gretay-js gretay-js marked this pull request as ready for review June 30, 2022 17:41
unix has become  a dependency of boot, so we can't build it with
the new flags, which means the check will fail on anything
that calls into Unix module. This will need to be fixed,
but for now just removing the flags to re-enable the build.
@gretay-js
Copy link
Contributor Author

I'm splitting this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant