
and functionality improvements (including support for measuring coverage), primitive argument-checking fixes, and object-file changes resulting in reduced load times (and some backward incompatibility): - annotations are now preserved in object files for debug only, for profiling only, for both, or not at all, depending on the settings of generate-inspector-information and compile-profile. in particular, when inspector information is not enabled but profiling is, source information does not leak into error messages and inspector output, though it is still available via the profile tools. The mechanics of this involved repurposing the fasl a? parameter to hold an annotation flags value when it is not #f and remaking annotations with new flags if necessary before emitting them. compile.ss, fasl.ss, misc.ms - altered a number of mats to produce correct results even when the 's' directory is profiled. misc.ms, cp0.ms, record.ms - profile-release-counters is now generation-friendly; that is, it doesn't look for dropped code objects in generations that have not been collected since the last call to profile-release-counters. also, it no longer allocates memory when it releases counters. pdhtml.ss, gc.c, gcwrapper.c, globals.h, prim5.c - removed unused entry points S_ifile, S_ofile, and S_iofile alloc.c, externs.h - mats that test loading profile info into the compiler's database to guide optimization now weed out preexisting entries, in case the 's' directory is profiled. 4.ms, mat.ss, misc.ms, primvars.ms - counters for dropped code objects are now released at the start of each mat group. mat.ss - replaced ehc (enable-heap-check) option with hci (heap-check-interval) option that allows heap checks to be performed periodically rather than on each collection. hci=0 is equivalent to ehc=f (disabling heap checks) and hci=1 is equivalent to ehc=t (enabling heap checks every collection), while hci=100 enables heap checks only every 100th collection. allx and bullyx mats use this feature to reduce heap-checking overhead to a more reasonable level. this is particularly important when the 's' directory is profiled, since the amount of static memory to be checked is greatly increased due to the counters. mats/Mf-base, mat.ss, primvars.ms - added a mat that calls #%show-allocation, which was otherwise not being tested. misc.ms - removed a broken primvars mat and updated two others. in each case, the mat was looking for information about primitives in the wrong (i.e., old) place and silently succeeding when it didn't find any primitives to tests. the revised mats (along with a few others) now check to make sure at least one identifier has the information they look for. the removed mat was checking for library information that is now compiled in, so the mat is now unnecessary. the others were (not) doing argument-error checks. fixing these turned up a handful of problems that have also been fixed: a couple of unbound variables in the mat driver, two broken primdata declarations, a tardy argument check by profile-load-data, and a bug in char-ready?, which was requiring an argument rather than defaulting it to the current input port. primdata.ss, pdhtml.ss, io.ms, primdvars.ms, 4.ms, 6.ms, misc.ms, patch* - added initial support for recording coverage information. when the new parameter generate-covin-files is set, the compiler generates .covin files containing the universe of all source objects for which profile forms are present in the expander output. when profiling and generation of covin files are enabled in the 's' directory, the mats optionally generate .covout files for each mat file giving the subset of the universe covered by the mat file, along with an all.covout in each mat output directory aggregating the coverage for the directory and another all.covout in the top-level mat directory aggregating the coverage for all directories. back.ss, compile.ss, cprep.ss, primdata.ss, s/Mf-base, mat.ss, mats/Mf-base, mats/primvars.ms - support for generating covout files is now built in. with-coverage-output gathers and dumps coverage information, and aggregate-coverage-output combines (aggregates) covout files. pdhtml.ss, primdata.ss, compile.ss, mat.ss, mats/Mf-base, primvars.ms - profile-clear now adjusts active coverage trackers to avoid losing coverage information. pdhtml.ss, prim5.c - nested with-coverage calls are now supported. pdhtml.ss - switched to a more compact representation for covin and covout files; reduces disk space (compressed or not) by about a factor of four and read time by about a factor of two with no increase in write time. primdata.ss, pdhtml.ss, cprep.ss, compile.ss, mat.ss, mats/Mf-base - added support for determining coverage for an entire run, including coverage for expressions hit during boot time. 'all' mats now produce run.covout files in each output directory, and 'allx' mats produce an aggregate run.covout file in the mat directory. pdhtml.ss, mat.ss, mats/Mf-base - profile-release-counters now adjusts active coverage trackers to account for the counters that have been released. pdhtml.ss, prim5.c - replaced the artificial "examples" target with a real "build-examples" target so make won't think it always has to mats that depend upon the examples directory having been compiled. mats make clean now runs make clean in the examples directory. mats/Mf-base importing a library from an object file now just visits the object file rather than doing a full load so that the run-time code for the library is not retained. The run-time code is still read because the current fasl format forces the entire file to be read, but not retaining the code can lower heap size and garbage-collection cost, particularly when many object-code libraries are imported. The downside is that the file must be revisited if the run-time code turns out to be required. This change exposed several places where the code was failing to check if a revisit is needed. syntax.ss, 7.ms, 8.ms, misc.ms, root-experr* - fixed typos: was passing unquoted load rather than quoted load to $load-library along one path (where it is loading source code and therefore irrelevant), and was reporting src-path rather than obj-path in a message about failing to define a library. syntax.ss - compile-file and friends now put all recompile information in the first fasl object after the header so the library manager can find it without loading the entire fasl file. The library manager now does so. It also now checks to see if library object files need to be recreated before loading them rather than loading them and possibly recompiling them after discovering they are out of date, since the latter requires loading the full object file even if it's out of date, while the former takes advantage of the ability to extract just recompile information. as well as reducing overhead, this eliminates possibly undesirable side effects, such as creation and registration of out-of-date nongenerative record-type descriptors. because the library manager expects to find recompile information at the front of an object file, it will not find all recompile information if object files are "catted" together. also, compile-file has to hold in memory the object code for all expressions in the file so that it can emit the unified recompile information, rather than writing to the object file incrementally, which can significantly increase the memory required to compile a large file full of individual top-level forms. This does not affect top-level programs, which were already handled as a whole, or a typical library file that contains just a single library form. compile.ss, syntax.ss - the library manager now checks include files before library dependencies when compile-imported-libraries is false (as it already did when compile-imported-libraries is true) in case a source change affects the set of imported libraries. (A library change can affect the set of include files as well, but checking dependencies before include files can cause unneeded libraries to be loaded.) The include-file check is based on recompile-info rather than dependencies, but the library checks are still based on dependencies. syntax.ss - fixed check for binding of scheme-version. (the check prevents premature treatment of recompile-info records as Lexpand forms to be passed to $interpret-backend.) scheme.c - strip-fasl-file now preserves recompile-info when compile-time info is stripped. strip.ss - removed include-req* from library/ct-info and ctdesc records; it is no longer needed now that all recompile information is maintained separately. expand-lang.ss, syntax.ss, compile.ss, cprep.ss, syntax.ss - changed the fasl format and reworked a lot of code in the expander, compiler, fasl writer, and fasl reader to allow the fasl reader to skip past run-time information when it isn't needed and compile-time information when it isn't needed. Skipping past still involves reading and decoding when encrypted, but the fasl reader no longer parses or allocates code and data in the portions to be skipped. Side effects of associating record uids with rtds are also avoided, as are the side effects of interning symbols present only in the skipped data. Skipping past code objects also reduces or eliminates the need to synchronize data and instruction caches. Since the fasl reader no longer returns compile-time (visit) or run-time (revisit) code and data when not needed, the fasl reader no longer wraps these objects in a pair with a 0 or 1 visit or revisit marker. To support this change, the fasl writer generates separate top-level fasl entries (and graphs) for separate forms in the same top-level source form (e.g., begin or library). This reliably breaks eq-ness of shared structure across these forms, which was previously broken only when visit or revisit code was loaded at different times (this is an incompatible change). Because of the change, fasl "groups" are no longer needed, so they are no longer handled. 7.ss, cmacros.ss, compile.ss, expand-lang.ss, strip.ss, externs.h, fasl.c, scheme.c, hash.ms - the change above is surfaced in an optional fasl-read "situation" argument (visit, revisit, or load). The default is load. visit causes it to skip past revisit code and data; revisit causes it to skip past visit code and data; and load causes it not to skip past either. visit-revisit data produced by (eval-when (visit revisit) ---) is never skipped. 7.ss, primdata.ss, io.stex - to improve compile-time and run-time error checking, the Lexpand recompile-info, library/rt-info, library-ct-info, and program-info forms have been replaced with list-structured forms, e.g., (recompile-info ,rcinfo). expand-lang.ss, compile.ss, cprep.ss, interpret.ss, syntax.ss - added visit-compiled-from-port and revisit-compiled-from-port to complement the existing load-compiled-from-port. 7.ss, primdata.ss, 7.ms, system.stex - increased amount read when seeking an lz4-encrypted input file from 32 to 1024 bytes at a time compress-io.c - replaced the fasl a? parameter value #t with an "all" flag value so it's value is consistently a mask. cmacros.ss, fasl.ss, compile.ss - split off profile mats into a separate file misc.ms, profile.ms (new), root-experr*, mats/Mf-base - added coverage percent computations to mat allx/bullyx output mat.ss, mats/Mf-base, primvars.ms - replaced coverage tables with more generic and generally useful source tables, which map source objects to arbitrary values. pdhtml.ss, compile.ss, cprep.ss, primdata.ss, mat.ss, mats/Mf-base, primvars.ms, profile.ms, syntax.stex - reduced profile counting overhead by using calls to fold-left instead of calls to apply and map and by using fixnum operations for profile counts on 64-bit machines. pdhtml.ss - used a critical section to fix a race condition in the calculations of profile counts that sometimes resulted in bogus (including negative) counts, especially when the 's' directory is profiled. pdhtml.ss - added discard flag to declaration for hashtable-size primdata.ss - redesigned the printed representation of source tables and rewrote get-source-table! to read and store incrementally to reduce memory overhead. compile.ss - added generate-covin-files to the set of parameters preserved by compile-file, etc. compile.ss, system.stex - moved covop argument before the undocumented machine and hostop arguments to compile-port and compile-to-port. removed the undocumented ofn argument from compile-to-port; using (port-name ip) instead. compile.ss, primdata.ss, 7.ms, system.stex - compile-port now tries to come up with a file position to supply to make-read, which it can do if the port's positions are character positions (presently string ports) or if the port is positioned at zero. compile.ss - audited the argument-type-error fuzz mat exceptions and fixed a host of problems this turned up (entries follow). added #f as an invalid argument for every type for which #f is indeed invalid to catch places where the maybe- prefix was missing on the argument type. the mat tries hard to determine if the condition raised (if any) as the result of an invalid argument is appropriate and redirects the remainder to the mat-output (.mo) file prefixed with 'Expected error', causing them to show up in the expected error output so developers will be encouraged to audit them in the future. primvars.ms, mat.ss - added an initial symbol? test on machine type names so we produce an invalid machine type error message rather than something confusing like "machine type #f is not supported". compile.ss - fixed declarations for many primitives that were specified as accepting arguments of more general types than they actually accept, such as number -> real for various numeric operations, symbol -> endianness for various bytevector operations, time -> time-utc for time-utc->date, and list -> list-of-string-pairs for default-library-search-handler. also replaced some of the sub-xxxx types with specific types such as sub-symbol -> endianness in utf16->string, but only where they were causing issues with the primvars argument-type-error fuzz mat. (this should be done more generally.) primdata.ss - fixed incorrect who arguments (was map instead of fold-right, current-date instead of time-utc->date); switched to using define-who/set-who! generally. 4.ss, date.ss - append! now checks all arguments before any mutation 5_2.ss - with-source-path now properly supplies itself as who for the string? argument check; callers like load now do their own checks. 7.ss - added missing integer? check to $fold-bytevector-native-ref whose lack could have resulted in a compile-time error. cp0.ss - fixed typo in output-port-buffer-mode error message io.ss - fixed who argument (was fx< rather than fx<?) library.ss - fixed declaration of first source-file-descriptor argument (was sfd, now string) primdata.ss - added missing article 'a' in a few error messages prims.ss - fixed the copy-environment argument-type error message for the list of symbols argument. syntax.ss - the environment procedure now catches exceptions that occur and reraises the exception with itself as who if the condition isn't already a who condition. syntax.ss - updated experr and allx patch files for changes to argument-count fuzz mat and fixes for problems turned up by them. root-experr*, patch* - fixed a couple of issues setting port sizes: string and bytevector output port put handlers don't need room to store the character or byte, so they now set the size to the buffer length rather than one less. binary-file-port-clear-output now sets the index rather than size to zero; setting the size to zero is inappropriate for some types of ports and could result in loss of buffering and even suppression of future output. removed a couple of redundant sets of the size that occur immediately after setting the buffer. io.ss - it is now possible to return from a call to with-profile-tracker multiple times and not double-count (or worse) any counts. pdhtml.ss, profile.ms - read-token now requires a file position when it is handed a source-file descriptor (since the source-file descriptor isn't otherwise useful), and the source-file descriptor argument can no longer be #f. the input file position plays the same role as the input file position in get-datum/annotations. these extra read-token arguments are now documented. read.ss, 6.ms, io.stex - the source-file descriptor argument to get-datum/annotations can no longer be #f. it was already documented that way. read.ss - read-token and do-read now look for the character-positions port flag before asking if the port has port-position, since the latter is slightly more expensive. read.ss - rd-error now reports the current port position if it can be determined when fp isn't already set, i.e., when reading from a port without character positions (presently any non string port) and fp has not been passed in explicitly (to read-token or get-datum/annotations). the port position might not be a character position, but it should be better than nothing. read.ss - added comment noting an invariant for s_profile_release_counters. prim5.c - restored accidentally dropped fasl-write formdef and dropped duplicate fasl-read formdef io.stex - added a 'coverage' target that tests the coverage of the Scheme-code portions of Chez Scheme by the mats. Makefile.in, Makefile-workarea.in - added .PHONY declarations for all of the targets in the top-level and workarea make files, and renamed the create-bintar, create-rpm, and create-pkg targets bintar, rpm, and pkg. Makefile.in, Makefile-workarea.in - added missing --retain-static-relocation command-line argument and updated the date scheme.1.in - removed a few redundant conditional variable settings configure - fixed declaration of condition wait (timeout -> maybe-timeout) primdata.ss original commit: 88501743001393fa82e89c90da9185fc0086fbcb
1653 lines
53 KiB
C
1653 lines
53 KiB
C
/* fasl.c
|
|
* Copyright 1984-2017 Cisco Systems, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
/* fasl representation:
|
|
*
|
|
* <fasl-file> -> <fasl-group>*
|
|
*
|
|
* <fasl-group> -> <fasl header><fasl-object>*
|
|
*
|
|
* <fasl-header> -> {header}\0\0\0chez<uptr version><uptr machine-type>(<bootfile-name> ...)
|
|
*
|
|
* <bootfile-name> -> <octet char>*
|
|
*
|
|
* <fasl-object> -> <situation>{fasl-size}<uptr size><fasl> # size is the size in bytes of the following <fasl>
|
|
*
|
|
* <situation> -> {visit}{revisit}{visit-revisit}
|
|
*
|
|
* <fasl> -> {pair}<uptr n><fasl elt1>...<fasl eltn><fasl last-cdr>
|
|
*
|
|
* -> {weak-pair}<fasl><fasl>
|
|
*
|
|
* -> {box}<fasl>
|
|
*
|
|
* -> {symbol}<faslstring>
|
|
*
|
|
* -> {gensym}<faslstring name><faslstring uname>
|
|
*
|
|
* -> {string}<faslstring>
|
|
*
|
|
* -> {vector}<uptr n><fasl elt1>...<fasl eltn>
|
|
*
|
|
* -> {fxvector}<uptr n><iptr elt1>...<iptr eltn>
|
|
*
|
|
* -> {bytevector}<uptr n><octet elt1>...<octet eltn>
|
|
*
|
|
* -> {immediate}<uptr>
|
|
*
|
|
* -> {small-integer}<iptr>
|
|
*
|
|
* -> {large-integer}<byte sign><uptr n><uptr bigit1>...<uptr bigitn>
|
|
*
|
|
* -> {ratum}<fasl numerator><fasl denominator>
|
|
*
|
|
* -> {inexactnum}<fasl real-part><fasl imag-part>
|
|
*
|
|
* -> {exactnum}<fasl real-part><fasl imag-part>
|
|
*
|
|
* -> {flonum}<uptr high><uptr low>
|
|
*
|
|
* -> {entry}<uptr index>
|
|
*
|
|
* -> {library}<uptr index>
|
|
*
|
|
* -> {library-code}<uptr index>
|
|
*
|
|
* -> {graph}<uptr graph-length><fasl object>
|
|
*
|
|
* -> {graph-def}<uptr index><fasl object>
|
|
*
|
|
* -> {graph-ref}<uptr index>
|
|
*
|
|
* -> {base-rtd}
|
|
*
|
|
* -> {rtd}<fasl uid><faslrecord>
|
|
*
|
|
* -> {record}<faslrecord>
|
|
*
|
|
* -> {eq-hashtable}<byte mutable?>
|
|
* <byte weak?>
|
|
* <uptr minlen>
|
|
* <uptr veclen>
|
|
* <uptr n>
|
|
* <keyval1>...<keyvaln>
|
|
* <keyval> -> <fasl key><fasl val>
|
|
*
|
|
* -> {symbol-hashtable}<byte mutable?>
|
|
* <uptr minlen>
|
|
* <byte equiv> ; 0: eq?, 1: eqv?, 2: equal?, 3: symbol=?
|
|
* <uptr veclen>
|
|
* <uptr n>
|
|
* <keyval1>...<keyvaln>
|
|
* <keyval> -> <fasl key><fasl val>
|
|
*
|
|
* -> {closure}<uptr offset><fasl code>
|
|
*
|
|
* -> {code}<byte flags>
|
|
* <uptr free> # number of free variables
|
|
* <uptr n> # length in bytes of code
|
|
* <fasl name>
|
|
* <fasl arity-mask> # two's complement encoding of accepted argument counts
|
|
* <fasl info> # inspector info
|
|
* <fasl pinfo*> # profiling info
|
|
* <byte code1>...<byte coden>
|
|
* <uptr m> # length in uptrs of relocation table
|
|
* <faslreloc> # first relocation entry
|
|
* ...
|
|
* <faslreloc> # last relocation entry
|
|
*
|
|
* <faslreloc> -> <byte type-etc> # bit 0: extended entry, bit 1: expect item offset, bit 2+: type
|
|
* <uptr code-offset>
|
|
* <uptr item-offset> # omitted if bit 1 of type-etc is 0
|
|
* <fasl object>
|
|
*
|
|
* <faslstring> -> <uptr n><uptr char1>...<uptr charn>
|
|
*
|
|
* <faslrecord> -> <uptr size> # size in bytes, not necessarily ptr-aligned
|
|
* <uptr n> # number of flds
|
|
* <fasl rtd>
|
|
* <field elt1>
|
|
* ...
|
|
* <field eltn>
|
|
* <field> -> <padty fld-type-ptr><fasl object>
|
|
* <padty fld-type-u8><octet>
|
|
* <padty fld-type-i16><iptr>
|
|
* <padty fld-type-i24><iptr>
|
|
* <padty fld-type-i32><iptr>
|
|
* <padty fld-type-i40><iptr high><uptr low> # 32-bit target
|
|
* <padty fld-type-i40><iptr> # 64-bit target
|
|
* <padty fld-type-i48><iptr high><uptr low> # 32-bit target
|
|
* <padty fld-type-i48><iptr> # 64-bit target
|
|
* <padty fld-type-i56><iptr high><uptr low> # 32-bit target
|
|
* <padty fld-type-i56><iptr> # 64-bit target
|
|
* <padty fld-type-i64><iptr high><uptr low> # 32-bit target
|
|
* <padty fld-type-i64><iptr> # 64-bit target
|
|
* <padty fld-type-single><uptr>
|
|
* <padty fld-type-double><uptr high><uptr low>
|
|
* <padty fld-type> -> <byte pad << 5 | fld-type>
|
|
*
|
|
* <uptr n> -> <ubyte1>*<ubyte0>
|
|
* <ubyte1> -> k << 1 | 1, 0 <= k <= 127
|
|
* <ubyte0> -> k << 1 | 0, 0 <= k <= 127
|
|
* each ubyte represents 7 bits of the uptr, least-significant first
|
|
* low-order bit is continuation bit: 1 iff more bytes are present
|
|
*
|
|
* <iptr n> -> <ibyte0> | <ibyte1><ubyte1>*<ubyte0>
|
|
* <ibyte1> -> sign << 7 | k << 1 | 1, 0 <= k <= 63
|
|
* <ibyte0> -> sign << 7 | k << 1 | 0, 0 <= k <= 63
|
|
* leading ibyte represents least-significant 6 bits and sign
|
|
* each ubyte represents 7 of the remaining bits of the iptr,
|
|
* least-significant first
|
|
*
|
|
* Notes:
|
|
* * a list of length n will appear to be shorter in the fasl
|
|
* representation when the tail of the list is shared, since the
|
|
* shared tail will be a {graph-def} or {graph-ref}.
|
|
*
|
|
* * the length of a relocation table is the number of uptrs in the
|
|
* table, not the number of relocation entries.
|
|
*
|
|
* * closure offset is the amount added to the code object before
|
|
* storing it in the code field of the closure.
|
|
*
|
|
* * {graph} defines the size of the graph used to commonize shared
|
|
* structure, including cycles. It must appear before {graph-def}
|
|
* or {graph-ref}. A {graph-def} at index i must appear before
|
|
* a {graph-ref} at index i.
|
|
*
|
|
* * after an rtd is read: if its uname is unbound, the rtd is placed
|
|
* into the symbol value slot of the uname; otherwise, the rtd is
|
|
* discarded and the existing symbol value of uname is returned
|
|
* instead. Note that when many records appear within the same
|
|
* aggregrate structure, the full rtd will appear only in the
|
|
* first occurrence; the remainder will simply be graph references.
|
|
*
|
|
* * at present, the fasl representation supports only records
|
|
* containing only scheme-object fields.
|
|
*/
|
|
|
|
#include "system.h"
|
|
#include "zlib.h"
|
|
|
|
#ifdef WIN32
|
|
#include <io.h>
|
|
#endif /* WIN32 */
|
|
|
|
#ifdef NAN_INCLUDE
|
|
#include NAN_INCLUDE
|
|
#endif
|
|
|
|
#define UFFO_TYPE_GZ 1
|
|
#define UFFO_TYPE_FD 2
|
|
#define UFFO_TYPE_BV 3
|
|
|
|
/* we do our own buffering size gzgetc is slow */
|
|
typedef struct unbufFaslFileObj {
|
|
ptr path;
|
|
INT type;
|
|
INT fd;
|
|
glzFile file;
|
|
} *unbufFaslFile;
|
|
|
|
typedef struct faslFileObj {
|
|
unbufFaslFile uf;
|
|
iptr size;
|
|
octet *next;
|
|
octet *end;
|
|
octet *buf;
|
|
} *faslFile;
|
|
|
|
/* locally defined functions */
|
|
static INT uf_read PROTO((unbufFaslFile uf, octet *s, iptr n));
|
|
static octet uf_bytein PROTO((unbufFaslFile uf));
|
|
static uptr uf_uptrin PROTO((unbufFaslFile uf));
|
|
static ptr fasl_entry PROTO((ptr tc, IFASLCODE situation, unbufFaslFile uf));
|
|
static ptr bv_fasl_entry PROTO((ptr tc, ptr bv, unbufFaslFile uf));
|
|
static void fillFaslFile PROTO((faslFile f));
|
|
static void bytesin PROTO((octet *s, iptr n, faslFile f));
|
|
static void toolarge PROTO((ptr path));
|
|
static iptr iptrin PROTO((faslFile f));
|
|
static uptr uptrin PROTO((faslFile f));
|
|
static float singlein PROTO((faslFile f));
|
|
static double doublein PROTO((faslFile f));
|
|
static iptr stringin PROTO((ptr *pstrbuf, iptr start, faslFile f));
|
|
static void faslin PROTO((ptr tc, ptr *x, ptr t, ptr *pstrbuf, faslFile f));
|
|
static void fasl_record PROTO((ptr tc, ptr *x, ptr t, ptr *pstrbuf, faslFile f));
|
|
static IBOOL rtd_equiv PROTO((ptr x, ptr y));
|
|
static IBOOL equalp PROTO((ptr x, ptr y));
|
|
#ifdef ARMV6
|
|
static void arm32_set_abs PROTO((void *address, uptr item));
|
|
static uptr arm32_get_abs PROTO((void *address));
|
|
static void arm32_set_jump PROTO((void *address, uptr item, IBOOL callp));
|
|
static uptr arm32_get_jump PROTO((void *address));
|
|
#endif /* ARMV6 */
|
|
#ifdef PPC32
|
|
static void ppc32_set_abs PROTO((void *address, uptr item));
|
|
static uptr ppc32_get_abs PROTO((void *address));
|
|
static void ppc32_set_jump PROTO((void *address, uptr item, IBOOL callp));
|
|
static uptr ppc32_get_jump PROTO((void *address));
|
|
#endif /* PPC32 */
|
|
#ifdef X86_64
|
|
static void x86_64_set_jump PROTO((void *address, uptr item, IBOOL callp));
|
|
static uptr x86_64_get_jump PROTO((void *address));
|
|
#endif /* X86_64 */
|
|
#ifdef SPARC64
|
|
static INT extract_reg_from_sethi PROTO((void *address));
|
|
static void emit_sethi_lo PROTO((U32 item, INT destreg, void *address));
|
|
static uptr sparc64_get_literal PROTO((void *address));
|
|
static void sparc64_set_call PROTO((void *address, U32 *call_addr, uptr item));
|
|
static U32 adjust_delay_inst PROTO((U32 delay_inst, U32 *old_call_addr, U32 *new_call_addr));
|
|
static INT sparc64_set_lit_only PROTO((void *address, uptr item, I32 destreg));
|
|
static void sparc64_set_literal PROTO((void *address, uptr item));
|
|
#endif /* SPARC64 */
|
|
|
|
static double s_nan;
|
|
|
|
void S_fasl_init() {
|
|
if (S_boot_time) {
|
|
S_protect(&S_G.base_rtd);
|
|
S_G.base_rtd = Sfalse;
|
|
S_protect(&S_G.rtd_key);
|
|
S_G.rtd_key = S_intern((const unsigned char *)"*rtd*");
|
|
S_protect(&S_G.eq_symbol);
|
|
S_G.eq_symbol = S_intern((const unsigned char *)"eq");
|
|
S_protect(&S_G.eq_ht_rtd);
|
|
S_G.eq_ht_rtd = Sfalse;
|
|
S_protect(&S_G.symbol_symbol);
|
|
S_G.symbol_symbol = S_intern((const unsigned char *)"symbol");
|
|
S_protect(&S_G.symbol_ht_rtd);
|
|
S_G.symbol_ht_rtd = Sfalse;
|
|
S_protect(&S_G.eqp);
|
|
S_G.eqp = Sfalse;
|
|
S_protect(&S_G.eqvp);
|
|
S_G.eqvp = Sfalse;
|
|
S_protect(&S_G.equalp);
|
|
S_G.equalp = Sfalse;
|
|
S_protect(&S_G.symboleqp);
|
|
S_G.symboleqp = Sfalse;
|
|
}
|
|
|
|
MAKE_NAN(s_nan)
|
|
#ifndef WIN32 /* msvc returns true for s_nan==s_nan! */
|
|
if (s_nan == s_nan) {
|
|
fprintf(stderr, "s_nan == s_nan\n");
|
|
S_abnormal_exit();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
ptr S_fasl_read(ptr file, IBOOL gzflag, IFASLCODE situation, ptr path) {
|
|
ptr tc = get_thread_context();
|
|
ptr x; struct unbufFaslFileObj uffo;
|
|
|
|
/* acquire mutex in case we modify code pages */
|
|
tc_mutex_acquire()
|
|
uffo.path = path;
|
|
if (gzflag) {
|
|
uffo.type = UFFO_TYPE_GZ;
|
|
uffo.file = S_gzxfile_gzfile(file);
|
|
} else {
|
|
uffo.type = UFFO_TYPE_FD;
|
|
uffo.fd = GET_FD(file);
|
|
}
|
|
x = fasl_entry(tc, situation, &uffo);
|
|
tc_mutex_release()
|
|
return x;
|
|
}
|
|
|
|
ptr S_bv_fasl_read(ptr bv, ptr path) {
|
|
ptr tc = get_thread_context();
|
|
ptr x; struct unbufFaslFileObj uffo;
|
|
|
|
/* acquire mutex in case we modify code pages */
|
|
tc_mutex_acquire()
|
|
uffo.path = path;
|
|
uffo.type = UFFO_TYPE_BV;
|
|
x = bv_fasl_entry(tc, bv, &uffo);
|
|
tc_mutex_release()
|
|
return x;
|
|
}
|
|
|
|
ptr S_boot_read(glzFile file, const char *path) {
|
|
ptr tc = get_thread_context();
|
|
struct unbufFaslFileObj uffo;
|
|
|
|
uffo.path = Sstring_utf8(path, -1);
|
|
uffo.type = UFFO_TYPE_GZ;
|
|
uffo.file = file;
|
|
return fasl_entry(tc, fasl_type_visit_revisit, &uffo);
|
|
}
|
|
|
|
#define GZ_IO_SIZE_T unsigned int
|
|
|
|
#ifdef WIN32
|
|
#define IO_SIZE_T unsigned int
|
|
#else /* WIN32 */
|
|
#define IO_SIZE_T size_t
|
|
#endif /* WIN32 */
|
|
|
|
static INT uf_read(unbufFaslFile uf, octet *s, iptr n) {
|
|
iptr k; INT errnum;
|
|
while (n > 0) {
|
|
uptr nx = n;
|
|
|
|
#if (iptr_bits > 32)
|
|
if ((WIN32 || gzflag) && (unsigned int)nx != nx) nx = 0xffffffff;
|
|
#endif
|
|
|
|
switch (uf->type) {
|
|
case UFFO_TYPE_GZ:
|
|
k = S_glzread(uf->file, s, (GZ_IO_SIZE_T)nx);
|
|
if (k > 0)
|
|
n -= k;
|
|
else if (k == 0)
|
|
return -1;
|
|
else {
|
|
S_glzerror(uf->file, &errnum);
|
|
S_glzclearerr(uf->file);
|
|
if (errnum != Z_ERRNO || errno != EINTR)
|
|
S_error1("", "error reading from ~a", uf->path);
|
|
}
|
|
break;
|
|
case UFFO_TYPE_FD:
|
|
k = READ(uf->fd, s, (IO_SIZE_T)nx);
|
|
if (k > 0)
|
|
n -= k;
|
|
else if (k == 0)
|
|
return -1;
|
|
else if (errno != EINTR)
|
|
S_error1("", "error reading from ~a", uf->path);
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
s += k;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void uf_skipbytes(unbufFaslFile uf, iptr n) {
|
|
switch (uf->type) {
|
|
case UFFO_TYPE_GZ:
|
|
if (S_glzseek(uf->file, n, SEEK_CUR) == -1) {
|
|
S_error1("", "error seeking ~a", uf->path);
|
|
}
|
|
break;
|
|
case UFFO_TYPE_FD:
|
|
if (LSEEK(uf->fd, n, SEEK_CUR) == -1) {
|
|
S_error1("", "error seeking ~a", uf->path);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static octet uf_bytein(unbufFaslFile uf) {
|
|
octet buf[1];
|
|
if (uf_read(uf, buf, 1) < 0)
|
|
S_error1("", "unexpected eof in fasl file ~a", uf->path);
|
|
return buf[0];
|
|
}
|
|
|
|
static uptr uf_uptrin(unbufFaslFile uf) {
|
|
uptr n, m; octet k;
|
|
|
|
k = uf_bytein(uf);
|
|
n = k >> 1;
|
|
while (k & 1) {
|
|
k = uf_bytein(uf);
|
|
m = n << 7;
|
|
if (m >> 7 != n) toolarge(uf->path);
|
|
n = m | (k >> 1);
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
char *S_format_scheme_version(uptr n) {
|
|
static char buf[16]; INT len;
|
|
if ((n >> 16) != ((n >> 16) & 0xffff)) return "unknown";
|
|
if ((n & 0xff) == 0)
|
|
len = snprintf(buf, 16, "%d.%d", (int) n >> 16, (int) (n >> 8) & 0xff);
|
|
else
|
|
len = snprintf(buf, 16, "%d.%d.%d", (int) n >> 16, (int) (n >> 8) & 0xff,
|
|
(int) n & 0xff);
|
|
return len > 0 ? buf : "unknown";
|
|
}
|
|
|
|
char *S_lookup_machine_type(uptr n) {
|
|
static char *machine_type_table[] = machine_type_names;
|
|
if (n < machine_type_limit)
|
|
return machine_type_table[n];
|
|
else
|
|
return "unknown";
|
|
}
|
|
|
|
static ptr fasl_entry(ptr tc, IFASLCODE situation, unbufFaslFile uf) {
|
|
ptr x; ptr strbuf = S_G.null_string;
|
|
octet tybuf[1]; IFASLCODE ty; iptr size;
|
|
|
|
for (;;) {
|
|
if (uf_read(uf, tybuf, 1) < 0) return Seof_object;
|
|
ty = tybuf[0];
|
|
|
|
while (ty == fasl_type_header) {
|
|
uptr n; ICHAR c;
|
|
|
|
/* check for remainder of magic number */
|
|
if (uf_bytein(uf) != 0 ||
|
|
uf_bytein(uf) != 0 ||
|
|
uf_bytein(uf) != 0 ||
|
|
uf_bytein(uf) != 'c' ||
|
|
uf_bytein(uf) != 'h' ||
|
|
uf_bytein(uf) != 'e' ||
|
|
uf_bytein(uf) != 'z')
|
|
S_error1("", "malformed fasl-object header (missing magic word) found in ~a", uf->path);
|
|
|
|
if ((n = uf_uptrin(uf)) != scheme_version)
|
|
S_error2("", "incompatible fasl-object version ~a found in ~a", S_string(S_format_scheme_version(n), -1), uf->path);
|
|
|
|
if ((n = uf_uptrin(uf)) != machine_type_any && n != machine_type)
|
|
S_error2("", "incompatible fasl-object machine-type ~a found in ~a", S_string(S_lookup_machine_type(n), -1), uf->path);
|
|
|
|
if (uf_bytein(uf) != '(')
|
|
S_error1("", "malformed fasl-object header (missing open paren) found in ~a", uf->path);
|
|
|
|
while ((c = uf_bytein(uf)) != ')')
|
|
if (c < 0) S_error1("", "malformed fasl-object header (missing close paren) found in ~a", uf->path);
|
|
|
|
ty = uf_bytein(uf);
|
|
}
|
|
|
|
switch (ty) {
|
|
case fasl_type_visit:
|
|
case fasl_type_revisit:
|
|
case fasl_type_visit_revisit:
|
|
break;
|
|
default:
|
|
S_error2("", "malformed fasl-object header (missing situation, got ~s) found in ~a", FIX(ty), uf->path);
|
|
return (ptr)0;
|
|
}
|
|
|
|
if (uf_bytein(uf) != fasl_type_fasl_size)
|
|
S_error1("", "malformed fasl-object header (missing fasl-size) found in ~a", uf->path);
|
|
|
|
size = uf_uptrin(uf);
|
|
|
|
if (ty == situation || situation == fasl_type_visit_revisit || ty == fasl_type_visit_revisit) {
|
|
struct faslFileObj ffo; octet buf[SBUFSIZ];
|
|
|
|
ffo.size = size;
|
|
ffo.buf = buf;
|
|
ffo.next = ffo.end = ffo.buf;
|
|
ffo.uf = uf;
|
|
faslin(tc, &x, S_G.null_vector, &strbuf, &ffo);
|
|
S_flush_instruction_cache(tc);
|
|
return x;
|
|
} else {
|
|
uf_skipbytes(uf, size);
|
|
}
|
|
}
|
|
}
|
|
|
|
static ptr bv_fasl_entry(ptr tc, ptr bv, unbufFaslFile uf) {
|
|
ptr x; ptr strbuf = S_G.null_string;
|
|
struct faslFileObj ffo;
|
|
|
|
ffo.size = Sbytevector_length(bv);
|
|
ffo.next = ffo.buf = &BVIT(bv, 0);
|
|
ffo.end = &BVIT(bv, ffo.size);
|
|
ffo.uf = uf;
|
|
|
|
faslin(tc, &x, S_G.null_vector, &strbuf, &ffo);
|
|
S_flush_instruction_cache(tc);
|
|
return x;
|
|
}
|
|
|
|
static void fillFaslFile(faslFile f) {
|
|
iptr n = f->size < SBUFSIZ ? f->size : SBUFSIZ;
|
|
if (uf_read(f->uf, f->buf, n) < 0)
|
|
S_error1("", "unexpected eof in fasl file ~a", f->uf->path);
|
|
f->end = (f->next = f->buf) + n;
|
|
f->size -= n;
|
|
}
|
|
|
|
#define bytein(f) ((((f)->next == (f)->end) ? fillFaslFile(f) : (void)0), *((f)->next++))
|
|
|
|
static void bytesin(octet *s, iptr n, faslFile f) {
|
|
iptr avail = f->end - f->next;
|
|
if (avail < n) {
|
|
if (avail != 0) {
|
|
memcpy(s, f->next, avail);
|
|
f->next = f->end;
|
|
n -= avail;
|
|
s += avail;
|
|
}
|
|
if (uf_read(f->uf, s, n) < 0)
|
|
S_error1("", "unexpected eof in fasl file ~a", f->uf->path);
|
|
f->size -= n;
|
|
} else {
|
|
memcpy(s, f->next, n);
|
|
f->next += n;
|
|
}
|
|
}
|
|
|
|
static void toolarge(ptr path) {
|
|
S_error1("", "fasl value too large for this machine type in ~a", path);
|
|
}
|
|
|
|
static iptr iptrin(faslFile f) {
|
|
uptr n, m; octet k, k0;
|
|
|
|
k0 = k = bytein(f);
|
|
n = (k & 0x7f) >> 1;
|
|
while (k & 1) {
|
|
k = bytein(f);
|
|
m = n << 7;
|
|
if (m >> 7 != n) toolarge(f->uf->path);
|
|
n = m | (k >> 1);
|
|
}
|
|
|
|
if (k0 & 0x80) {
|
|
if (n < ((uptr)1 << (ptr_bits - 1))) {
|
|
return -(iptr)n;
|
|
} else if (n > ((uptr)1 << (ptr_bits - 1))) {
|
|
toolarge(f->uf->path);
|
|
}
|
|
#if (fixnum_bits > 32)
|
|
return (iptr)0x8000000000000000;
|
|
#else
|
|
return (iptr)0x80000000;
|
|
#endif
|
|
} else {
|
|
if (n >= ((uptr)1 << (ptr_bits - 1))) toolarge(f->uf->path);
|
|
return (iptr)n;
|
|
}
|
|
}
|
|
|
|
static uptr uptrin(faslFile f) {
|
|
uptr n, m; octet k;
|
|
|
|
k = bytein(f);
|
|
n = k >> 1;
|
|
while (k & 1) {
|
|
k = bytein(f);
|
|
m = n << 7;
|
|
if (m >> 7 != n) toolarge(f->uf->path);
|
|
n = m | (k >> 1);
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
static float singlein(faslFile f) {
|
|
union { float f; U32 u; } val;
|
|
|
|
val.u = (U32)uptrin(f);
|
|
|
|
return val.f;
|
|
}
|
|
|
|
static double doublein(faslFile f) {
|
|
#ifdef LITTLE_ENDIAN_IEEE_DOUBLE
|
|
union { double d; struct { U32 l; U32 h; } u; } val;
|
|
#else
|
|
union { double d; struct { U32 h; U32 l; } u; } val;
|
|
#endif
|
|
|
|
val.u.h = (U32)uptrin(f);
|
|
val.u.l = (U32)uptrin(f);
|
|
|
|
return val.d;
|
|
}
|
|
|
|
static iptr stringin(ptr *pstrbuf, iptr start, faslFile f) {
|
|
iptr end, n, i; ptr p = *pstrbuf;
|
|
|
|
end = start + (n = uptrin(f));
|
|
if (Sstring_length(*pstrbuf) < end) {
|
|
ptr newp = S_string((char *)0, end);
|
|
for (i = 0; i != start; i += 1) Sstring_set(newp, i, Sstring_ref(p, i));
|
|
*pstrbuf = p = newp;
|
|
}
|
|
for (i = start; i != end; i += 1) Sstring_set(p, i, uptrin(f));
|
|
return n;
|
|
}
|
|
|
|
static void faslin(ptr tc, ptr *x, ptr t, ptr *pstrbuf, faslFile f) {
|
|
IFASLCODE ty = bytein(f);
|
|
switch (ty) {
|
|
case fasl_type_pair: {
|
|
iptr n; ptr p;
|
|
n = uptrin(f);
|
|
*x = p = Scons(FIX(0), FIX(0));
|
|
faslin(tc, &INITCAR(p), t, pstrbuf, f);
|
|
while (--n) {
|
|
INITCDR(p) = Scons(FIX(0), FIX(0));
|
|
p = INITCDR(p);
|
|
faslin(tc, &INITCAR(p), t, pstrbuf, f);
|
|
}
|
|
faslin(tc, &INITCDR(p), t, pstrbuf, f);
|
|
return;
|
|
}
|
|
case fasl_type_box:
|
|
case fasl_type_immutable_box:
|
|
*x = Sbox(FIX(0));
|
|
faslin(tc, &INITBOXREF(*x), t, pstrbuf, f);
|
|
if (ty == fasl_type_immutable_box)
|
|
BOXTYPE(*x) = type_immutable_box;
|
|
return;
|
|
case fasl_type_symbol: {
|
|
iptr n;
|
|
n = stringin(pstrbuf, 0, f);
|
|
*x = S_intern_sc(&STRIT(*pstrbuf, 0), n, Sfalse);
|
|
return;
|
|
}
|
|
case fasl_type_gensym: {
|
|
iptr pn, un;
|
|
pn = stringin(pstrbuf, 0, f);
|
|
un = stringin(pstrbuf, pn, f);
|
|
*x = S_intern3(&STRIT(*pstrbuf, 0), pn, &STRIT(*pstrbuf, pn), un, Sfalse, Sfalse);
|
|
return;
|
|
}
|
|
case fasl_type_ratnum:
|
|
*x = S_rational(FIX(0), FIX(0));
|
|
faslin(tc, &RATNUM(*x), t, pstrbuf, f);
|
|
faslin(tc, &RATDEN(*x), t, pstrbuf, f);
|
|
return;
|
|
case fasl_type_exactnum:
|
|
*x = S_exactnum(FIX(0), FIX(0));
|
|
faslin(tc, &EXACTNUM_REAL_PART(*x), t, pstrbuf, f);
|
|
faslin(tc, &EXACTNUM_IMAG_PART(*x), t, pstrbuf, f);
|
|
return;
|
|
case fasl_type_vector:
|
|
case fasl_type_immutable_vector: {
|
|
iptr n; ptr *p;
|
|
n = uptrin(f);
|
|
*x = S_vector(n);
|
|
p = &INITVECTIT(*x, 0);
|
|
while (n--) faslin(tc, p++, t, pstrbuf, f);
|
|
if (ty == fasl_type_immutable_vector) {
|
|
if (Svector_length(*x) == 0)
|
|
*x = NULLIMMUTABLEVECTOR(tc);
|
|
else
|
|
VECTTYPE(*x) |= vector_immutable_flag;
|
|
}
|
|
return;
|
|
}
|
|
case fasl_type_fxvector:
|
|
case fasl_type_immutable_fxvector: {
|
|
iptr n; ptr *p;
|
|
n = uptrin(f);
|
|
*x = S_fxvector(n);
|
|
p = &FXVECTIT(*x, 0);
|
|
while (n--) {
|
|
iptr t = iptrin(f);
|
|
if (!FIXRANGE(t)) toolarge(f->uf->path);
|
|
*p++ = FIX(t);
|
|
}
|
|
if (ty == fasl_type_immutable_fxvector) {
|
|
if (Sfxvector_length(*x) == 0)
|
|
*x = NULLIMMUTABLEFXVECTOR(tc);
|
|
else
|
|
FXVECTOR_TYPE(*x) |= fxvector_immutable_flag;
|
|
}
|
|
return;
|
|
}
|
|
case fasl_type_bytevector:
|
|
case fasl_type_immutable_bytevector: {
|
|
iptr n;
|
|
n = uptrin(f);
|
|
*x = S_bytevector(n);
|
|
bytesin(&BVIT(*x,0), n, f);
|
|
if (ty == fasl_type_immutable_bytevector) {
|
|
if (Sbytevector_length(*x) == 0)
|
|
*x = NULLIMMUTABLEBYTEVECTOR(tc);
|
|
else
|
|
BYTEVECTOR_TYPE(*x) |= bytevector_immutable_flag;
|
|
}
|
|
return;
|
|
}
|
|
case fasl_type_base_rtd: {
|
|
ptr rtd;
|
|
if ((rtd = S_G.base_rtd) == Sfalse) {
|
|
if (!Srecordp(rtd)) S_error_abort("S_G.base-rtd has not been set");
|
|
}
|
|
*x = rtd;
|
|
return;
|
|
} case fasl_type_rtd: {
|
|
ptr rtd, rtd_uid, plist, ls;
|
|
|
|
faslin(tc, &rtd_uid, t, pstrbuf, f);
|
|
|
|
/* look for rtd on uid's property list */
|
|
plist = SYMSPLIST(rtd_uid);
|
|
for (ls = plist; ls != Snil; ls = Scdr(Scdr(ls))) {
|
|
if (Scar(ls) == S_G.rtd_key) {
|
|
ptr tmp;
|
|
*x = rtd = Scar(Scdr(ls));
|
|
fasl_record(tc, &tmp, t, pstrbuf, f);
|
|
if (!rtd_equiv(tmp, rtd))
|
|
S_error2("", "incompatible record type ~s in ~a", RECORDDESCNAME(tmp), f->uf->path);
|
|
return;
|
|
}
|
|
}
|
|
|
|
fasl_record(tc, x, t, pstrbuf, f);
|
|
rtd = *x;
|
|
|
|
/* register rtd on uid's property list */
|
|
SETSYMSPLIST(rtd_uid, Scons(S_G.rtd_key, Scons(rtd, plist)));
|
|
return;
|
|
}
|
|
case fasl_type_record: {
|
|
fasl_record(tc, x, t, pstrbuf, f);
|
|
return;
|
|
}
|
|
case fasl_type_eq_hashtable: {
|
|
ptr rtd, ht, v; uptr subtype; uptr veclen, i, n;
|
|
if ((rtd = S_G.eq_ht_rtd) == Sfalse) {
|
|
S_G.eq_ht_rtd = rtd = SYMVAL(S_intern((const unsigned char *)"$eq-ht-rtd"));
|
|
if (!Srecordp(rtd)) S_error_abort("$eq-ht-rtd has not been set");
|
|
}
|
|
*x = ht = S_record(size_record_inst(UNFIX(RECORDDESCSIZE(rtd))));
|
|
RECORDINSTTYPE(ht) = rtd;
|
|
INITPTRFIELD(ht,eq_hashtable_type_disp) = S_G.eq_symbol;
|
|
INITPTRFIELD(ht,eq_hashtable_mutablep_disp) = bytein(f) ? Strue : Sfalse;
|
|
switch ((subtype = bytein(f))) {
|
|
case eq_hashtable_subtype_normal:
|
|
case eq_hashtable_subtype_weak:
|
|
case eq_hashtable_subtype_ephemeron:
|
|
INITPTRFIELD(ht,eq_hashtable_subtype_disp) = FIX(subtype);
|
|
break;
|
|
default:
|
|
S_error2("", "invalid eq-hashtable subtype code", FIX(subtype), f->uf->path);
|
|
}
|
|
INITPTRFIELD(ht,eq_hashtable_minlen_disp) = FIX(uptrin(f));
|
|
veclen = uptrin(f);
|
|
INITPTRFIELD(ht,eq_hashtable_vec_disp) = v = S_vector(veclen);
|
|
n = uptrin(f);
|
|
INITPTRFIELD(ht,eq_hashtable_size_disp) = FIX(n);
|
|
for (i = 0; i < veclen ; i += 1) { INITVECTIT(v, i) = FIX(i); }
|
|
while (n > 0) {
|
|
ptr keyval;
|
|
switch (subtype) {
|
|
case eq_hashtable_subtype_normal:
|
|
keyval = Scons(FIX(0), FIX(0));
|
|
break;
|
|
case eq_hashtable_subtype_weak:
|
|
keyval = S_cons_in(space_weakpair, 0, FIX(0), FIX(0));
|
|
break;
|
|
case eq_hashtable_subtype_ephemeron:
|
|
default:
|
|
keyval = S_cons_in(space_ephemeron, 0, FIX(0), FIX(0));
|
|
break;
|
|
}
|
|
faslin(tc, &INITCAR(keyval), t, pstrbuf, f);
|
|
faslin(tc, &INITCDR(keyval), t, pstrbuf, f);
|
|
i = ((uptr)Scar(keyval) >> primary_type_bits) & (veclen - 1);
|
|
INITVECTIT(v, i) = S_tlc(keyval, ht, Svector_ref(v, i));
|
|
n -= 1;
|
|
}
|
|
return;
|
|
}
|
|
case fasl_type_symbol_hashtable: {
|
|
ptr rtd, ht, equiv, v; uptr equiv_code, veclen, i, n;
|
|
if ((rtd = S_G.symbol_ht_rtd) == Sfalse) {
|
|
S_G.symbol_ht_rtd = rtd = SYMVAL(S_intern((const unsigned char *)"$symbol-ht-rtd"));
|
|
if (!Srecordp(rtd)) S_error_abort("$symbol-ht-rtd has not been set");
|
|
}
|
|
*x = ht = S_record(size_record_inst(UNFIX(RECORDDESCSIZE(rtd))));
|
|
RECORDINSTTYPE(ht) = rtd;
|
|
INITPTRFIELD(ht,symbol_hashtable_type_disp) = S_G.symbol_symbol;
|
|
INITPTRFIELD(ht,symbol_hashtable_mutablep_disp) = bytein(f) ? Strue : Sfalse;
|
|
INITPTRFIELD(ht,symbol_hashtable_minlen_disp) = FIX(uptrin(f));
|
|
equiv_code = bytein(f);
|
|
switch (equiv_code) {
|
|
case 0:
|
|
if ((equiv = S_G.eqp) == Sfalse) {
|
|
S_G.eqp = equiv = SYMVAL(S_intern((const unsigned char *)"eq?"));
|
|
if (!Sprocedurep(equiv)) S_error_abort("fasl: eq? has not been set");
|
|
}
|
|
break;
|
|
case 1:
|
|
if ((equiv = S_G.eqvp) == Sfalse) {
|
|
S_G.eqvp = equiv = SYMVAL(S_intern((const unsigned char *)"eqv?"));
|
|
if (!Sprocedurep(equiv)) S_error_abort("fasl: eqv? has not been set");
|
|
}
|
|
break;
|
|
case 2:
|
|
if ((equiv = S_G.equalp) == Sfalse) {
|
|
S_G.equalp = equiv = SYMVAL(S_intern((const unsigned char *)"equal?"));
|
|
if (!Sprocedurep(equiv)) S_error_abort("fasl: equal? has not been set");
|
|
}
|
|
break;
|
|
case 3:
|
|
if ((equiv = S_G.symboleqp) == Sfalse) {
|
|
S_G.symboleqp = equiv = SYMVAL(S_intern((const unsigned char *)"symbol=?"));
|
|
if (!Sprocedurep(equiv)) S_error_abort("fasl: symbol=? has not been set");
|
|
}
|
|
break;
|
|
default:
|
|
S_error2("", "invalid symbol-hashtable equiv code", FIX(equiv_code), f->uf->path);
|
|
/* make compiler happy */
|
|
equiv = Sfalse;
|
|
}
|
|
INITPTRFIELD(ht,symbol_hashtable_equivp_disp) = equiv;
|
|
veclen = uptrin(f);
|
|
INITPTRFIELD(ht,symbol_hashtable_vec_disp) = v = S_vector(veclen);
|
|
n = uptrin(f);
|
|
INITPTRFIELD(ht,symbol_hashtable_size_disp) = FIX(n);
|
|
for (i = 0; i < veclen ; i += 1) { INITVECTIT(v, i) = Snil; }
|
|
while (n > 0) {
|
|
ptr keyval;
|
|
keyval = Scons(FIX(0), FIX(0));
|
|
faslin(tc, &INITCAR(keyval), t, pstrbuf, f);
|
|
faslin(tc, &INITCDR(keyval), t, pstrbuf, f);
|
|
i = UNFIX(SYMHASH(Scar(keyval))) & (veclen - 1);
|
|
INITVECTIT(v, i) = Scons(keyval, Svector_ref(v, i));
|
|
n -= 1;
|
|
}
|
|
return;
|
|
}
|
|
case fasl_type_closure: {
|
|
ptr cod; iptr offset;
|
|
offset = uptrin(f);
|
|
*x = S_closure((ptr)0, 0);
|
|
faslin(tc, &cod, t, pstrbuf, f);
|
|
CLOSENTRY(*x) = (ptr)((uptr)cod + offset);
|
|
return;
|
|
}
|
|
case fasl_type_flonum: {
|
|
*x = Sflonum(doublein(f));
|
|
return;
|
|
}
|
|
case fasl_type_inexactnum: {
|
|
ptr rp, ip;
|
|
faslin(tc, &rp, t, pstrbuf, f);
|
|
faslin(tc, &ip, t, pstrbuf, f);
|
|
*x = S_inexactnum(FLODAT(rp), FLODAT(ip));
|
|
return;
|
|
}
|
|
case fasl_type_string:
|
|
case fasl_type_immutable_string: {
|
|
iptr i, n; ptr str;
|
|
n = uptrin(f);
|
|
str = S_string((char *)0, n);
|
|
for (i = 0; i != n; i += 1) Sstring_set(str, i, uptrin(f));
|
|
if (ty == fasl_type_immutable_string) {
|
|
if (n == 0)
|
|
str = NULLIMMUTABLESTRING(tc);
|
|
else
|
|
STRTYPE(str) |= string_immutable_flag;
|
|
}
|
|
*x = str;
|
|
return;
|
|
}
|
|
case fasl_type_small_integer:
|
|
*x = Sinteger(iptrin(f));
|
|
return;
|
|
case fasl_type_large_integer: {
|
|
IBOOL sign; iptr n; ptr t; bigit *p;
|
|
sign = bytein(f);
|
|
n = uptrin(f);
|
|
t = S_bignum(n, sign);
|
|
p = &BIGIT(t, 0);
|
|
while (n--) *p++ = (bigit)uptrin(f);
|
|
*x = S_normalize_bignum(t);
|
|
return;
|
|
}
|
|
case fasl_type_weak_pair:
|
|
*x = S_cons_in(space_weakpair, 0, FIX(0), FIX(0));
|
|
faslin(tc, &INITCAR(*x), t, pstrbuf, f);
|
|
faslin(tc, &INITCDR(*x), t, pstrbuf, f);
|
|
return;
|
|
case fasl_type_ephemeron:
|
|
*x = S_cons_in(space_ephemeron, 0, FIX(0), FIX(0));
|
|
faslin(tc, &INITCAR(*x), t, pstrbuf, f);
|
|
faslin(tc, &INITCDR(*x), t, pstrbuf, f);
|
|
return;
|
|
case fasl_type_code: {
|
|
iptr n, m, a; INT flags; iptr free;
|
|
ptr co, reloc, name, pinfos;
|
|
flags = bytein(f);
|
|
free = uptrin(f);
|
|
n = uptrin(f) /* length in bytes of code */;
|
|
*x = co = S_code(tc, type_code | (flags << code_flags_offset), n);
|
|
CODEFREE(co) = free;
|
|
faslin(tc, &name, t, pstrbuf, f);
|
|
if (Sstringp(name)) name = SYMNAME(S_intern_sc(&STRIT(name, 0), Sstring_length(name), name));
|
|
CODENAME(co) = name;
|
|
faslin(tc, &CODEARITYMASK(co), t, pstrbuf, f);
|
|
faslin(tc, &CODEINFO(co), t, pstrbuf, f);
|
|
faslin(tc, &pinfos, t, pstrbuf, f);
|
|
CODEPINFOS(co) = pinfos;
|
|
if (pinfos != Snil) {
|
|
S_G.profile_counters = Scons(S_weak_cons(co, pinfos), S_G.profile_counters);
|
|
}
|
|
bytesin((octet *)&CODEIT(co, 0), n, f);
|
|
m = uptrin(f);
|
|
CODERELOC(co) = reloc = S_relocation_table(m);
|
|
RELOCCODE(reloc) = co;
|
|
a = 0;
|
|
n = 0;
|
|
while (n < m) {
|
|
INT type_etc, type; uptr item_off, code_off;
|
|
ptr obj;
|
|
type_etc = bytein(f);
|
|
type = type_etc >> 2;
|
|
code_off = uptrin(f);
|
|
item_off = (type_etc & 2) ? uptrin(f) : 0;
|
|
if (type_etc & 1) {
|
|
RELOCIT(reloc,n) = (type << reloc_type_offset)|reloc_extended_format ; n += 1;
|
|
RELOCIT(reloc,n) = item_off; n += 1;
|
|
RELOCIT(reloc,n) = code_off; n += 1;
|
|
} else {
|
|
RELOCIT(reloc,n) = MAKE_SHORT_RELOC(type,code_off,item_off); n += 1;
|
|
}
|
|
a += code_off;
|
|
faslin(tc, &obj, t, pstrbuf, f);
|
|
S_set_code_obj("read", type, co, a, obj, item_off);
|
|
}
|
|
return;
|
|
}
|
|
case fasl_type_immediate:
|
|
*x = (ptr)uptrin(f);
|
|
return;
|
|
case fasl_type_entry:
|
|
*x = (ptr)S_lookup_c_entry(uptrin(f));
|
|
return;
|
|
case fasl_type_library:
|
|
*x = S_lookup_library_entry(uptrin(f), 1);
|
|
return;
|
|
case fasl_type_library_code:
|
|
*x = CLOSCODE(S_lookup_library_entry(uptrin(f), 1));
|
|
return;
|
|
case fasl_type_graph:
|
|
faslin(tc, x, S_vector(uptrin(f)), pstrbuf, f);
|
|
return;
|
|
case fasl_type_graph_def: {
|
|
ptr *p;
|
|
p = &INITVECTIT(t, uptrin(f));
|
|
faslin(tc, p, t, pstrbuf, f);
|
|
*x = *p;
|
|
return;
|
|
}
|
|
case fasl_type_graph_ref:
|
|
*x = Svector_ref(t, uptrin(f));
|
|
return;
|
|
default:
|
|
S_error2("", "invalid object type ~d in fasl file ~a", FIX(ty), f->uf->path);
|
|
}
|
|
}
|
|
|
|
#define big 0
|
|
#define little 1
|
|
static void fasl_record(ptr tc, ptr *x, ptr t, ptr *pstrbuf, faslFile f) {
|
|
uptr size, n, addr; ptr p; UINT padty;
|
|
|
|
size = uptrin(f);
|
|
n = uptrin(f);
|
|
*x = p = S_record(size_record_inst(size));
|
|
faslin(tc, &RECORDINSTTYPE(p), t, pstrbuf, f);
|
|
addr = (uptr)&RECORDINSTIT(p, 0);
|
|
for (; n != 0; n -= 1) {
|
|
padty = bytein(f);
|
|
addr += padty >> 4;
|
|
switch (padty & 0xf) {
|
|
case fasl_fld_ptr:
|
|
faslin(tc, (ptr *)addr, t, pstrbuf, f);
|
|
addr += sizeof(ptr);
|
|
break;
|
|
case fasl_fld_u8:
|
|
*(U8 *)addr = (U8)bytein(f);
|
|
addr += 1;
|
|
break;
|
|
case fasl_fld_i16:
|
|
*(I16 *)addr = (I16)iptrin(f);
|
|
addr += 2;
|
|
break;
|
|
case fasl_fld_i24: {
|
|
iptr q = iptrin(f);
|
|
#if (native_endianness == little)
|
|
*(U16 *)addr = (U16)q;
|
|
*(U8 *)(addr + 2) = (U8)(q >> 16);
|
|
#elif (native_endianness == big)
|
|
*(U16 *)addr = (U16)(q >> 8);
|
|
*(U8 *)(addr + 2) = (U8)q;
|
|
#else
|
|
unexpected_endianness();
|
|
#endif
|
|
addr += 3;
|
|
break;
|
|
}
|
|
case fasl_fld_i32:
|
|
*(I32 *)addr = (I32)iptrin(f);
|
|
addr += 4;
|
|
break;
|
|
case fasl_fld_i40: {
|
|
I64 q;
|
|
#if (ptr_bits == 32)
|
|
q = (I64)iptrin(f) << 32;
|
|
q |= (U32)uptrin(f);
|
|
#elif (ptr_bits == 64)
|
|
q = (I64)iptrin(f);
|
|
#else
|
|
unexpected_ptr_bits();
|
|
#endif
|
|
#if (native_endianness == little)
|
|
*(U32 *)addr = (U32)q;
|
|
*(U8 *)(addr + 4) = (U8)(q >> 32);
|
|
#elif (native_endianness == big)
|
|
*(U32 *)addr = (U32)(q >> 8);
|
|
*(U8 *)(addr + 4) = (U8)q;
|
|
#else
|
|
unexpected_endianness();
|
|
#endif
|
|
addr += 5;
|
|
break;
|
|
}
|
|
case fasl_fld_i48: {
|
|
I64 q;
|
|
#if (ptr_bits == 32)
|
|
q = (I64)iptrin(f) << 32;
|
|
q |= (U32)uptrin(f);
|
|
#elif (ptr_bits == 64)
|
|
q = (I64)iptrin(f);
|
|
#else
|
|
unexpected_ptr_bits();
|
|
#endif
|
|
#if (native_endianness == little)
|
|
*(U32 *)addr = (U32)q;
|
|
*(U16 *)(addr + 4) = (U16)(q >> 32);
|
|
#elif (native_endianness == big)
|
|
*(U32 *)addr = (U32)(q >> 16);
|
|
*(U16 *)(addr + 4) = (U16)q;
|
|
#else
|
|
unexpected_endianness();
|
|
#endif
|
|
addr += 6;
|
|
break;
|
|
}
|
|
case fasl_fld_i56: {
|
|
I64 q;
|
|
#if (ptr_bits == 32)
|
|
q = (I64)iptrin(f) << 32;
|
|
q |= (U32)uptrin(f);
|
|
#elif (ptr_bits == 64)
|
|
q = (I64)iptrin(f);
|
|
#else
|
|
unexpected_ptr_bits();
|
|
#endif
|
|
#if (native_endianness == little)
|
|
*(U32 *)addr = (U32)q;
|
|
*(U16 *)(addr + 4) = (U16)(q >> 32);
|
|
*(U8 *)(addr + 6) = (U8)(q >> 48);
|
|
#elif (native_endianness == big)
|
|
*(U32 *)addr = (U32)(q >> 24);
|
|
*(U32 *)(addr + 3) = (U32)q;
|
|
#else
|
|
unexpected_endianness();
|
|
#endif
|
|
addr += 7;
|
|
break;
|
|
}
|
|
case fasl_fld_i64: {
|
|
I64 q;
|
|
#if (ptr_bits == 32)
|
|
q = (I64)iptrin(f) << 32;
|
|
q |= (U32)uptrin(f);
|
|
#elif (ptr_bits == 64)
|
|
q = (I64)iptrin(f);
|
|
#else
|
|
unexpected_ptr_bits();
|
|
#endif
|
|
*(I64 *)addr = q;
|
|
addr += 8;
|
|
break;
|
|
}
|
|
case fasl_fld_single:
|
|
*(float *)addr = (float)singlein(f);
|
|
addr += sizeof(float);
|
|
break;
|
|
case fasl_fld_double:
|
|
*(double *)addr = (double)doublein(f);
|
|
addr += sizeof(double);
|
|
break;
|
|
default:
|
|
S_error1("", "unrecognized record fld type ~d", FIX(padty & 0xf));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* limited version for checking rtd fields */
|
|
static IBOOL equalp(x, y) ptr x, y; {
|
|
if (x == y) return 1;
|
|
if (Spairp(x)) return Spairp(y) && equalp(Scar(x), Scar(y)) && equalp(Scdr(x), Scdr(y));
|
|
if (Svectorp(x)) {
|
|
iptr n;
|
|
if (!Svectorp(y)) return 0;
|
|
if ((n = Svector_length(x)) != Svector_length(y)) return 0;
|
|
while (--n >= 0) if (!equalp(Svector_ref(x, n), Svector_ref(y, n))) return 0;
|
|
return 1;
|
|
}
|
|
return Sbignump(x) && Sbignump(y) && S_big_eq(x, y);
|
|
}
|
|
|
|
static IBOOL rtd_equiv(x, y) ptr x, y; {
|
|
return RECORDINSTTYPE(x) == RECORDINSTTYPE(y) &&
|
|
RECORDDESCPARENT(x) == RECORDDESCPARENT(y) &&
|
|
equalp(RECORDDESCPM(x), RECORDDESCPM(y)) &&
|
|
equalp(RECORDDESCMPM(x), RECORDDESCMPM(y)) &&
|
|
equalp(RECORDDESCFLDS(x), RECORDDESCFLDS(y)) &&
|
|
RECORDDESCSIZE(x) == RECORDDESCSIZE(y) &&
|
|
RECORDDESCFLAGS(x) == RECORDDESCFLAGS(y);
|
|
}
|
|
|
|
#ifdef HPUX
|
|
INT pax_decode21(INT x)
|
|
{
|
|
INT x0_4, x5_6, x7_8, x9_19, x20;
|
|
|
|
x20 = x & 0x1; x >>= 1;
|
|
x9_19 = x & 0x7ff; x >>= 11;
|
|
x7_8 = x & 0x3; x >>= 2;
|
|
x5_6 = x & 0x3;
|
|
x0_4 = x >> 2;
|
|
|
|
return (((x20<<11 | x9_19)<<2 | x5_6)<<5 | x0_4)<<2 | x7_8;
|
|
}
|
|
|
|
INT pax_encode21(INT n)
|
|
{
|
|
INT x0_4, x5_6, x7_8, x9_19, x20;
|
|
|
|
x7_8 = n & 0x3; n >>= 2;
|
|
x0_4 = n & 0x1f; n >>= 5;
|
|
x5_6 = n & 0x3; n >>= 2;
|
|
x9_19 = n & 0x7ff;
|
|
x20 = n >> 11;
|
|
|
|
return (((x0_4<<2 | x5_6)<<2 | x7_8)<<11 | x9_19)<<1 | x20;
|
|
}
|
|
#endif /* HPUX */
|
|
|
|
/* used here, in S_gc(), and in compile.ss */
|
|
void S_set_code_obj(who, typ, p, n, x, o) char *who; IFASLCODE typ; iptr n, o; ptr p, x; {
|
|
void *address; uptr item;
|
|
|
|
address = (void *)((uptr)p + n);
|
|
item = (uptr)x + o;
|
|
switch (typ) {
|
|
case reloc_abs:
|
|
*(uptr *)address = item;
|
|
break;
|
|
#ifdef ARMV6
|
|
case reloc_arm32_abs:
|
|
arm32_set_abs(address, item);
|
|
break;
|
|
case reloc_arm32_jump:
|
|
arm32_set_jump(address, item, 0);
|
|
break;
|
|
case reloc_arm32_call:
|
|
arm32_set_jump(address, item, 1);
|
|
break;
|
|
#endif /* ARMV6 */
|
|
#ifdef PPC32
|
|
case reloc_ppc32_abs:
|
|
ppc32_set_abs(address, item);
|
|
break;
|
|
case reloc_ppc32_jump:
|
|
ppc32_set_jump(address, item, 0);
|
|
break;
|
|
case reloc_ppc32_call:
|
|
ppc32_set_jump(address, item, 1);
|
|
break;
|
|
#endif /* PPC32 */
|
|
#ifdef I386
|
|
case reloc_rel:
|
|
item = item - ((uptr)address + sizeof(uptr));
|
|
*(uptr *)address = item;
|
|
break;
|
|
#endif /* I386 */
|
|
#ifdef X86_64
|
|
case reloc_x86_64_jump:
|
|
x86_64_set_jump(address, item, 0);
|
|
break;
|
|
case reloc_x86_64_call:
|
|
x86_64_set_jump(address, item, 1);
|
|
break;
|
|
#endif /* X86_64 */
|
|
#ifdef SPARC64
|
|
case reloc_sparc64abs:
|
|
sparc64_set_literal(address, item);
|
|
break;
|
|
/* we don't use this presently since it can't handle out-of-range
|
|
relocations */
|
|
case reloc_sparc64rel:
|
|
/* later: make the damn thing local by copying it an
|
|
every other code object we can reach into a single
|
|
close area of memory */
|
|
item = item - (uptr)address;
|
|
if ((iptr)item < -0x20000000 || (iptr)item > 0x1FFFFFFF)
|
|
S_error1("", "sparc64rel address out of range ~x",
|
|
Sunsigned((uptr)address));
|
|
*(U32 *)address = *(U32 *)address & ~0x3fffffff | item >> 2 & 0x3fffffff;
|
|
break;
|
|
#endif /* SPARC64 */
|
|
#ifdef SPARC
|
|
case reloc_sparcabs:
|
|
*(U32 *)address = *(U32 *)address & ~0x3fffff | item >> 10 & 0x3fffff;
|
|
*((U32 *)address + 1) = *((U32 *)address + 1) & ~0x3ff | item & 0x3ff;
|
|
break;
|
|
case reloc_sparcrel:
|
|
item = item - (uptr)address;
|
|
*(U32 *)address = *(U32 *)address & ~0x3fffffff | item >> 2 & 0x3fffffff;
|
|
break;
|
|
#endif /* SPARC */
|
|
default:
|
|
S_error1(who, "invalid relocation type ~s", FIX(typ));
|
|
}
|
|
}
|
|
|
|
/* used in S_gc() */
|
|
ptr S_get_code_obj(typ, p, n, o) IFASLCODE typ; iptr n, o; ptr p; {
|
|
void *address; uptr item;
|
|
|
|
address = (void *)((uptr)p + n);
|
|
switch (typ) {
|
|
case reloc_abs:
|
|
item = *(uptr *)address;
|
|
break;
|
|
#ifdef ARMV6
|
|
case reloc_arm32_abs:
|
|
item = arm32_get_abs(address);
|
|
break;
|
|
case reloc_arm32_jump:
|
|
case reloc_arm32_call:
|
|
item = arm32_get_jump(address);
|
|
break;
|
|
#endif /* ARMV6 */
|
|
#ifdef PPC32
|
|
case reloc_ppc32_abs:
|
|
item = ppc32_get_abs(address);
|
|
break;
|
|
case reloc_ppc32_jump:
|
|
case reloc_ppc32_call:
|
|
item = ppc32_get_jump(address);
|
|
break;
|
|
#endif /* PPC32 */
|
|
#ifdef I386
|
|
case reloc_rel:
|
|
item = *(uptr *)address;
|
|
item = item + ((uptr)address + sizeof(uptr));
|
|
break;
|
|
#endif /* I386 */
|
|
#ifdef X86_64
|
|
case reloc_x86_64_jump:
|
|
case reloc_x86_64_call:
|
|
item = x86_64_get_jump(address);
|
|
break;
|
|
#endif /* X86_64 */
|
|
#ifdef SPARC64
|
|
case reloc_sparc64abs:
|
|
item = sparc64_get_literal(address);
|
|
break;
|
|
case reloc_sparc64rel:
|
|
item = (*(U32 *)address & 0x3fffffff) << 2;
|
|
if (item & 0x80000000) /* sign bit set */
|
|
item = item | 0xffffffff00000000;
|
|
item = (uptr)address + (iptr)item;
|
|
break;
|
|
#endif /* SPARC64 */
|
|
#ifdef SPARC
|
|
case reloc_sparcabs:
|
|
item = (*(U32 *)address & 0x3fffff) << 10 | *((U32 *)address + 1) & 0x3ff;
|
|
break;
|
|
case reloc_sparcrel:
|
|
item = (*(U32 *)address & 0x3fffffff) << 2;
|
|
item += (uptr)address;
|
|
break;
|
|
#endif /* SPARC */
|
|
default:
|
|
S_error1("", "invalid relocation type ~s", FIX(typ));
|
|
return (ptr)0 /* not reached */;
|
|
}
|
|
return (ptr)(item - o);
|
|
}
|
|
|
|
|
|
#ifdef ARMV6
|
|
static void arm32_set_abs(void *address, uptr item) {
|
|
/* code generator produces ldrlit destreg, 0; brai 0; long 0 */
|
|
/* we change long 0 => long item */
|
|
*((U32 *)address + 2) = item;
|
|
}
|
|
|
|
static uptr arm32_get_abs(void *address) {
|
|
return *((U32 *)address + 2);
|
|
}
|
|
|
|
#define MAKE_B(n) (0xEA000000 | (n))
|
|
#define MAKE_BL(n) (0xEB000000 | (n))
|
|
#define B_OR_BL_DISP(x) ((x) & 0xFFFFFF)
|
|
#define MAKE_BX(reg) (0xE12FFF10 | (reg))
|
|
#define MAKE_BLX(reg) (0xE12FFF30 | (reg))
|
|
#define MAKE_LDRLIT(dst,n) (0xE59F0000 | ((dst) << 12) | (n))
|
|
#define LDRLITP(x) (((x) & 0xFFFF0000) == 0xE59F0000)
|
|
#define LDRLIT_DST(x) (((x) >> 12) & 0xf)
|
|
#define MAKE_MOV(dst,src) (0xE1A00000 | ((dst) << 12) | (src))
|
|
#define MOV_SRC(x) ((x) & 0xf)
|
|
/* nop instruction is not supported by all ARMv6 chips, so use recommended mov r0, r0 */
|
|
#define NOP MAKE_MOV(0,0)
|
|
|
|
static void arm32_set_jump(void *address, uptr item, IBOOL callp) {
|
|
/* code generator produces ldrlit %ip, 0; brai 0; long 0; bx or blx %ip */
|
|
U32 inst = *((U32 *)address + 0);
|
|
INT reg = LDRLITP(inst) ? LDRLIT_DST(inst) : MOV_SRC(*((U32 *)address + 1));
|
|
I32 worddisp = (U32 *)item - ((U32 *)address + 2);
|
|
if (worddisp >= -0x800000 && worddisp <= 0x7FFFFF) {
|
|
worddisp &= 0xFFFFFF;
|
|
*((U32 *)address + 0) = (callp ? MAKE_BL(worddisp) : MAKE_B(worddisp));
|
|
*((U32 *)address + 1) = MAKE_MOV(reg,reg); /* effective NOP recording tmp reg for later use */
|
|
*((U32 *)address + 2) = NOP;
|
|
*((U32 *)address + 3) = NOP;
|
|
} else {
|
|
*((U32 *)address + 0) = MAKE_LDRLIT(reg,0);
|
|
*((U32 *)address + 1) = MAKE_B(0);
|
|
*((U32 *)address + 2) = item;
|
|
*((U32 *)address + 3) = (callp ? MAKE_BLX(reg) : MAKE_BX(reg));
|
|
}
|
|
}
|
|
|
|
static uptr arm32_get_jump(void *address) {
|
|
U32 inst = *((U32 *)address + 0);
|
|
if (LDRLITP(inst)) {
|
|
return *((U32 *)address + 2);
|
|
} else {
|
|
I32 worddisp = B_OR_BL_DISP(inst);
|
|
if (worddisp >= 0x800000) worddisp -= 0x1000000;
|
|
return (uptr)(((U32 *)address + 2) + worddisp);
|
|
}
|
|
}
|
|
#endif /* ARMV6 */
|
|
|
|
#ifdef PPC32
|
|
|
|
#define UPDATE_ADDIS(item, instr) (((instr) & ~0xFFFF) | (((item) >> 16) & 0xFFFF))
|
|
#define UPDATE_ADDI(item, instr) (((instr) & ~0xFFFF) | ((item) & 0xFFFF))
|
|
|
|
#define MAKE_B(disp, callp) ((18 << 26) | (((disp) & 0xFFFFFF) << 2) | (callp))
|
|
#define MAKE_ADDIS(item) ((15 << 26) | (((item) >> 16) & 0xFFFF))
|
|
#define MAKE_ORI(item) ((24 << 26) | ((item) & 0xFFFF))
|
|
#define MAKE_NOP ((24 << 26))
|
|
#define MAKE_MTCTR ((31 << 26) | (9 << 16) | (467 << 1))
|
|
#define MAKE_BCTR(callp) ((19 << 26) | (20 << 21) | (528 << 1) | (callp))
|
|
|
|
static void ppc32_set_abs(void *address, uptr item) {
|
|
/* code generator produces addis destreg, %r0, 0 (hi) ; addi destreg, destreg, 0 (lo) */
|
|
/* we change 0 (hi) => upper 16 bits of address */
|
|
/* we change 0 (lo) => lower 16 bits of address */
|
|
/* low part is signed: if negative, increment high part */
|
|
item = item + (item << 1 & 0x10000);
|
|
*((U32 *)address + 0) = UPDATE_ADDIS(item, *((U32 *)address + 0));
|
|
*((U32 *)address + 1) = UPDATE_ADDI(item, *((U32 *)address + 1));
|
|
}
|
|
|
|
static uptr ppc32_get_abs(void *address) {
|
|
uptr item = ((*((U32 *)address + 0) & 0xFFFF) << 16) | (*((U32 *)address + 1) & 0xFFFF);
|
|
return item - (item << 1 & 0x10000);
|
|
}
|
|
|
|
static void ppc32_set_jump(void *address, uptr item, IBOOL callp) {
|
|
iptr disp = (iptr *)item - (iptr *)address;
|
|
if (-0x800000 <= disp && disp <= 0x7FFFFF) {
|
|
*((U32 *)address + 0) = MAKE_B(disp, callp);
|
|
*((U32 *)address + 1) = MAKE_NOP;
|
|
*((U32 *)address + 2) = MAKE_NOP;
|
|
*((U32 *)address + 3) = MAKE_NOP;
|
|
} else {
|
|
*((U32 *)address + 0) = MAKE_ADDIS(item);
|
|
*((U32 *)address + 1) = MAKE_ORI(item);
|
|
*((U32 *)address + 2) = MAKE_MTCTR;
|
|
*((U32 *)address + 3) = MAKE_BCTR(callp);
|
|
}
|
|
}
|
|
|
|
static uptr ppc32_get_jump(void *address) {
|
|
uptr item, instr = *(U32 *)address;
|
|
|
|
if ((instr >> 26) == 18) {
|
|
/* bl disp */
|
|
iptr disp = (instr >> 2) & 0xFFFFFF;
|
|
if (disp & 0x800000) disp -= 0x1000000;
|
|
item = (uptr)address + (disp << 2);
|
|
} else {
|
|
/* lis r0, high
|
|
ori r0, r0, low */
|
|
item = ((instr & 0xFFFF) << 16) | (*((U32 *)address + 1) & 0xFFFF);
|
|
}
|
|
|
|
return item;
|
|
}
|
|
#endif /* PPC32 */
|
|
|
|
#ifdef X86_64
|
|
static void x86_64_set_jump(void *address, uptr item, IBOOL callp) {
|
|
I64 disp = (I64)item - ((I64)address + 5); /* 5 = size of call instruction */
|
|
if ((I32)disp == disp) {
|
|
*(octet *)address = callp ? 0xE8 : 0xE9; /* call or jmp disp32 opcode */
|
|
*(I32 *)((uptr)address + 1) = (I32)disp;
|
|
*((octet *)address + 5) = 0x90; /* nop */
|
|
*((octet *)address + 6) = 0x90; /* nop */
|
|
*((octet *)address + 7) = 0x90; /* nop */
|
|
*((octet *)address + 8) = 0x90; /* nop */
|
|
*((octet *)address + 9) = 0x90; /* nop */
|
|
*((octet *)address + 10) = 0x90; /* nop */
|
|
*((octet *)address + 11) = 0x90; /* nop */
|
|
} else {
|
|
*(octet *)address = 0x48; /* REX w/REX.w set */
|
|
*((octet *)address + 1)= 0xB8; /* MOV imm64 to RAX */
|
|
*(uptr *)((uptr)address + 2) = item;
|
|
*((octet *)address + 10) = 0xFF; /* call/jmp reg/mem opcode */
|
|
*((octet *)address + 11) = callp ? 0xD0 : 0xE0; /* mod=11, ttt=010 (call) or 100 (jmp), r/m = 0 (RAX) */
|
|
}
|
|
}
|
|
|
|
static uptr x86_64_get_jump(void *address) {
|
|
if (*(octet *)address == 0x48) /* REX w/REX.w set */
|
|
/* must be long form: move followed by call/jmp */
|
|
return *(uptr *)((uptr)address + 2);
|
|
else
|
|
/* must be short form: call/jmp */
|
|
return ((uptr)address + 5) + *(I32 *)((uptr)address + 1);
|
|
}
|
|
#endif /* X86_64 */
|
|
|
|
#ifdef SPARC64
|
|
#define ASMREG0 1
|
|
/* TMPREG is asm-literal-tmp in sparc64macros.ss */
|
|
#define TMPREG 5
|
|
/* CRETREG is retreg in sparc64macros.ss */
|
|
#define CRETREG 15
|
|
/* SRETREG is ret in sparc64macros.ss */
|
|
#define SRETREG 26
|
|
|
|
#define OP_ADDI 0x80002000
|
|
#define OP_CALL 0x40000000
|
|
#define OP_JSR 0x81C00000
|
|
#define OP_OR 0x80100000
|
|
#define OP_ORI 0x80102000
|
|
#define OP_SETHI 0x1000000
|
|
/* SLLXI is the 64-bit version */
|
|
#define OP_SLLXI 0x81283000
|
|
#define OP_XORI 0x80182000
|
|
/* NOP is sethi %g0,0 */
|
|
#define NOP 0x1000000
|
|
#define IMMMASK (U32)0x1fff
|
|
#define IMMRANGE(x) ((U32)(x) + (U32)0x1000 <= IMMMASK)
|
|
#define ADDI(src,imm,dst) (OP_ADDI | (dst) << 25 | (src) << 14 | (imm) & IMMMASK)
|
|
#define JSR(src) (OP_JSR | CRETREG << 25 | (src) << 14)
|
|
#define ORI(src,imm,dst) (OP_ORI | (dst) << 25 | (src) << 14 | (imm) & IMMMASK)
|
|
#define SETHI(dst,high) (OP_SETHI | (dst) << 25 | (high) & 0x3fffff)
|
|
#define CALL(disp) (OP_CALL | (disp) >> 2 & 0x3fffffff)
|
|
|
|
|
|
static INT extract_reg_from_sethi(address) void *address; {
|
|
return *(U32 *)address >> 25;
|
|
}
|
|
|
|
static void emit_sethi_lo(U32 item, INT destreg, void *address) {
|
|
U32 high = item >> 10;
|
|
U32 low = item & 0x3ff;
|
|
|
|
/* sethi destreg, high */
|
|
*(U32 *)address = SETHI(destreg,high);
|
|
/* setlo destreg, low */
|
|
*((U32 *)address + 1) = ORI(destreg,low,destreg);
|
|
}
|
|
|
|
static uptr sparc64_get_literal(address) void *address; {
|
|
uptr item;
|
|
|
|
/* we may have "call disp" followed by delay instruction */
|
|
item = *(U32 *)address;
|
|
if (item >> 30 == OP_CALL >> 30) {
|
|
item = (item & 0x3fffffff) << 2;
|
|
if (item & 0x80000000) /* sign bit set */
|
|
item = item | 0xffffffff00000000;
|
|
item = (uptr)address + (iptr)item;
|
|
return item;
|
|
}
|
|
|
|
item = (item & 0x3fffff) << 10 | *((U32 *)address + 1) & 0x3ff;
|
|
if (*((U32 *)address + 2) != NOP) {
|
|
item = item << 32 |
|
|
(*((U32 *)address + 3) & 0x3fffff) << 10 |
|
|
*((U32 *)address + 4) & 0x3ff;
|
|
}
|
|
return item;
|
|
}
|
|
|
|
static U32 adjust_delay_inst(delay_inst, old_call_addr, new_call_addr)
|
|
U32 delay_inst; U32 *old_call_addr, *new_call_addr; {
|
|
INT offset;
|
|
|
|
offset = sizeof(U32) * (old_call_addr - new_call_addr);
|
|
if (offset == 0) return delay_inst;
|
|
|
|
if ((delay_inst & ~IMMMASK) == ADDI(CRETREG,0,SRETREG)) {
|
|
INT k = delay_inst & IMMMASK;
|
|
k = k - ((k << 1) & (IMMMASK+1));
|
|
offset = k + offset;
|
|
if (IMMRANGE(offset)) return ADDI(CRETREG,offset,SRETREG);
|
|
} else if ((delay_inst & ~IMMMASK) == ADDI(CRETREG,0,CRETREG)) {
|
|
INT k = delay_inst & IMMMASK;
|
|
k = k - ((k << 1) & (IMMMASK+1));
|
|
offset = k + offset;
|
|
if (offset == 0) return NOP;
|
|
if (IMMRANGE(offset)) return ADDI(CRETREG,offset,CRETREG);
|
|
} else if (IMMRANGE(offset))
|
|
return ADDI(CRETREG,offset,CRETREG);
|
|
|
|
return 0; /* fortunately, not a valid instruction here */
|
|
}
|
|
|
|
static void sparc64_set_call(address, call_addr, item) void *address; U32 *call_addr; uptr item; {
|
|
U32 delay_inst = *(call_addr + 1), new_delay_inst; iptr disp;
|
|
|
|
/* later: make item local if it refers to Scheme code, i.e., is in the
|
|
Scheme heap, by copying it and every other code object we can reach
|
|
into a single close area of memory. Or generate a close stub. */
|
|
disp = item - (uptr)address;
|
|
if (disp >= -0x20000000 && disp <= 0x1FFFFFFF &&
|
|
(new_delay_inst = adjust_delay_inst(delay_inst, call_addr,
|
|
(U32 *)address))) {
|
|
*(U32 *)address = CALL(disp);
|
|
*((U32 *)address + 1) = new_delay_inst;
|
|
} else {
|
|
INT n = sparc64_set_lit_only(address, item, ASMREG0);
|
|
new_delay_inst = adjust_delay_inst(delay_inst, call_addr, (U32 *)address + n);
|
|
*((U32 *)address + n) = JSR(ASMREG0);
|
|
*((U32 *)address + n + 1) = new_delay_inst;
|
|
}
|
|
}
|
|
|
|
static INT sparc64_set_lit_only(address, item, destreg) void *address; uptr item; I32 destreg; {
|
|
|
|
if ((iptr)item >= -0xffffffff && item <= 0xffffffff) {
|
|
uptr x, high, low;
|
|
|
|
if ((iptr)item < 0) {
|
|
x = 0x100000000 - item;
|
|
high = x >> 10;
|
|
low = x - (high << 10);
|
|
/* sethi destreg, ~high */
|
|
*(U32 *)address = OP_SETHI | destreg << 25 | ~high & 0x3fffff;
|
|
/* xor.i destreg, low|0x1c00, destreg */
|
|
*((U32 *)address + 1) = OP_XORI | destreg << 25 | destreg << 14 |
|
|
low | 0x1c00;
|
|
} else {
|
|
emit_sethi_lo(item, destreg, address);
|
|
}
|
|
*((U32 *)address + 2) = NOP;
|
|
*((U32 *)address + 3) = NOP;
|
|
*((U32 *)address + 4) = NOP;
|
|
*((U32 *)address + 5) = NOP;
|
|
return 2;
|
|
} else {
|
|
emit_sethi_lo(item >> 32, destreg, address);
|
|
/* sll destreg, 32, destreg */
|
|
*((U32 *)address + 2) = OP_SLLXI | destreg << 25 | destreg << 14 | 32;
|
|
emit_sethi_lo(item & 0xffffffff, TMPREG, (void *)((U32 *)address+3));
|
|
/* or destreg, tmpreg, destreg */
|
|
*((U32 *)address + 5) = OP_OR | destreg << 25 | destreg << 14 | TMPREG;
|
|
return 6;
|
|
}
|
|
}
|
|
|
|
static void sparc64_set_literal(address, item) void *address; uptr item; {
|
|
I32 destreg;
|
|
|
|
/* case 1: we have call followed by delay inst */
|
|
if (*(U32 *)address >> 30 == OP_CALL >> 30) {
|
|
sparc64_set_call(address, (U32 *)address, item);
|
|
return;
|
|
}
|
|
|
|
destreg = extract_reg_from_sethi(address);
|
|
|
|
/* case 2: we have two-instr load-literal followed by jsr and delay inst */
|
|
if (*((U32 *)address + 2) == JSR(destreg)) {
|
|
sparc64_set_call(address, (U32 *)address + 2, item);
|
|
return;
|
|
}
|
|
|
|
/* case 3: we have six-instr load-literal followed by jsr and a delay
|
|
instruction we're willing to try to deal with */
|
|
if (*((U32 *)address + 6) == JSR(destreg) &&
|
|
(*((U32 *)address + 7) & ~IMMMASK == ADDI(CRETREG,0,SRETREG) ||
|
|
*((U32 *)address + 7) == NOP)) {
|
|
sparc64_set_call(address, (U32 *)address + 6, item);
|
|
return;
|
|
}
|
|
|
|
/* case 4: we have a plain load-literal */
|
|
sparc64_set_lit_only(address, item, destreg);
|
|
}
|
|
#endif /* SPARC64 */
|