cs: remove experimental JIT database support
Caching compiled JIT fragments in a SQLite database did not turn out to be a viable path, so remove partial support for it. JIT mode in general is rarely a good option, but it's at least completely worked out, so left in for now.
This commit is contained in:
parent
2a7d94d89c
commit
28271158dd
|
@ -127,8 +127,7 @@ LINKLET_SRCS = linklet/version.ss \
|
|||
linklet/performance.ss \
|
||||
linklet/annotation.ss \
|
||||
linklet/compress.ss \
|
||||
linklet/cross-compile.ss \
|
||||
linklet/db.ss
|
||||
linklet/cross-compile.ss
|
||||
|
||||
$(BUILDDIR)linklet.$(CSO): linklet.sls $(LINKLET_SRCS) $(LINKLET_DEPS) $(COMPILE_FILE_DEPS)
|
||||
$(COMPILE_FILE) linklet.sls $(LINKLET_DEPS)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
The implementation of Racket on Chez Scheme in this directory is
|
||||
organized into two layers:
|
||||
The implementation of Racket on Chez Scheme (Racket CS) in this
|
||||
directory is organized into two layers:
|
||||
|
||||
* The immediate directory contains Scheme sources to implement Racket
|
||||
functionality on top of Chez Scheme. It references sibling
|
||||
|
@ -20,13 +20,13 @@ build).
|
|||
Requirements
|
||||
========================================================================
|
||||
|
||||
Building Racket-on-Chez requires both an existing Racket build and
|
||||
Chez Scheme build.
|
||||
Building Racket CS requires both an existing Racket build and Chez
|
||||
Scheme build.
|
||||
|
||||
The existing Racket must be "new enough" to build the current
|
||||
Racket-on-Chez version. In the worst case, it must be exactly the same
|
||||
version (using the traditional Racket implementation) as the one
|
||||
you're trying to build.
|
||||
The existing Racket must be "new enough" to build the current Racket
|
||||
CS version. In the worst case, it must be exactly the same version
|
||||
(using the traditional Racket implementation) as the one you're trying
|
||||
to build.
|
||||
|
||||
Note: When you use `configure --enable-cs` or similar as described
|
||||
in "../README.txt", then a bootstrapping variant of Racket is built
|
||||
|
@ -43,11 +43,11 @@ already), and see "Building Chez Scheme" below for building.
|
|||
Development versus Build
|
||||
========================================================================
|
||||
|
||||
The Racket-on-Chez implementation can be built and run in two
|
||||
different ways: development mode for running directly using a Chez
|
||||
Scheme installation, and build mode for creating a `racket` or
|
||||
`racketcs` executable that combines Chez Scheme and Racket
|
||||
functionality into a single executable.
|
||||
The Racket CS implementation can be built and run in two different
|
||||
ways: development mode for running directly using a Chez Scheme
|
||||
installation, and build mode for creating a `racket` or `racketcs`
|
||||
executable that combines Chez Scheme and Racket functionality into a
|
||||
single executable.
|
||||
|
||||
Development Mode
|
||||
----------------
|
||||
|
@ -80,11 +80,10 @@ makefile makes too many Unix-ish assumptions.
|
|||
Build Mode
|
||||
----------
|
||||
|
||||
To build a Racket-on-Chez executable, the `configure` script and
|
||||
makefile in "c" subdirectory are normally used via `configure` and
|
||||
`make` in the parent directory of this one, as described in
|
||||
"../README.txt". However, you can use them directly with something
|
||||
like
|
||||
To build a Racket CS executable, the `configure` script and makefile
|
||||
in "c" subdirectory are normally used via `configure` and `make` in
|
||||
the parent directory of this one, as described in "../README.txt".
|
||||
However, you can use them directly with something like
|
||||
|
||||
cd [build]
|
||||
mkdir cs
|
||||
|
@ -98,7 +97,7 @@ is a build directory (usually "../build" relative to [here]).
|
|||
|
||||
The `configure` script accepts flags like `--enable-racket=...` and
|
||||
`--enable-scheme=...` to select an existing Racket and a Chez Scheme
|
||||
build directory to use for building Racket-on-Chez:
|
||||
build directory to use for building Racket CS:
|
||||
|
||||
* By default, the build uses Racket as "[build]/racket/racket3m" and
|
||||
bootstraps bytecode from "[here]/../../collects".
|
||||
|
@ -109,12 +108,12 @@ build directory to use for building Racket-on-Chez:
|
|||
* By default, the build looks for a Chez Scheme build directory as
|
||||
"build/ChezScheme".
|
||||
|
||||
Building Racket-on-Chez requires a Chez Scheme build directory, not
|
||||
just a Chez Scheme installation that is accessible as `scheme`.
|
||||
Building Racket CS requires a Chez Scheme build directory, not just
|
||||
a Chez Scheme installation that is accessible as `scheme`.
|
||||
|
||||
The resulting Racket-on-Chez executable has the suffix "cs". To
|
||||
generate an executable without the "cs" suffix, supply
|
||||
`--enable-csdefault` to `configure`. The presence or absence of "cs"
|
||||
The resulting Racket CS executable has the suffix "cs". To generate an
|
||||
executable without the "cs" suffix, supply `--enable-csdefault` to
|
||||
`configure`. The option to select the presence or absence of "cs" also
|
||||
affects the location of ".zo" files.
|
||||
|
||||
Compilation on Windows does not use the `configure` script in "c".
|
||||
|
@ -129,7 +128,7 @@ not already present (in which case `git` must be available).
|
|||
Machine Code versus JIT
|
||||
========================================================================
|
||||
|
||||
Racket-on-Chez currently supports three compilation modes:
|
||||
Racket CS currently supports three compilation modes:
|
||||
|
||||
* Machine-code mode --- The compiled form of a module is machine code
|
||||
generated by compiling either whole linklets (for small enough
|
||||
|
@ -141,7 +140,7 @@ Racket-on-Chez currently supports three compilation modes:
|
|||
|
||||
In development mode or when the "cs" suffix is used for build mode,
|
||||
compiled ".zo" files in this mode are written to a subdirectory of
|
||||
"compiled" using the Chez Scheme platform name (e.g., "a6osx").
|
||||
"compiled" using the Chez Scheme platform name (e.g., "ta6osx").
|
||||
|
||||
Set `PLT_CS_COMPILE_LIMIT` to set the maximum size of forms to
|
||||
compile before falling back to interpreted "bytecode". The default
|
||||
|
@ -177,18 +176,11 @@ Racket-on-Chez currently supports three compilation modes:
|
|||
compiled ".zo" files in this mode are written to a "cs"
|
||||
subdirectory of "compiled".
|
||||
|
||||
S-expression fragments are hashed at compilation time, so that the
|
||||
hash for each fragment is stored in the ".zo" file. At JIT time,
|
||||
the hash is used to consult and/or update a cache (implemented as
|
||||
an SQLite database) of machine-code forms. Set the `PLT_JIT_CACHE`
|
||||
environment variable to change the cache file, or set the
|
||||
environment variable to empty to disable the cache.
|
||||
|
||||
In development mode or when the "cs" suffix is used for build mode,
|
||||
set the `PLT_ZO_PATH` environment variable to override the path used
|
||||
for ".zo" files. For example, you may want to preserve a normal build
|
||||
while also building in machine-code mode with `PLT_CS_DEBUG` set, in
|
||||
which case setting `PLT_ZO_PATH` to something like "a6osx-debug" could
|
||||
which case setting `PLT_ZO_PATH` to something like "ta6osx-debug" could
|
||||
be a good idea.
|
||||
|
||||
|
||||
|
@ -228,7 +220,7 @@ Development mode is driven by the makefile in this directory.
|
|||
Building
|
||||
--------
|
||||
|
||||
Running `make` will build the Racket-on-Chez implementation. Use `make
|
||||
Running `make` will build the Racket CS implementation. Use `make
|
||||
expander-demo` to run a demo that loads `racket/base` from source.
|
||||
|
||||
Use `make setup` (or `make setup-v` for a verbose version) to build
|
||||
|
@ -251,8 +243,8 @@ running plain `racket`, where command-line arguments are supplied in
|
|||
Structure
|
||||
---------
|
||||
|
||||
The Racket-on-Chez implementation is in layers. The immediate layer
|
||||
over Chez Scheme is called "Rumble", and it implements delimited
|
||||
The Racket CS implementation is organized in layers. The immediate
|
||||
layer over Chez Scheme is called "Rumble", and it implements delimited
|
||||
continuations, structures, chaperones and impersonators, engines (for
|
||||
threads), and similar base functionality. The Rumble layer is
|
||||
implemented in Chez Scheme.
|
||||
|
@ -357,34 +349,7 @@ examples, including loading `racket/base` from source.
|
|||
Dumping Linklets and Schemified Linklets
|
||||
----------------------------------------
|
||||
|
||||
Set the `PLT_LINKLET_SHOW` environment variable to pretty print each
|
||||
linklet generated by the expander and its schemified form that is
|
||||
passed on to Chez Scheme.
|
||||
|
||||
By default, `PLT_LINKLET_SHOW` does not distinguish gensyms that have
|
||||
the same base name, so the schemified form is not really accurate. Set
|
||||
`PLT_LINKLET_SHOW_GENSYM` instead (or in addition) to get more
|
||||
accurate output.
|
||||
|
||||
In JIT mode, the schemified form is shown after a conversion to
|
||||
support JIT mode. Set `PLT_LINKLET_SHOW_PRE_JIT` to see the
|
||||
pre-conversion form. Set `PLT_LINKLET_SHOW_JIT_DEMAND` to see forms as
|
||||
they are compiled on demand.
|
||||
|
||||
In machine-code mode, set `PLT_LINKLET_SHOW_LAMBDA` to see individual
|
||||
compiled terms when a linklet is not compiled whole; set
|
||||
`PLT_LINKLET_SHOW_POST_LAMBDA` to see the linklet reorganized around
|
||||
those compiled parts; and/or set `PLT_LINKLET_SHOW_POST_INTERP` to see
|
||||
the "bytecode" form.
|
||||
|
||||
Set `PLT_LINKLET_SHOW_CP0` to see the Schemified form of a linklet
|
||||
after expansion and optimization by Chez Scheme's cp0.
|
||||
|
||||
Set `PLT_LINKLET_SHOW_ASSEMBLY` to see the assembly form of a linklet
|
||||
after compilation by Chez Scheme. Assembly format uses Chez Scheme's
|
||||
abstraction of architecture-specific machine instructions (where the
|
||||
assembly is translated to actual machine code in a fairly
|
||||
straightforward way).
|
||||
See "Inspecting Compiler Passes" in the Racket reference manual.
|
||||
|
||||
Safety and Debugging Mode
|
||||
-------------------------
|
||||
|
@ -395,13 +360,13 @@ If you make changes to files in "rumble", you should turn off
|
|||
You may want to turn on `DEBUG_COMP` in the makefile, so that
|
||||
backtraces provide expression-specific source locations instead of
|
||||
just procedure-specific source locations. Enabling `DEBUG_COMP` makes
|
||||
the Racket-on-Chez implementation take up twice as much memory and
|
||||
take twice as long to load.
|
||||
the Racket CS implementation take up twice as much memory and take
|
||||
twice as long to load.
|
||||
|
||||
Turning on `DEBUG_COMP` affects only the Racket-on-Chez
|
||||
implementation. To preserve per-expression locations on compiled
|
||||
Racket code, set `PLT_CS_DEBUG`. See also "JIT versus Machine Code"
|
||||
for a suggestion on setting `PLT_ZO_PATH`.
|
||||
Turning on `DEBUG_COMP` affects only the Racket CS implementation. To
|
||||
preserve per-expression locations on compiled Racket code, set
|
||||
`PLT_CS_DEBUG`. See also "JIT versus Machine Code" for a suggestion on
|
||||
setting `PLT_ZO_PATH`.
|
||||
|
||||
When you change "rumble" or other layers, you can continue to use
|
||||
Racket modules that were previously compiled to ".zo" form... usually,
|
||||
|
@ -411,8 +376,8 @@ compatibility.
|
|||
FFI Differences
|
||||
---------------
|
||||
|
||||
Compared to the traditional Racket implementation, Racket-on-Chez's
|
||||
FFI behaves in several different ways:
|
||||
Compared to the traditional Racket implementation, Racket CS's FFI
|
||||
behaves in several different ways:
|
||||
|
||||
* The `make-sized-byte-string` function always raises an exception,
|
||||
because a foreign address cannot be turned into a byte string whose
|
||||
|
@ -425,10 +390,6 @@ FFI behaves in several different ways:
|
|||
is used as a result type, the C result is copied into a fresh byte
|
||||
string.
|
||||
|
||||
* The 'atomic-interior allocation mode returns memory that is allowed
|
||||
to move after the cpointer returned by allocation becomes
|
||||
unreachable.
|
||||
|
||||
* A `_gcpointer` can only refer to the start of an allocated object,
|
||||
and never the interior of an 'atomic-interior allocation. Like
|
||||
traditional Racket, `_gcpointer` is equivalent to `_pointer` for
|
||||
|
@ -440,7 +401,9 @@ FFI behaves in several different ways:
|
|||
|
||||
* Calling a foreign function implicitly uses atomic mode and also
|
||||
disables GC. If the foreign function calls back to Racket, the
|
||||
callback runs in atomic mode with the GC still disabled.
|
||||
callback runs in atomic mode with the GC still disabled. Use the
|
||||
`blocking?` option for a foreign call or callback to adjuse that
|
||||
behavior.
|
||||
|
||||
* An immobile cell must be modified only through its original pointer
|
||||
or a reconstructed `_gcpointer`. If it is cast or reconstructed as
|
||||
|
@ -534,7 +497,7 @@ configuration:
|
|||
other layers by 30-50%.
|
||||
|
||||
* `PLT_CS_DEBUG` not set --- an environment variable similar to
|
||||
`DEBUG_COMP`, but applies to code compiled by Racket-on-Chez.
|
||||
`DEBUG_COMP`, but applies to code compiled by Racket CS.
|
||||
|
||||
Effectiveness: Avoids improvement to stack traces, but also avoids
|
||||
increases load time and memory use of Racket programs by as much as
|
||||
|
|
|
@ -137,17 +137,6 @@
|
|||
n))))
|
||||
10000)))
|
||||
|
||||
(define no-future-jit-db? (getenv "PLT_NO_FUTURE_JIT_CACHE")) ; => don't calculate key for cache
|
||||
(define jit-db-path (let ([bstr (environment-variables-ref
|
||||
(|#%app| current-environment-variables)
|
||||
(string->utf8 "PLT_JIT_CACHE"))])
|
||||
(cond
|
||||
[(equal? bstr '#vu8()) #f] ; empty value disables the JIT cache
|
||||
[(not bstr)
|
||||
(build-path (find-system-path 'addon-dir)
|
||||
"cs-jit.sqlite")]
|
||||
[else (bytes->path bstr)])))
|
||||
|
||||
;; For "main.sps" to select the default ".zo" directory name:
|
||||
(define platform-independent-zo-mode? (not (eq? linklet-compilation-mode 'mach)))
|
||||
|
||||
|
@ -225,7 +214,6 @@
|
|||
(include "linklet/read.ss")
|
||||
(include "linklet/annotation.ss")
|
||||
(include "linklet/performance.ss")
|
||||
(include "linklet/db.ss")
|
||||
|
||||
;; `compile`, `interpret`, etc. have `dynamic-wind`-based state
|
||||
;; that need to be managed correctly when swapping Racket
|
||||
|
@ -353,28 +341,6 @@
|
|||
(fx= (char->integer #\e) (bytevector-u8-ref bv 6))
|
||||
(fx= (char->integer #\z) (bytevector-u8-ref bv 7))))
|
||||
|
||||
(define-values (lookup-code insert-code delete-code)
|
||||
(let ([get-procs!-maker
|
||||
(lambda (retry)
|
||||
(lambda args
|
||||
(let-values ([(lookup insert delete) (get-code-database-procedures)])
|
||||
(set! lookup-code lookup)
|
||||
(set! insert-code insert)
|
||||
(set! delete-code delete)
|
||||
(apply retry args))))])
|
||||
(values (get-procs!-maker (lambda (hash) (lookup-code hash)))
|
||||
(get-procs!-maker (lambda (hash code) (insert-code hash code)))
|
||||
(get-procs!-maker (lambda (hash) (delete-code hash))))))
|
||||
|
||||
(define (add-code-hash a)
|
||||
(cond
|
||||
[no-future-jit-db? a]
|
||||
[else
|
||||
;; Combine an annotation with a hash code in a vector
|
||||
(let-values ([(o get) (open-bytevector-output-port)])
|
||||
(fasl-write* (cons (version) a) o)
|
||||
(vector (sha1-bytes (get)) a))]))
|
||||
|
||||
(define-record-type wrapped-code
|
||||
(fields (mutable content) ; bytevector for 'lambda mode; annotation or (vector hash annotation) for 'jit mode
|
||||
arity-mask
|
||||
|
@ -387,38 +353,17 @@
|
|||
f
|
||||
(performance-region
|
||||
'on-demand
|
||||
(let ([f (if (and (vector? f)
|
||||
(or (not jit-db-path)
|
||||
(wrong-jit-db-thread?)))
|
||||
(vector-ref f 1)
|
||||
f)])
|
||||
(cond
|
||||
[(bytevector? f)
|
||||
(let* ([f (code-from-bytevector f)])
|
||||
(wrapped-code-content-set! wc f)
|
||||
f)]
|
||||
[(vector? f)
|
||||
(when jit-demand-on?
|
||||
(show "JIT demand" (strip-nested-annotations (vector-ref f 1))))
|
||||
(let* ([hash (vector-ref f 0)]
|
||||
[code (lookup-code hash)])
|
||||
(cond
|
||||
[code
|
||||
(let* ([f (eval-from-bytevector code '() 'compile)])
|
||||
(wrapped-code-content-set! wc f)
|
||||
f)]
|
||||
[else
|
||||
(let ([code (compile-to-bytevector (vector-ref f 1) '() 'compile)])
|
||||
(insert-code hash code)
|
||||
(let* ([f (eval-from-bytevector code '() 'compile)])
|
||||
(wrapped-code-content-set! wc f)
|
||||
f))]))]
|
||||
[else
|
||||
(let ([f (compile* f)])
|
||||
(when jit-demand-on?
|
||||
(show "JIT demand" (strip-nested-annotations (wrapped-code-content wc))))
|
||||
(wrapped-code-content-set! wc f)
|
||||
f)]))))))
|
||||
(cond
|
||||
[(bytevector? f)
|
||||
(let* ([f (code-from-bytevector f)])
|
||||
(wrapped-code-content-set! wc f)
|
||||
f)]
|
||||
[else
|
||||
(let ([f (compile* f)])
|
||||
(when jit-demand-on?
|
||||
(show "JIT demand" (strip-nested-annotations (wrapped-code-content wc))))
|
||||
(wrapped-code-content-set! wc f)
|
||||
f)])))))
|
||||
|
||||
(define (jitified-extract-closed wc)
|
||||
(let ([f (wrapped-code-content wc)])
|
||||
|
@ -612,9 +557,7 @@
|
|||
;; Preserve annotated `lambda` source for on-demand compilation:
|
||||
(lambda (expr arity-mask name)
|
||||
(let ([a (correlated->annotation (xify expr) serializable? sfd-cache)])
|
||||
(make-wrapped-code (if serializable?
|
||||
(add-code-hash a)
|
||||
a)
|
||||
(make-wrapped-code a
|
||||
arity-mask
|
||||
(extract-inferred-name expr name))))]
|
||||
[else
|
||||
|
|
|
@ -1,266 +0,0 @@
|
|||
|
||||
;; For now, don't try to use the JIT database from multiple threads
|
||||
(meta-cond
|
||||
[(threaded?)
|
||||
(begin
|
||||
(define original-thread-id (get-thread-id))
|
||||
(define (wrong-jit-db-thread?)
|
||||
(not (eqv? original-thread-id (get-thread-id)))))]
|
||||
[else
|
||||
(define (wrong-jit-db-thread?) #f)])
|
||||
|
||||
(define (db-error who fmt . args)
|
||||
(let ([str (string-append (symbol->string who)
|
||||
": "
|
||||
(apply #%format fmt args))])
|
||||
(log-message root-logger 'error 'jit-db str #f)
|
||||
#f))
|
||||
|
||||
(define (no-db-procedures)
|
||||
(values (lambda (hash) #f)
|
||||
(lambda (hash code) (void))
|
||||
(lambda (hash) (void))))
|
||||
|
||||
;; Gets Sqlite3-based lookup, insert, and delete on demand,
|
||||
;; returning the dummy functions from `no-db-procedures`
|
||||
;; if something goes wrong setting up the database
|
||||
(define (get-code-database-procedures)
|
||||
(with-interrupts-disabled
|
||||
(guard
|
||||
(exn [else (db-error 'load "could not load sqlite ~s"
|
||||
(if (message-condition? exn)
|
||||
(condition-message exn)
|
||||
exn))
|
||||
(no-db-procedures)])
|
||||
(let ([ok (begin
|
||||
;; FIXME: look in the Racket "lib" directory, first
|
||||
(case (system-type)
|
||||
[(macosx) (load-shared-object "libsqlite3.0.dylib")]
|
||||
[(windows) (load-shared-object "sqlite3.dll")]
|
||||
[else (load-shared-object "libsqlite3.so.0")])
|
||||
(void))])
|
||||
(define SQLITE_OPEN_READONLY #x00000001)
|
||||
(define SQLITE_OPEN_READWRITE #x00000002)
|
||||
(define SQLITE_OPEN_CREATE #x00000004)
|
||||
|
||||
(define SQLITE_OK 0)
|
||||
(define SQLITE_CONSTRAINT 19)
|
||||
(define SQLITE_ROW 100)
|
||||
(define SQLITE_DONE 101)
|
||||
|
||||
(define SQLITE_TRANSIENT -1)
|
||||
|
||||
(define memcpy_pp (foreign-procedure "(cs)byte-copy" (uptr iptr uptr iptr iptr) void))
|
||||
(define memcpy_bp (foreign-procedure "(cs)byte-copy" (u8* iptr uptr iptr iptr) void))
|
||||
(define memcpy_pb (foreign-procedure "(cs)byte-copy" (uptr iptr u8* iptr iptr) void))
|
||||
(define memcpy_bb (foreign-procedure "(cs)byte-copy" (u8* iptr u8* iptr iptr) void))
|
||||
|
||||
(define (memcpy dest src len)
|
||||
(cond
|
||||
[(bytevector? dest)
|
||||
(if (bytevector? src)
|
||||
(memcpy_bb src 0 dest 0 len)
|
||||
(memcpy_pb src 0 dest 0 len))]
|
||||
[else
|
||||
(if (bytevector? src)
|
||||
(memcpy_bp src 0 dest 0 len)
|
||||
(memcpy_pp src 0 dest 0 len))]))
|
||||
|
||||
(define sqlite3_open_v2
|
||||
(foreign-procedure "sqlite3_open_v2"
|
||||
(u8* ; path
|
||||
uptr ; receives a pointer result
|
||||
int ; flags
|
||||
uptr) ; VFS
|
||||
int))
|
||||
|
||||
(define sqlite3_prepare_v2
|
||||
(foreign-procedure "sqlite3_prepare_v2"
|
||||
(uptr ; db
|
||||
uptr ; statement string
|
||||
int ; statement length
|
||||
uptr ; ptr to result
|
||||
uptr) ; ptr to leftover statement string
|
||||
int))
|
||||
|
||||
(define sqlite3_step
|
||||
(foreign-procedure "sqlite3_step"
|
||||
(uptr) ; statement
|
||||
int))
|
||||
|
||||
(define sqlite3_reset
|
||||
(foreign-procedure "sqlite3_reset"
|
||||
(uptr) ; statement
|
||||
int))
|
||||
|
||||
(define sqlite3_clear_bindings
|
||||
(foreign-procedure "sqlite3_clear_bindings"
|
||||
(uptr) ; statement
|
||||
int))
|
||||
|
||||
(define sqlite3_finalize
|
||||
(foreign-procedure "sqlite3_finalize"
|
||||
(uptr) ; statement
|
||||
int))
|
||||
|
||||
(define sqlite3_bind_blob
|
||||
(foreign-procedure "sqlite3_bind_blob"
|
||||
(uptr ; statement
|
||||
int ; parameter index
|
||||
u8* ; data
|
||||
int ; length
|
||||
iptr) ; use SQLITE_TRANSIENT
|
||||
int))
|
||||
|
||||
(define sqlite3_column_blob
|
||||
(foreign-procedure "sqlite3_column_blob"
|
||||
(uptr ; statement
|
||||
int) ; column
|
||||
uptr))
|
||||
(define sqlite3_column_bytes
|
||||
(foreign-procedure "sqlite3_column_bytes"
|
||||
(uptr ; statement
|
||||
int) ; column
|
||||
int))
|
||||
|
||||
(define sqlite3_errstr
|
||||
(foreign-procedure "sqlite3_errstr"
|
||||
(int)
|
||||
string))
|
||||
|
||||
(define sqlite3_errmsg
|
||||
(foreign-procedure "sqlite3_errmsg"
|
||||
(uptr) ; database
|
||||
string))
|
||||
|
||||
(define (errstr r)
|
||||
(sqlite3_errstr r))
|
||||
|
||||
(define db
|
||||
(let ([db-ptr (foreign-alloc (foreign-sizeof 'uptr))])
|
||||
(define r
|
||||
(sqlite3_open_v2 (bytes-append (path->bytes jit-db-path)
|
||||
'#vu8(0))
|
||||
db-ptr
|
||||
(bitwise-ior SQLITE_OPEN_READWRITE
|
||||
SQLITE_OPEN_CREATE)
|
||||
0))
|
||||
(let ([db (foreign-ref 'uptr 0 db-ptr)])
|
||||
(foreign-free db-ptr)
|
||||
(cond
|
||||
[(= r SQLITE_OK) db]
|
||||
[else (db-error 'open "failed ~s" (errstr r))]))))
|
||||
|
||||
(define (prepare db stmt-str)
|
||||
(let* ([stmt (string->utf8 stmt-str)]
|
||||
[stmt-len (bytevector-length stmt)]
|
||||
[stmt-copy (foreign-alloc stmt-len)]
|
||||
[s-ptr (foreign-alloc (foreign-sizeof 'uptr))]
|
||||
[rest-ptr (foreign-alloc (foreign-sizeof 'uptr))])
|
||||
(memcpy stmt-copy stmt stmt-len)
|
||||
(let ([r (sqlite3_prepare_v2 db
|
||||
stmt-copy
|
||||
stmt-len
|
||||
s-ptr
|
||||
rest-ptr)])
|
||||
(let* ([s (foreign-ref 'uptr 0 s-ptr)]
|
||||
[rest (foreign-ref 'uptr 0 rest-ptr)])
|
||||
(foreign-free stmt-copy)
|
||||
(cond
|
||||
[(= r SQLITE_OK)
|
||||
(cond
|
||||
[(= rest (+ stmt-copy stmt-len))
|
||||
;; Success
|
||||
s]
|
||||
[else
|
||||
(finalize s)
|
||||
(db-error 'prepare "more than one statement ~s" stmt-str)])]
|
||||
[else
|
||||
(db-error 'prepare "error ~s" (errstr r))])))))
|
||||
|
||||
(define (finalize s)
|
||||
(define r (sqlite3_finalize s))
|
||||
(unless (= r SQLITE_OK)
|
||||
(db-error 'finalize "error ~s" (errstr r))))
|
||||
|
||||
(define (step s result-shape)
|
||||
(define r (sqlite3_step s))
|
||||
(cond
|
||||
[(= r SQLITE_ROW)
|
||||
(let loop ([result-shape result-shape] [col 0])
|
||||
(case result-shape
|
||||
[(bytes)
|
||||
(let* ([blob (sqlite3_column_blob s col)]
|
||||
[len (sqlite3_column_bytes s col)]
|
||||
[bstr (make-bytevector len)])
|
||||
(memcpy bstr blob len)
|
||||
bstr)]
|
||||
[(void ignore-constraint) (void)]
|
||||
[else
|
||||
(cond
|
||||
[else (db-error 'step "unrecognized result format ~s" result-shape)])]))]
|
||||
[(= r SQLITE_DONE)
|
||||
#f]
|
||||
[(and (= r SQLITE_CONSTRAINT)
|
||||
(eq? result-shape 'ignore-constraint))
|
||||
;; Ignore a constraint failure, because we assume it reflects a
|
||||
;; lost race trying to insert code for the same hash
|
||||
(void)]
|
||||
[else
|
||||
(db-error 'step "error ~s" (errstr r))]))
|
||||
|
||||
(define initialized-db
|
||||
(when db
|
||||
(let ([s (prepare db "SELECT name FROM sqlite_master WHERE type='table' AND name='compiled'")])
|
||||
(unless (step s 'void)
|
||||
(let ([s2 (prepare db "CREATE TABLE compiled (hash blob(24), code blob(1024), PRIMARY KEY (hash))")])
|
||||
(step s2 'void)
|
||||
(finalize s2)))
|
||||
(finalize s))
|
||||
;; FIXME: this pragma is needed for reasonable performance on Linux, but
|
||||
;; we should instead batch updates in `insert` (since it's ok for an
|
||||
;; update to get lost, but not ok for the database to be corrupted)
|
||||
(let ([s (prepare db "PRAGMA synchronous = OFF")])
|
||||
(step s 'void)
|
||||
(finalize s))))
|
||||
|
||||
(define (check who r)
|
||||
(unless (= r SQLITE_OK)
|
||||
(db-error who "error ~s" (errstr r))))
|
||||
|
||||
(define (bind s pos v)
|
||||
(check 'bind (sqlite3_bind_blob s pos v (bytevector-length v) SQLITE_TRANSIENT)))
|
||||
|
||||
(define lookup-s (prepare db "SELECT code FROM compiled WHERE hash=$1"))
|
||||
(define delete-s (prepare db "DELETE FROM compiled WHERE hash=$1"))
|
||||
(define insert-s (prepare db "INSERT INTO compiled VALUES ($1, $2)"))
|
||||
|
||||
(define (reset s)
|
||||
(sqlite3_reset s) ; ignore any error, since it's a repeat of recent error
|
||||
(check 'clear-bindings (sqlite3_clear_bindings s)))
|
||||
|
||||
(define (lookup hash)
|
||||
(with-interrupts-disabled
|
||||
(bind lookup-s 1 hash)
|
||||
(let ([r (step lookup-s 'bytes)])
|
||||
(reset lookup-s)
|
||||
r)))
|
||||
|
||||
(define (insert hash code)
|
||||
(with-interrupts-disabled
|
||||
(bind insert-s 1 hash)
|
||||
(bind insert-s 2 code)
|
||||
(step insert-s 'ignore-constraint)
|
||||
(reset insert-s)
|
||||
(void)))
|
||||
|
||||
(define (delete hash)
|
||||
(with-interrupts-disabled
|
||||
(bind delete-s 1 hash)
|
||||
(step delete-s 'void)
|
||||
(reset delete-s)
|
||||
(void)))
|
||||
|
||||
(if db
|
||||
(values lookup insert delete)
|
||||
(no-db-procedures))))))
|
Loading…
Reference in New Issue
Block a user