locking macro creates monitor bytecode difficult to analyze in Graal native-image and ART runtime
Description
Android ART runs compile time verification on bytecode and fails on some usages of the locking macro. Example:
Android error:
Graal fails with this error:
Cause: The locking macro emits a try block. When not in tail position, try blocks get lifted into lambdas, and closed over variables (including, importantly, the lockee) become ‘fields’ of the lambda object. Thus in that situation the monitor enter/exit calls were being made on a field. Graal doesn’t trust that the value of that field will be the same in the enter and exit calls, so it balks that they might not be a balanced pair. The solution is to get the lockee onto the stack so Graal can understand that it is the same target. The problem has nothing to do with exception handling blocks or failure to emulate javac’s infinite loop trick.
Approach: We need to get the lockee on the stack, even when lifting occurs, and use the same stack variable in try/finally. Thus we need a let around try/finally. But lifting can still happen, so that let needs to be in what’s lifted. That’s the role of the outer try. The outer try will be lifted, and the let with it, the let will put the field on the stack, Graal sees enter/exit on the same stack value and its verifier is thus happy. The inner try will always be in tail position.
Patch: clj-1472-5.patch
Environment
Android ART runtime
Activity
The commit fixing this is at https://github.com/clojure/clojure/commit/f5403e9c666f3281fdb880cb2c21303c273eed2d.
Applied for 1.10.2
I have verified that the new clj-1472-5.patch resolves the issue per instructions by Lee Read with Graal 19, existing Clojure tests all pass, core.memoize code is ok.
The latest Graal (20) works under Java 8, but fails with different errors under Java 11.
Repro instructions from Lee Read:
https://github.com/lread/clj-graal-docs/blob/master/CLJ-1472/steps-to-reproduce.md
This code in core.memoize is broken with CLJ-1472-4 as the locking thunk is pulled out of the context where set! on mutable volatile deftype fields is allowed: