* enhancement: ASDF has been updated to version 2.017.
* enhancement: the --core command line option now accepts binaries with
an embedded core.
+ * enhancement: when built with :sb-core-compression, core files (regular
+ or executable) can be compressed with zlib. Use the :COMPRESSION
+ argument to SAVE-LISP-AND-DIE to specify a compression level.
* optimization: SLEEP no longer conses.
* optimization: *PRINT-PRETTY* no longer slows down printing of strings
or bit-vectors when using the standard pretty-print dispatch table.
ranges to ARRAY-IN-BOUNDS-P. (lp#826970)
* bug fix: ,@ and ,. now signal a read-time error for certain non-list
expressions. (lp#770184)
+ * bug fix: complex single float literals are correctly aligned when used
+ as arguments of arithmetic operators.
changes in sbcl-1.0.51 relative to sbcl-1.0.50:
* minor incompatible change: SB-BSD-SOCKET socket streams no longer
;; SB-BIGNUM:%MULTIPLY.
; :multiply-high-vops
+ ;; SBCL has optional support for zlib-based compressed core files. Enable
+ ;; this feature to compile it in. Obviously, doing so adds a dependency
+ ;; on zlib.
+ ; :sb-core-compression
+
;;
;; miscellaneous notes on other things which could have special significance
;; in the *FEATURES* list
"BUILD-ID-CORE-ENTRY-TYPE-CODE"
"*FASL-FILE-TYPE*"
"CLOSE-FASL-OUTPUT"
+ "DEFLATED-CORE-SPACE-ID-FLAG"
"DUMP-ASSEMBLER-ROUTINES"
"DUMP-OBJECT"
"DYNAMIC-CORE-SPACE-ID"
"*!LOAD-TIME-VALUES*"
"LOAD-TYPE-PREDICATE"
#!+(and sb-thread sb-lutex) "LUTEX-TABLE-CORE-ENTRY-TYPE-CODE"
+ "MAX-CORE-SPACE-ID"
"NEW-DIRECTORY-CORE-ENTRY-TYPE-CODE"
"OPEN-FASL-OUTPUT" "PAGE-TABLE-CORE-ENTRY-TYPE-CODE"
"READ-ONLY-CORE-SPACE-ID"
(file c-string)
(initial-fun (unsigned #.sb!vm:n-word-bits))
(prepend-runtime int)
- (save-runtime-options int))
+ (save-runtime-options int)
+ (compressed int)
+ (compression-level int))
#!+gencgc
(define-alien-routine "gc_and_save" void
(file c-string)
(prepend-runtime int)
- (save-runtime-options int))
+ (save-runtime-options int)
+ (compressed int)
+ (compression-level int))
#!+gencgc
(defvar sb!vm::*restart-lisp-function*)
(save-runtime-options nil)
(purify t)
(root-structures ())
- (environment-name "auxiliary"))
+ (environment-name "auxiliary")
+ (compression nil))
#!+sb-doc
"Save a \"core image\", i.e. enough information to restart a Lisp
process later in the same state, in the file of the specified name.
This is also passed to the PURIFY function when :PURIFY is T.
(rarely used)
+ :COMPRESSION
+ This is only meaningful if the runtime was built with the :SB-CORE-COMPRESSION
+ feature enabled. If NIL (the default), saves to uncompressed core files. If
+ :SB-CORE-COMPRESSION was enabled at build-time, the argument may also be
+ an integer from -1 to 9, corresponding to zlib compression levels, or T
+ (which is equivalent to the default compression level, -1).
+
The save/load process changes the values of some global variables:
*STANDARD-OUTPUT*, *DEBUG-IO*, etc.
sufficiently motivated to do lengthy fixes."
#!+gencgc
(declare (ignore purify root-structures environment-name))
+ #!+sb-core-compression
+ (check-type compression (or boolean (integer -1 9)))
+ #!-sb-core-compression
+ (when compression
+ (error "Unable to save compressed core: this runtime was not built with zlib support"))
+ (when (eql t compression)
+ (setf compression -1))
(tune-hashtable-sizes-of-all-packages)
(deinit)
;; FIXME: Would it be possible to unmix the PURIFY logic from this
;; since the GC will invalidate the stack.
#!+gencgc (gc-and-save name
(foreign-bool executable)
- (foreign-bool save-runtime-options)))
+ (foreign-bool save-runtime-options)
+ (foreign-bool compression)
+ (or compression 0)))
(without-gcing
(save name
(get-lisp-obj-address #'restart-lisp)
(foreign-bool executable)
- (foreign-bool save-runtime-options))))))
+ (foreign-bool save-runtime-options)
+ (foreign-bool compression)
+ (or compression 0))))))
;; Save the restart function into a static symbol, to allow GC-AND-SAVE
;; access to it even after the GC has moved it.
#!+gencgc
(defvar *read-only*)
(defconstant read-only-core-space-id 3)
+(defconstant max-core-space-id 3)
+(defconstant deflated-core-space-id-flag 4)
+
(defconstant descriptor-low-bits 16
"the number of bits in the low half of the descriptor")
(defconstant target-space-alignment (ash 1 descriptor-low-bits)
7 :large t)
(maybe-record-with-translated-name '("-CORE-ENTRY-TYPE-CODE") 8)
(maybe-record-with-translated-name '("-CORE-SPACE-ID") 9)
+ (maybe-record-with-translated-name '("-CORE-SPACE-ID-FLAG") 9)
(maybe-record-with-translated-name '("-GENERATION+") 10))))))
;; KLUDGE: these constants are sort of important, but there's no
;; pleasing way to inform the code above about them. So we fake
OS_SRC = linux-os.c alpha-linux-os.c
OS_LIBS = -ldl
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
+
GC_SRC = cheneygc.c
# Nothing to do for after-grovel-headers.
OS_SRC = osf1-os.c alpha-osf1-os.c
OS_LIBS = #-ldl
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
GC_SRC = cheneygc.c
ifdef LISP_FEATURE_SB_THREAD
OS_LIBS += -lpthread
endif
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
GC_SRC = cheneygc.c
OS_SRC = linux-os.c hppa-linux-os.c
OS_LIBS = -ldl
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
GC_SRC = cheneygc.c
ifdef LISP_FEATURE_SB_THREAD
OS_LIBS += -lpthread
endif
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
GC_SRC = cheneygc.c
OS_SRC = bsd-os.c darwin-os.c ppc-darwin-os.c
OS_LIBS = -lSystem -lc
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
CC = gcc
ifdef LISP_FEATURE_SB_THREAD
OS_LIBS += -lpthread
endif
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
# Nothing to do for after-grovel-headers.
.PHONY: after-grovel-headers
OS_SRC = bsd-os.c undefineds.c ppc-bsd-os.c
OS_LIBS = # -ldl
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
GC_SRC = gencgc.c
OS_SRC = bsd-os.c ppc-bsd-os.c
OS_LIBS = -lutil
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
GC_SRC = gencgc.c
OS_SRC = linux-os.c sparc-linux-os.c
OS_LIBS = -ldl
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
GC_SRC = cheneygc.c
OS_SRC = bsd-os.c sparc-bsd-os.c
OS_LIBS = # -ldl
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
GC_SRC = cheneygc.c
OS_SRC = sunos-os.c sparc-sunos-os.c
OS_LIBS = -ldl -lsocket -lnsl -lrt
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
GC_SRC = cheneygc.c
OS_SRC = bsd-os.c x86-64-bsd-os.c
OS_LIBS = # -ldl
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
CFLAGS += -fno-omit-frame-pointer
ifdef LISP_FEATURE_SB_THREAD
OS_LIBS += -lpthread
endif
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
ASSEM_SRC = x86-64-assem.S ldso-stubs.S
ARCH_SRC = x86-64-arch.c
OS_SRC = sunos-os.c x86-64-sunos-os.c os-common.c
OS_LIBS= -ldl -lsocket -lnsl -lrt
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
GC_SRC= gencgc.c
OS_SRC = bsd-os.c x86-bsd-os.c
OS_LIBS = # -ldl
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
GC_SRC = gencgc.c
ifdef LISP_FEATURE_SB_THREAD
OS_LIBS += -lpthread
endif
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
GC_SRC = gencgc.c
OS_SRC = sunos-os.c x86-sunos-os.c os-common.c
OS_LIBS= -ldl -lsocket -lnsl -lrt
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
+
GC_SRC= gencgc.c
# Nothing to do for after-grovel-headers.
# working on one and it would be a nice thing to have.)
OS_LINK_FLAGS = -Wl,--export-dynamic
OS_LIBS =
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
GC_SRC = gencgc.c
OS_LIBS += -lpthread
endif
+ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ OS_LIBS += -lz
+endif
+
CFLAGS += -fno-omit-frame-pointer
GC_SRC = gencgc.c
#include "pthread-lutex.h"
#endif
+#include <errno.h>
+
+#ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+# include <zlib.h>
+#endif
unsigned char build_id[] =
#include "../../output/build-id.tmp"
}
#endif
+#ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+# define ZLIB_BUFFER_SIZE (1u<<16)
+os_vm_address_t inflate_core_bytes(int fd, os_vm_offset_t offset,
+ os_vm_address_t addr, int len)
+{
+ z_stream stream;
+ unsigned char buf[ZLIB_BUFFER_SIZE];
+ int ret;
+
+ if (-1 == lseek(fd, offset, SEEK_SET)) {
+ lose("Unable to lseek() on corefile\n");
+ }
+
+ stream.zalloc = NULL;
+ stream.zfree = NULL;
+ stream.opaque = NULL;
+ stream.avail_in = 0;
+ stream.next_in = buf;
+
+ ret = inflateInit(&stream);
+ if (ret != Z_OK)
+ lose("zlib error %i\n", ret);
+
+ stream.next_out = (void*)addr;
+ stream.avail_out = len;
+ do {
+ ssize_t count = read(fd, buf, sizeof(buf));
+ if (count < 0)
+ lose("unable to read core file (errno = %i)\n", errno);
+ stream.next_in = buf;
+ stream.avail_in = count;
+ if (count == 0) break;
+ ret = inflate(&stream, Z_NO_FLUSH);
+ switch (ret) {
+ case Z_STREAM_END:
+ break;
+ case Z_OK:
+ if (stream.avail_out == 0)
+ lose("Runaway gzipped core directory... aborting\n");
+ if (stream.avail_in > 0)
+ lose("zlib inflate returned without fully"
+ "using up input buffer... aborting\n");
+ break;
+ default:
+ lose("zlib inflate error: %i\n", ret);
+ break;
+ }
+ } while (ret != Z_STREAM_END);
+
+ if (stream.avail_out > 0) {
+ if (stream.avail_out >= os_vm_page_size)
+ fprintf(stderr, "Warning: gzipped core directory significantly"
+ "shorter than expected (%lu bytes)", (unsigned long)stream.avail_out);
+ /* Is this needed? */
+ memset(stream.next_out, 0, stream.avail_out);
+ }
+
+ inflateEnd(&stream);
+ return addr;
+}
+# undef ZLIB_BUFFER_SIZE
+#endif
+
static void
process_directory(int fd, lispobj *ptr, int count, os_vm_offset_t file_offset)
{
struct ndir_entry *entry;
+ int compressed;
FSHOW((stderr, "/process_directory(..), count=%d\n", count));
for (entry = (struct ndir_entry *) ptr; --count>= 0; ++entry) {
-
+ compressed = 0;
long id = entry->identifier;
+ if (id <= (MAX_CORE_SPACE_ID | DEFLATED_CORE_SPACE_ID_FLAG)) {
+ if (id & DEFLATED_CORE_SPACE_ID_FLAG)
+ compressed = 1;
+ id &= ~(DEFLATED_CORE_SPACE_ID_FLAG);
+ }
long offset = os_vm_page_size * (1 + entry->data_page);
os_vm_address_t addr =
(os_vm_address_t) (os_vm_page_size * entry->address);
os_vm_address_t real_addr;
FSHOW((stderr, "/mapping %ld(0x%lx) bytes at 0x%lx\n",
(long)len, (long)len, (unsigned long)addr));
+ if (compressed) {
+#ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ real_addr = inflate_core_bytes(fd, offset + file_offset, addr, len);
+#else
+ lose("This runtime was not built with zlib-compressed core support... aborting\n");
+#endif
+ } else {
#ifdef LISP_FEATURE_HPUX
- real_addr = copy_core_bytes(fd, offset + file_offset, addr, len);
+ real_addr = copy_core_bytes(fd, offset + file_offset, addr, len);
#else
- real_addr = os_map(fd, offset + file_offset, addr, len);
+ real_addr = os_map(fd, offset + file_offset, addr, len);
#endif
+ }
if (real_addr != addr) {
lose("file mapped in wrong place! "
"(0x%08x != 0x%08lx)\n",
* SB!VM:RESTART-LISP-FUNCTION */
void
gc_and_save(char *filename, boolean prepend_runtime,
- boolean save_runtime_options)
+ boolean save_runtime_options,
+ boolean compressed, int compression_level)
{
FILE *file;
void *runtime_bytes = NULL;
/* The dumper doesn't know that pages need to be zeroed before use. */
zero_all_free_pages();
save_to_filehandle(file, filename, SymbolValue(RESTART_LISP_FUNCTION,0),
- prepend_runtime, save_runtime_options);
+ prepend_runtime, save_runtime_options,
+ compressed ? compression_level : COMPRESSION_LEVEL_NONE);
/* Oops. Save still managed to fail. Since we've mangled the stack
* beyond hope, there's not much we can do.
* (beyond FUNCALLing RESTART_LISP_FUNCTION, but I suspect that's
#include "genesis/lutex.h"
#endif
+#ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+# include <zlib.h>
+#endif
+
+
/* write_runtime_options uses a simple serialization scheme that
* consists of one word of magic, one word indicating whether options
* are actually saved, and one word per struct field. */
}
}
+static void
+write_bytes_to_file(FILE * file, char *addr, long bytes, int compression)
+{
+ if (compression == COMPRESSION_LEVEL_NONE) {
+ while (bytes > 0) {
+ long count = fwrite(addr, 1, bytes, file);
+ if (count > 0) {
+ bytes -= count;
+ addr += count;
+ }
+ else {
+ perror("error writing to save file");
+ bytes = 0;
+ }
+ }
+#ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ } else if ((compression >= -1) && (compression <= 9)) {
+# define ZLIB_BUFFER_SIZE (1u<<16)
+ z_stream stream;
+ unsigned char buf[ZLIB_BUFFER_SIZE];
+ unsigned char * written, * end;
+ long total_written = 0;
+ int ret;
+ stream.zalloc = NULL;
+ stream.zfree = NULL;
+ stream.opaque = NULL;
+ stream.avail_in = bytes;
+ stream.next_in = (void*)addr;
+ ret = deflateInit(&stream, compression);
+ if (ret != Z_OK)
+ lose("deflateInit: %i\n", ret);
+ do {
+ stream.avail_out = sizeof(buf);
+ stream.next_out = buf;
+ ret = deflate(&stream, Z_FINISH);
+ if (ret < 0) lose("zlib deflate error: %i... exiting\n", ret);
+ written = buf;
+ end = buf+sizeof(buf)-stream.avail_out;
+ total_written += end - written;
+ while (written < end) {
+ long count = fwrite(written, 1, end-written, file);
+ if (count > 0) {
+ written += count;
+ } else {
+ lose("unable to write to core file\n");
+ }
+ }
+ } while (stream.avail_out == 0);
+ deflateEnd(&stream);
+ printf("compressed %lu bytes into %lu at level %i\n",
+ bytes, total_written, compression);
+# undef ZLIB_BUFFER_SIZE
+#endif
+ } else {
+#ifdef LISP_FEATURE_SB_CORE_COMPRESSION
+ lose("Unknown core compression level %i, exiting\n", compression);
+#else
+ lose("zlib-compressed core support not built in this runtime\n");
+#endif
+ }
+
+ fflush(file);
+};
+
+
static long
-write_bytes(FILE *file, char *addr, long bytes, os_vm_offset_t file_offset)
+write_and_compress_bytes(FILE *file, char *addr, long bytes, os_vm_offset_t file_offset,
+ int compression)
{
- long count, here, data;
+ long here, data;
bytes = (bytes+os_vm_page_size-1)&~(os_vm_page_size-1);
fseek(file, 0, SEEK_END);
data = (ftell(file)+os_vm_page_size-1)&~(os_vm_page_size-1);
fseek(file, data, SEEK_SET);
-
- while (bytes > 0) {
- count = fwrite(addr, 1, bytes, file);
- if (count > 0) {
- bytes -= count;
- addr += count;
- }
- else {
- perror("error writing to save file");
- bytes = 0;
- }
- }
- fflush(file);
+ write_bytes_to_file(file, addr, bytes, compression);
fseek(file, here, SEEK_SET);
return ((data - file_offset) / os_vm_page_size) - 1;
}
+static long
+write_bytes(FILE *file, char *addr, long bytes, os_vm_offset_t file_offset)
+{
+ return write_and_compress_bytes(file, addr, bytes, file_offset,
+ COMPRESSION_LEVEL_NONE);
+}
+
#if defined(LISP_FEATURE_SB_THREAD) && defined(LISP_FEATURE_SB_LUTEX)
/* saving lutexes in the core */
static void **lutex_addresses;
#endif
static void
-output_space(FILE *file, int id, lispobj *addr, lispobj *end, os_vm_offset_t file_offset)
+output_space(FILE *file, int id, lispobj *addr, lispobj *end,
+ os_vm_offset_t file_offset,
+ int core_compression_level)
{
- size_t words, bytes, data;
+ size_t words, bytes, data, compressed_flag;
static char *names[] = {NULL, "dynamic", "static", "read-only"};
- write_lispobj(id, file);
+ compressed_flag
+ = ((core_compression_level != COMPRESSION_LEVEL_NONE)
+ ? DEFLATED_CORE_SPACE_ID_FLAG : 0);
+
+ write_lispobj(id | compressed_flag, file);
words = end - addr;
write_lispobj(words, file);
#if defined(LISP_FEATURE_SB_THREAD) && defined(LISP_FEATURE_SB_LUTEX)
printf("scanning space for lutexes...\n");
- scan_for_lutexes((char *)addr, words);
+ scan_for_lutexes((void *)addr, words);
#endif
printf("writing %lu bytes from the %s space at 0x%08lx\n",
(unsigned long)bytes, names[id], (unsigned long)addr);
- data = write_bytes(file, (char *)addr, bytes, file_offset);
+ data = write_and_compress_bytes(file, (char *)addr, bytes, file_offset,
+ core_compression_level);
write_lispobj(data, file);
write_lispobj((long)addr / os_vm_page_size, file);
boolean
save_to_filehandle(FILE *file, char *filename, lispobj init_function,
boolean make_executable,
- boolean save_runtime_options)
+ boolean save_runtime_options,
+ int core_compression_level)
{
struct thread *th;
os_vm_offset_t core_start_pos;
READ_ONLY_CORE_SPACE_ID,
(lispobj *)READ_ONLY_SPACE_START,
(lispobj *)SymbolValue(READ_ONLY_SPACE_FREE_POINTER,0),
- core_start_pos);
+ core_start_pos,
+ core_compression_level);
output_space(file,
STATIC_CORE_SPACE_ID,
(lispobj *)STATIC_SPACE_START,
(lispobj *)SymbolValue(STATIC_SPACE_FREE_POINTER,0),
- core_start_pos);
+ core_start_pos,
+ core_compression_level);
#ifdef LISP_FEATURE_GENCGC
/* Flush the current_region, updating the tables. */
gc_alloc_update_all_page_tables();
DYNAMIC_CORE_SPACE_ID,
(lispobj *)DYNAMIC_SPACE_START,
dynamic_space_free_pointer,
- core_start_pos);
+ core_start_pos,
+ core_compression_level);
#else
output_space(file,
DYNAMIC_CORE_SPACE_ID,
(lispobj *)current_dynamic_space,
dynamic_space_free_pointer,
- core_start_pos);
+ core_start_pos,
+ core_compression_level);
#endif
#else
output_space(file,
DYNAMIC_CORE_SPACE_ID,
(lispobj *)DYNAMIC_SPACE_START,
(lispobj *)SymbolValue(ALLOCATION_POINTER,0),
- core_start_pos);
+ core_start_pos,
+ core_compression_level);
#endif
write_lispobj(INITIAL_FUN_CORE_ENTRY_TYPE_CODE, file);
boolean
save(char *filename, lispobj init_function, boolean prepend_runtime,
- boolean save_runtime_options)
+ boolean save_runtime_options, boolean compressed, int compression_level)
{
FILE *file;
void *runtime_bytes = NULL;
save_runtime_to_filehandle(file, runtime_bytes, runtime_size);
return save_to_filehandle(file, filename, init_function, prepend_runtime,
- save_runtime_options);
+ save_runtime_options,
+ compressed ? compressed : COMPRESSION_LEVEL_NONE);
}
#ifndef _SAVE_H_
#define _SAVE_H_
-
+#include <limits.h>
#include "core.h"
+#define COMPRESSION_LEVEL_NONE INT_MIN
+
extern FILE* open_core_for_saving(char *filename);
extern void *load_runtime(char *runtime_path, size_t *size_out);
extern FILE *prepare_to_save(char *filename, boolean prepend_runtime, void **runtime_bytes, size_t *runtime_size);
extern boolean save_runtime_to_filehandle(FILE *output, void *runtime_bytes, size_t runtime_size);
-extern boolean save_to_filehandle(FILE *file, char *filename, lispobj initfun, boolean make_executable, boolean keep_runtime_options);
-extern boolean save(char *filename, lispobj initfun, boolean prepend_runtime, boolean keep_runtime_options);
+extern boolean save_to_filehandle(FILE *file, char *filename, lispobj initfun,
+ boolean make_executable, boolean keep_runtime_options,
+ int core_compression_level);
+extern boolean save(char *filename, lispobj initfun, boolean prepend_runtime,
+ boolean keep_runtime_options,
+ boolean compressed_core, int core_compression_level);
#endif
exit 1
fi
+rm "$tmpcore"
+run_sbcl <<EOF
+ (save-lisp-and-die "$tmpcore" :toplevel (lambda () 42)
+ :compression (and (member :sb-core-compression *features*) t))
+EOF
+run_sbcl_with_core "$tmpcore" --no-userinit --no-sysinit
+check_status_maybe_lose "SAVE-LISP-AND-DIE :COMPRESS" $? 0 "(compressed saved core ran)"
+
+rm "$tmpcore"
+run_sbcl <<EOF
+ (save-lisp-and-die "$tmpcore" :toplevel (lambda () 42) :executable t
+ :compression (and (member :sb-core-compression *features*) t))
+EOF
+chmod u+x "$tmpcore"
+./"$tmpcore" --no-userinit --no-sysinit
+check_status_maybe_lose "SAVE-LISP-AND-DIE :EXECUTABLE-COMPRESS" $? 0 "(executable compressed saved core ran)"
+
exit $EXIT_TEST_WIN