/* fns2.c Copyright (C) 1989-2007 Codemist Ltd */ /* * Basic functions part 2. */ /* * This code may be used and modified, and redistributed in binary * or source form, subject to the "CCL Public License", which should * accompany it. This license is a variant on the BSD license, and thus * permits use of code derived from this in either open and commercial * projects: but it does require that updates to this code be made * available back to the originators of the package. * Before merging other code in with this or linking this code * with other packages or libraries please check that the license terms * of the other material are compatible with those of this. */ /* Signature: 63a1c950 19-Jun-2007 */ #include "headers.h" #ifdef COMMON #include "clsyms.h" #endif #ifdef SOCKETS #include "sockhdr.h" #endif Lisp_Object getcodevector(int type, int32_t size) { /* * type is the code (e.g. TYPE_BPS) that gets packed, together with * the size, into a header word. * size is measured in bytes and must allow space for the header word. * This obtains space in the BPS area */ Lisp_Object nil = C_nil; #ifdef CHECK_FOR_CORRUPT_HEAP validate_all(); #endif for (;;) { int32_t alloc_size = (int32_t)doubleword_align_up(size); char *cf = (char *)codefringe, *cl = (char *)codelimit; int32_t free = cf - cl; char *r; if (alloc_size > free) { char msg[40]; sprintf(msg, "codevector %ld", (long)size); reclaim(nil, msg, GC_BPS, alloc_size); errexit(); continue; } r = cf - alloc_size; codefringe = (Lisp_Object)r; *((Header *)r) = type + (size << 10) + TAG_ODDS; /* * codelimit is always 8 bytes above the base of the code-page. The * address I need to return for a code-vector points (in a packed way) * to the first byte of actual byte data, ie CELL bytes above the start * of the data-structure. Oh joy! */ return TAG_BPS + (((int32_t)((r + CELL) - (cl - 8)) & (PAGE_POWER_OF_TWO-4)) << 6) + (((int32_t)(bps_pages_count-1))<<(PAGE_BITS+6)); /* Wow! Obscure!! */ } } Lisp_Object Lget_bps(Lisp_Object nil, Lisp_Object n) { int32_t n1; if (!is_fixnum(n) || (int32_t)n<0) return aerror1("get-bps", n); n1 = int_of_fixnum(n); n = getcodevector(TYPE_BPS, n1+CELL); errexit(); return onevalue(n); } /*/* * WARNING. This code is intended to allocate space into which I will post * executable code. Under a really old-fashioned "spirit of C" even though * grabbing space with malloc and re-using it this way was officially not * guaranteed one could typically expect to get away with it. Well apart * on Harvard-architecture machines where code and data addresses are quite * distinct. But more recently - I have become aware of it hitting wide * distribution in Fedora Core in late 2003 - security concerns have prompted * some major vendors to move to a model where malloc space is explicitly * locked down as non-executable. In such cases, which may become increasingly * common, it will be VITAL to adjust what lies behind this to perform * system calls to arrange that pages that will be used for native code are * tagged as executable. Maybe it is just as well that at present this is * an experimental extension to CSL and is not ready for general use anyway! */ Lisp_Object get_native_code_vector(int32_t size) { /* * Create some space for native code and return a handle that identifies * its start point. size is measured in bytes. */ Lisp_Object nil = C_nil; if (size <= 0) size = 8; for (;;) { int32_t alloc_size = (intptr_t)doubleword_align_up(size); intptr_t cf = native_fringe; intptr_t free = CSL_PAGE_SIZE - cf - 0x100; /* 256 bytes to be safe */ /* * When I start up a cold CSL I will have native_fringe set to zero and * native_pages_count also zero, indicating that there is none of this stuff * active. */ if (native_fringe == 0 || alloc_size > free) { char msg[40]; sprintf(msg, "native code %ld", (long)size); reclaim(nil, msg, GC_NATIVE, alloc_size); errexit(); continue; } free = (intptr_t)native_pages[native_pages_count-1]; free = doubleword_align_up(free); /* * I put the number of bytes in this block as the first word of the chunk * of memory, and arrange that there is a zero in what would be the first * word of unused space. Provided the user does not clobber bytes 0 to 4 * or the block this is enough to allow restart code to scan through all * native code segments. */ car32(free+native_fringe) = alloc_size; car32(free+native_fringe+alloc_size) = 0; native_fringe += alloc_size; native_pages_changed = 1; return Lcons(nil, fixnum_of_int(native_pages_count-1), fixnum_of_int(cf)); } } Lisp_Object Lget_native(Lisp_Object nil, Lisp_Object n) { int32_t n1; if (!is_fixnum(n) || (int32_t)n<0) return aerror1("get-native", n); n1 = int_of_fixnum(n); n = get_native_code_vector(n1); errexit(); return onevalue(n); } int do_not_kill_native_code = 0; void set_fns(Lisp_Object a, one_args *f1, two_args *f2, n_args *fn) { Lisp_Object nil = C_nil; Lisp_Object w1, w2, w3 = nil; /* * If I redefine a function for any reason (except to set trace options * on a bytecoded definition) I will discard any native-coded definitions * by splicing them out of the record. I provide a global variable to * defeat this behaviour (ugh). */ if (!do_not_kill_native_code) { for (w1 = native_code; w1!=nil; w1=qcdr(w1)) { w2 = qcar(w1); if (qcar(w2) == a) break; w3 = w1; } if (w1 != nil) { w1 = qcdr(w1); if (w3 == nil) native_code = w1; else qcdr(w3) = w1; } } if ((qheader(a) & (SYM_C_DEF | SYM_CODEPTR)) == (SYM_C_DEF | SYM_CODEPTR)) { if (symbol_protect_flag) { if (warn_about_protected_symbols) { trace_printf("+++ WARNING: protected function "); prin_to_trace(a); trace_printf(" not redefined\n"); } return; } else { if (warn_about_protected_symbols) { trace_printf("+++ WARNING: protected function "); prin_to_trace(a); trace_printf(" *has* been redefined\n"); } Lsymbol_protect(C_nil, a, C_nil); } } ifn1(a) = (intptr_t)f1; ifn2(a) = (intptr_t)f2; ifnn(a) = (intptr_t)fn; } #ifdef HIDE_USELESS_SYMBOL_ENVIRONMENTS static CSLbool interpreter_entry(Lisp_Object a) /* * If a function will be handled by the interpreter, including the case * of it being undefined, then the fn1() cell will tell me so. */ { return ( qfn1(a) == interpreted1 || qfn1(a) == traceinterpreted1 || qfn1(a) == double_interpreted1 || qfn1(a) == funarged1 || qfn1(a) == tracefunarged1 || qfn1(a) == double_funarged1 || qfn1(a) == undefined1); } #endif static char *show_fn1(one_args *p) { int i; for (i=0; entries_table1[i].s!=NULL; i++) if (entries_table1[i].p == p) return entries_table1[i].s; trace_printf("+++ Unknown function pointer = %p\n", p); return "unknown"; } static char *show_fn2(two_args *p) { int i; for (i=0; entries_table2[i].s!=NULL; i++) if (entries_table2[i].p == p) return entries_table2[i].s; trace_printf("+++ Unknown function pointer = %p\n", p); return "unknown"; } static char *show_fnn(n_args *p) { int i; for (i=0; entries_tablen[i].s!=NULL; i++) if (entries_tablen[i].p == p) return entries_tablen[i].s; trace_printf("+++ Unknown function pointer = %p\n", p); return "unknown"; } Lisp_Object Lsymbol_fn_cell(Lisp_Object nil, Lisp_Object a) /* * For debugging... */ { char *s1, *s2, *sn; if (!symbolp(a)) return onevalue(nil); s1 = show_fn1(qfn1(a)); s2 = show_fn2(qfn2(a)); sn = show_fnn(qfnn(a)); trace_printf("%s %s %s\n", s1, s2, sn); return onevalue(nil); } Lisp_Object Lsymbol_argcount(Lisp_Object nil, Lisp_Object a) /* * For debugging and JIT compiler use. Only valid if the function involved * is byte-coded. For simple functions taking a fixed number of args the * result is an integer. Otherwise it is a list of 3 items * (fewest-legal-args most-args-before-&rest flags) * where the flags has a 1 bit if missing &optional args are to be left * for the bytecoded stuff to unpick, otherwise they should be mapped to nil * somewhere. The 2 bit is present if a &rest argument is present. */ { one_args *f1; two_args *f2; n_args *fn; int low, high, hardrest; Lisp_Object r; unsigned char *b; if (!symbolp(a)) return onevalue(nil); f1 = qfn1(a); f2 = qfn2(a); fn = qfnn(a); r = qenv(a); if (!consp(r)) return onevalue(nil); r = qcar(r); if (!is_bps(r)) return onevalue(nil); b = (unsigned char *)data_of_bps(r); if (f1 == bytecoded1 || f1 == tracebytecoded1 || f1 == f1_as_0 || f1 == f1_as_1 || #ifdef JIT f1 == jitcompileme1 || #endif f1 == double_bytecoded1) return onevalue(fixnum_of_int(1)); if (f2 == bytecoded2 || f2 == tracebytecoded2 || f2 == f2_as_0 || f2 == f2_as_1 || f2 == f2_as_2 || #ifdef JIT f2 == jitcompileme2 || #endif f2 == double_bytecoded2) return onevalue(fixnum_of_int(2)); if (fn == bytecoded0 || fn == tracebytecoded0 || fn == f0_as_0 || #ifdef JIT fn == jitcompileme0 || #endif fn == double_bytecoded0) return onevalue(fixnum_of_int(0)); if (fn == bytecoded3 || fn == tracebytecoded3 || fn == f3_as_0 || fn == f3_as_1 || fn == f3_as_2 || fn == f3_as_3 || #ifdef JIT fn == jitcompileme3 || #endif fn == double_bytecoded3) return onevalue(fixnum_of_int(3)); if (fn == bytecodedn || fn == tracebytecodedn || #ifdef JIT fn == jitcompilemen || #endif fn == double_bytecodedn) return onevalue(fixnum_of_int(b[0])); low = b[0]; /* smallest number of valid args */ high = low + b[1]; /* largest number before &rest is accounted for */ hardrest = 0; /* * byteopt - optional arguments, with default of NIL */ if (f1 == byteopt1 || f1 == tracebyteopt1) hardrest = 0; /* * hardopt - optional arguments but default is passed as a SPID so that * the user can follow up and apply cleverer default processing */ else if (f1 == hardopt1 || f1 == tracehardopt1) hardrest = 1; /* * byteoptrest - anything with a &rest argument on the end. */ else if (f1 == byteoptrest1 || f1 == tracebyteoptrest1) hardrest = 1; /* * hardoptrest - some &optional args with non-nil default value, plus &rest */ else if (f1 == hardoptrest1 || f1 == tracehardoptrest1) hardrest = 3; else return onevalue(nil); r = list3(fixnum_of_int(low), fixnum_of_int(high), fixnum_of_int(hardrest)); errexit(); return onevalue(r); } Lisp_Object Lsymbol_argcode(Lisp_Object nil, Lisp_Object a) /* * This hands back a single integer that encodes what must be in the * three function cells for anything byte-coded. * Or nil if the argument did not name a bytecoded function. */ { #define BYTE_ARGMASK 0x007 /* 0, 1, 2, 3 or 4. 4 means "4 or more" */ #define BYTE_OPT 0x008 /* &optional present */ #define BYTE_HARDOPT 0x010 /* "hard" case of &optional */ #define BYTE_REST 0x020 /* &rest present */ #define BYTE_TRACED 0x040 #define BYTE_DOUBLED 0x080 #define BYTE_CALLAS 0x100 /* link to some other function */ /* * I can not see any way much better than a grim sequence of explicit * tests to achieve what I want here. */ one_args *f1; two_args *f2; n_args *fn; Lisp_Object r; int val; if (!symbolp(a)) return onevalue(nil); f1 = qfn1(a); f2 = qfn2(a); fn = qfnn(a); r = qenv(a); if (!consp(r)) return onevalue(nil); r = qcar(r); if (!is_bps(r)) return onevalue(nil); if (f1 == bytecoded1) val = 1; #ifdef JIT else if (f1 == jitcompileme1) val = 1; #endif else if (f1 == tracebytecoded1) val = 1 + BYTE_TRACED; else if (f1 == double_bytecoded1) val = 1 + BYTE_DOUBLED; else if (f2 == bytecoded2) val = 2; #ifdef JIT else if (f2 == jitcompileme2) val = 2; #endif else if (f2 == tracebytecoded2) val = 2 + BYTE_TRACED; else if (f2 == double_bytecoded2) val = 2 + BYTE_DOUBLED; else if (fn == bytecoded0) val = 0; #ifdef JIT else if (fn == jitcompileme0) val = 0; #endif else if (fn == tracebytecoded0) val = 0 + BYTE_TRACED; else if (fn == double_bytecoded0) val = 0 + BYTE_DOUBLED; else if (fn == bytecoded3) val = 3; #ifdef JIT else if (fn == jitcompileme3) val = 3; #endif else if (fn == tracebytecoded3) val = 3 + BYTE_TRACED; else if (fn == double_bytecoded3) val = 3 + BYTE_DOUBLED; else if (fn == bytecodedn) val = 4; #ifdef JIT else if (fn == jitcompilemen) val = 4; #endif else if (fn == tracebytecodedn) val = 4 + BYTE_TRACED; else if (fn == double_bytecodedn) val = 4 + BYTE_DOUBLED; /* * I observe that I do not support double-execute for &optional and &rest * functions. I do not mind that too much! */ else if (f1 == byteopt1) val = BYTE_OPT; else if (f1 == tracebyteopt1) val = BYTE_OPT + BYTE_TRACED; else if (f1 == hardopt1) val = BYTE_HARDOPT; else if (f1 == tracehardopt1) val = BYTE_HARDOPT + BYTE_TRACED; else if (f1 == byteoptrest1) val = BYTE_OPT + BYTE_REST; else if (f1 == tracebyteoptrest1) val = BYTE_OPT + BYTE_REST + BYTE_TRACED; else if (f1 == hardoptrest1) val = BYTE_HARDOPT + BYTE_REST; else if (f1 == tracehardoptrest1) val = BYTE_HARDOPT + BYTE_REST + BYTE_TRACED; else if (fn == f3_as_0) val = BYTE_CALLAS + 0; else if (fn == f3_as_1) val = BYTE_CALLAS + 1; else if (fn == f3_as_2) val = BYTE_CALLAS + 2; else if (fn == f3_as_3) val = BYTE_CALLAS + 3; else if (fn == f0_as_0) val = BYTE_CALLAS + 4; else if (f2 == f2_as_0) val = BYTE_CALLAS + 5; else if (f2 == f2_as_1) val = BYTE_CALLAS + 6; else if (f2 == f2_as_2) val = BYTE_CALLAS + 7; /* The next two intrude on the BYTE_OPT bit-position... */ else if (f1 == f1_as_0) val = BYTE_CALLAS + 8; else if (f1 == f1_as_1) val = BYTE_CALLAS + 9; else return onevalue(nil); return onevalue(fixnum_of_int(val)); } /* * The job of this function is to put back information as retrieved by * symbol-argcode. It is used in part of the support for native compilation * via C code... Note that it doe snot interact with any JIT stuff since the * two compilation strategies are complementary and I do not expect that I * will every use both at once. */ Lisp_Object Lsymbol_restore_fns(Lisp_Object nil, Lisp_Object name) { int r; Lisp_Object n, env; /* * This expects a property that has value <argcode> . <env> */ if (!is_symbol(name)) return onevalue(nil); n = get(name, bytecoded_symbol); if (!consp(n)) return onevalue(nil); env = qcdr(n); n = qcar(n); if (!is_fixnum(n)) return onevalue(nil); /* * When I first implemented this I thought I might remove the bytecode * definition from the property list. However I *hope* that soon I will be * re-instating the native defintion, which would lead to a need to put this * stuff back. So I think I will just leave it here and avoid the sweat * of re-instating it during system restart. This may lead to heap images * being a little larger but I propose not to fuss about that and further * I hope that structures will be shared and mean that any overhead is * small. * * Lremprop(nil, name, bytecoded_symbol); */ r = int_of_fixnum(n); switch (r) { case 1: set_fns(name, bytecoded1, too_many_1, wrong_no_1); break; case 1 + BYTE_TRACED: set_fns(name, tracebytecoded1, too_many_1, wrong_no_1); break; case 1 + BYTE_DOUBLED: set_fns(name, double_bytecoded1, too_many_1, wrong_no_1); break; case 2: set_fns(name, too_few_2, bytecoded2, wrong_no_2); break; case 2 + BYTE_TRACED: set_fns(name, too_few_2, tracebytecoded2, wrong_no_2); break; case 2 + BYTE_DOUBLED: set_fns(name, too_few_2, double_bytecoded2, wrong_no_2); break; case 0: set_fns(name, wrong_no_0a, wrong_no_0b, bytecoded0); break; case 0 + BYTE_TRACED: set_fns(name, wrong_no_0a, wrong_no_0b, tracebytecoded0); break; case 0 + BYTE_DOUBLED: set_fns(name, wrong_no_0a, wrong_no_0b, double_bytecoded0); break; case 3: set_fns(name, wrong_no_3a, wrong_no_3b, bytecoded3); break; case 3 + BYTE_TRACED: set_fns(name, wrong_no_3a, wrong_no_3b, tracebytecoded3); break; case 3 + BYTE_DOUBLED: set_fns(name, wrong_no_3a, wrong_no_3b, double_bytecoded3); break; case 4: set_fns(name, wrong_no_na, wrong_no_nb, bytecodedn); break; case 4 + BYTE_TRACED: set_fns(name, wrong_no_na, wrong_no_nb, tracebytecodedn); break; case 4 + BYTE_DOUBLED: set_fns(name, wrong_no_na, wrong_no_nb, double_bytecodedn); break; case BYTE_OPT: set_fns(name, byteopt1, byteopt2, byteoptn); break; case BYTE_OPT + BYTE_TRACED: set_fns(name, tracebyteopt1, tracebyteopt2, tracebyteoptn); break; case BYTE_HARDOPT: set_fns(name, hardopt1, hardopt2, hardoptn); break; case BYTE_HARDOPT + BYTE_TRACED: set_fns(name, tracehardopt1, tracehardopt2, tracehardoptn); break; case BYTE_OPT + BYTE_REST: set_fns(name, byteoptrest1, byteoptrest2, byteoptrestn); break; case BYTE_OPT + BYTE_REST + BYTE_TRACED: set_fns(name, tracebyteoptrest1, tracebyteoptrest2, tracebyteoptrestn); break; case BYTE_HARDOPT + BYTE_REST: set_fns(name, hardoptrest1, hardoptrest2, hardoptrestn); break; case BYTE_HARDOPT + BYTE_REST + BYTE_TRACED: set_fns(name, tracehardoptrest1, tracehardoptrest2, tracehardoptrestn); break; case BYTE_CALLAS + 0: set_fns(name, wrong_no_na, wrong_no_nb, f3_as_0); break; case BYTE_CALLAS + 1: set_fns(name, wrong_no_na, wrong_no_nb, f3_as_1); break; case BYTE_CALLAS + 2: set_fns(name, wrong_no_na, wrong_no_nb, f3_as_2); break; case BYTE_CALLAS + 3: set_fns(name, wrong_no_na, wrong_no_nb, f3_as_3); break; case BYTE_CALLAS + 4: set_fns(name, wrong_no_na, wrong_no_nb, f0_as_0); break; case BYTE_CALLAS + 5: set_fns(name, too_few_2, f2_as_0, wrong_no_2); break; case BYTE_CALLAS + 6: set_fns(name, too_few_2, f2_as_1, wrong_no_2); break; case BYTE_CALLAS + 7: set_fns(name, too_few_2, f2_as_2, wrong_no_2); break; case BYTE_CALLAS + 8: set_fns(name, f1_as_0, too_many_1, wrong_no_1); break; case BYTE_CALLAS + 9: set_fns(name, f1_as_1, too_many_1, wrong_no_1); break; default: return onevalue(nil); } qenv(name) = env; return onevalue(lisp_true); } Lisp_Object Lsymbol_env(Lisp_Object nil, Lisp_Object a) /* * Not Common Lisp - read the 'environment' cell associated with a * symbol. This cell is deemed empty unless the symbol-function is * compiled code. For use mainly for debugging. */ { if (!symbolp(a)) return onevalue(nil); #ifdef HIDE_USELESS_SYMBOL_ENVIRONMENTS else if ((qheader(a) & (SYM_SPECIAL_FORM | SYM_MACRO)) != 0 || interpreter_entry(a)) return onevalue(nil); #endif return onevalue(qenv(a)); } Lisp_Object Lsymbol_set_env(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { CSL_IGNORE(nil); if (!is_symbol(a)) return aerror1("symbol-set-env", a); if ((qheader(a) & (SYM_C_DEF | SYM_CODEPTR)) == (SYM_C_DEF | SYM_CODEPTR)) return onevalue(nil); qenv(a) = b; return onevalue(b); } Lisp_Object Lsymbol_fastgets(Lisp_Object nil, Lisp_Object a) { if (!symbolp(a)) return onevalue(nil); return onevalue(qfastgets(a)); } /* * (protect 'name t) arranges that the function indicated (which is * expected to have been defined in the C kernel) can not be redefined. * (protect 'name nil) restores the usual state of affairs. */ Lisp_Object Lsymbol_protect(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { Header h; if (!is_symbol(a)) return onevalue(nil); h = qheader(a); if (b == nil) qheader(a) = h & ~(SYM_CODEPTR | SYM_C_DEF); else qheader(a) = h | SYM_CODEPTR | SYM_C_DEF; h &= (SYM_CODEPTR | SYM_C_DEF); return onevalue(Lispify_predicate(h == (SYM_CODEPTR | SYM_C_DEF))); } /* * (symbol-make-fastget 'xxx nil) returns current information, nil if no * fastget usage set. * (symbol-make-fastget 'xxx n) sets it to n (0 <= n < 63) * (symbol-make-fastget 'xxx -1) sets the option off * (symbol-make-fastget n) specify fast-get range (n <= 63) */ Lisp_Object Lsymbol_make_fastget1(Lisp_Object nil, Lisp_Object a) { int32_t n, n1 = fastget_size; CSL_IGNORE(nil); if (!is_fixnum(a) || (n = int_of_fixnum(a)) < 0 || (n > MAX_FASTGET_SIZE)) return aerror1("symbol-make-fastget", a); term_printf("+++ Fastget size was %d, now %d\n", n1, n); fastget_size = n; return onevalue(fixnum_of_int(n1)); } Lisp_Object Lsymbol_make_fastget(Lisp_Object nil, Lisp_Object a, Lisp_Object n) { int32_t n1, p, q; Header h; if (!symbolp(a)) return onevalue(nil); h = qheader(a); p = header_fastget(h); if (is_fixnum(n)) { n1 = int_of_fixnum(n); if (n1 < -1 || n1 >= fastget_size) return aerror1("symbol-make-fastget", n); trace_printf("+++ Use fastget slot %d for ", n1); loop_print_trace(a); errexit(); trace_printf("\n"); if (p != 0) elt(fastget_names, p-1) = SPID_NOPROP; q = (n1 + 1) & 0x3f; h = (h & ~SYM_FASTGET_MASK) | (q << SYM_FASTGET_SHIFT); qheader(a) = h; if (q != 0) elt(fastget_names, q-1) = a; } if (p == 0) return onevalue(nil); else return onevalue(fixnum_of_int(p - 1)); } static Lisp_Object deleqip(Lisp_Object a, Lisp_Object l) /* * This deletes the item a (tested for using EQ) from the list l, * assuming that the list is nil-terminated and that the item a * occurs at most once. It overwrites the list l in the process. */ { Lisp_Object nil = C_nil, w, r; if (l == nil) return nil; if (qcar(l) == a) return qcdr(l); r = l; while (w = l, (l = qcdr(l)) != nil) { if (qcar(l) == a) { qcdr(w) = qcdr(l); return r; } } return r; } void lose_C_def(Lisp_Object a) { /* * None of the code here can cause garbage collection. */ #ifdef COMMON Lisp_Object nil = C_nil; Lisp_Object b = get(a, unset_var, nil), c; #else nil_as_base Lisp_Object b = get(a, unset_var), c; #endif Lremprop(C_nil, a, unset_var); qheader(a) &= ~SYM_C_DEF; #ifdef COMMON c = get(b, work_symbol, nil); #else c = get(b, work_symbol); #endif c = deleqip(a, c); if (c == C_nil) Lremprop(C_nil, b, work_symbol); else putprop(b, work_symbol, c); } /* * (symbol-set-native fn args bpsbase offset env) * where bpsbase is as handed back by (make-native nnn) and offset is * the offset in this block to enter at. * If args has the actual arg count in its bottom byte. Usually the * rest of it will be zero, and then one function cell is set to point to the * given entrypoint and the other two are set to point at error handlers. * If any bits in args beyond that are set then this call only changes the * directly specified function cell, and the others are left in whatever state * they were. If several of the fuction cells are to be filled in (eg to cope * with &optional or &rest arguments) then a simple call with args<256 must * be made first, followed by the calls (args>=256) that fill in the other * two cells. * The first time that symbol-set-native is called on a function that * function MUST have a byte coded definition, and this definition is * picked up and stored away, so that if (preserve) is called the bytecoded * definition will be available for use on systems with different * architectures. To make things tolerably consistent with that any operation * that installs a new bytecoded (or for that matter other) definition * will clear away any native-compiled versions of the function. * * The native code that is installed will be expected to have relocation * records starting at the start of bpsbase, and these will be activated, * filling in references from the bps to other executable parts of Lisp. * Passing bad arguments to this function provide a quick and easy way to * cayse UTTER havoc. Therefore I disable its use in server applications. */ Lisp_Object MS_CDECL Lsymbol_set_native(Lisp_Object nil, int nargs, ...) { va_list a; Lisp_Object fn, args, bpsbase, offset, env, w1, w2, w3; int32_t pagenumber, t_p, arginfo; intptr_t address, page, bps; #ifdef SOCKETS /* * Security measure - deny symbol-set-native to remote users */ if (socket_server != 0) return aerror("symbol-set-native"); #endif argcheck(nargs, 5, "symbol-set-native"); va_start(a, nargs); fn = va_arg(a, Lisp_Object); args = va_arg(a, Lisp_Object); bpsbase = va_arg(a, Lisp_Object); offset = va_arg(a, Lisp_Object); env = va_arg(a, Lisp_Object); va_end(a); if (!is_symbol(fn) || (qheader(fn) & (SYM_SPECIAL_FORM | SYM_CODEPTR)) != 0) return aerror1("symbol-set-native", fn); if (!is_fixnum(args)) return aerror1("symbol-set-native", args); if (!consp(bpsbase) || !is_fixnum(qcar(bpsbase)) || !is_fixnum(qcdr(bpsbase))) return aerror1("symbol-set-native", bpsbase); if (!is_fixnum(offset)) return aerror1("symbol-set-native", offset); nargs = int_of_fixnum(args); pagenumber = int_of_fixnum(qcar(bpsbase)); if (pagenumber<0 || pagenumber>=native_pages_count) return aerror1("symbol-set-native", bpsbase); bps = int_of_fixnum(qcdr(bpsbase)); address = bps+int_of_fixnum(offset); if (address<8 || address>=CSL_PAGE_SIZE) return aerror1("symbol-set-native", offset); page = (intptr_t)native_pages[pagenumber]; page = doubleword_align_up(page); bps = page + bps; relocate_native_function((unsigned char *)bps); /* * Here I need to push the info I have just collected onto * the native_code list since otherwise things will not be re-loaded in * from a checkpoint image. Also if the function is at present byte-coded * I need to record that info about it in native_code. */ w1 = native_code; w2 = nil; while (w1!=nil) { w2 = qcar(w1); if (qcar(w2) == fn) break; w1 = qcdr(w1); } if (w1 == nil) { /* * Here the function has not been seen as native code ever before, so it has * not been entered into the list. Do something about that... */ push2(env, fn); args = Lsymbol_argcount(nil, fn); errexitn(2); if (args == nil) return aerror1("No bytecode definition found for", fn); /* * Now I have to reverse the information that symbol_argcount gave me * to get the single numeric code as wanted by symbol_set_definition. * Oh what a mess. */ if (is_fixnum(args)) arginfo = int_of_fixnum(args); else { arginfo = int_of_fixnum(qcar(args)); args = qcdr(args); arginfo |= ((int_of_fixnum(qcar(args)) - arginfo) << 8); args = qcdr(args); arginfo |= int_of_fixnum(qcar(args)) << 16; } fn = stack[0]; w2 = list2(fn, fixnum_of_int(arginfo)); errexitn(2); w2 = cons(w2, native_code); errexitn(2); native_code = w2; w2 = qcar(w2); pop2(fn, env); } w2 = qcdr(w2); /* {nargs,(type . offset . env),...} */ /* * If I was defining this function in the simple way I should clear any * previous version (for this machine architecture) from the record. * Just at present this does not release the memory, but at some stage * in the future I may arrange to compact away old code when I do a * preserve operation (say). */ if (nargs <= 0xff) { w1 = w3 = w2; for (w1=qcdr(w2); w1!=nil; w1=qcdr(w1)) { w3 = qcar(w1); if (qcar(w3) == fixnum_of_int(native_code_tag)) break; w3 = w1; } if (w1 != nil) qcdr(w3) = qcdr(w1); } /* * w2 is still the entry for this function in the native code list. It * needs to have an entry of type 0 (ie for bytecoded) and so the next * thing to do is to check that such an entry exists and if not to create * it. */ w1 = w2; while ((w1 = qcdr(w1)) != nil) { w3 = qcar(w1); if (qcar(w3) == fixnum_of_int(0)) break; w1 = qcdr(w1); } if (w1 == nil) { /* * This is where there was no bytecode entry on the native code list * for this function, so I had better create one for it. Note that only * one such entry will ever be stored so it does not matter much where on * the list it goes. I suspect that the list ought always to be empty * in this case anyway. */ push3(fn, env, w2); w1 = list2star(fixnum_of_int(0), fixnum_of_int(0), qenv(fn)); errexitn(3); w2 = stack[0]; w1 = cons(w1, qcdr(w2)); errexitn(3); pop3(w2, env, fn); qcdr(w2) = w1; } /* * Now the list of native code associated with this function certainly holds * a byte-coded definition (and for sanity that had better be consistent * with the native code I am installing now, but that is not something * that can be checked at this level). Put in an entry referring to the * current gubbins. */ push3(w2, fn, env); /* * now I pack the code type, arg category and offset into the * single fixnum that that information has to end up in. */ t_p = (native_code_tag << 20); if ((nargs & 0xffffff00) != 0) { switch (nargs & 0xff) { case 1: t_p |= (1<<18); break; case 2: t_p |= (2<<18); break; default:t_p |= (3<<18); break; } } t_p |= (pagenumber & 0x3ffff); w1 = list2star(fixnum_of_int(t_p), fixnum_of_int(address), env); errexitn(3); w1 = ncons(w1); pop3(env, fn, w2); errexit(); while ((w3 = qcdr(w2)) != nil) w2 = w3; /* Tag onto the END */ qcdr(w2) = w1; qheader(fn) &= ~SYM_TRACED; address = page + address; /* * The code here must do just about the equivalent to that in restart.c */ switch (nargs & 0xff) { case 0: ifnn(fn) = address; if (nargs<=0xff) ifn1(fn) = (intptr_t)wrong_no_0a, ifn2(fn) = (intptr_t)wrong_no_0b; break; case 1: ifn1(fn) = address; if (nargs<=0xff) ifn2(fn) = (intptr_t)too_many_1, ifnn(fn) = (intptr_t)wrong_no_1; break; case 2: ifn2(fn) = address; if (nargs<=0xff) ifn1(fn) = (intptr_t)too_few_2, ifnn(fn) = (intptr_t)wrong_no_2; break; case 3: ifnn(fn) = address; if (nargs<=0xff) ifn1(fn) = (intptr_t)wrong_no_3a, ifn2(fn) = (intptr_t)wrong_no_3b; break; default: ifnn(fn) = address; if (nargs<=0xff) ifn1(fn) = (intptr_t)wrong_no_na, ifn2(fn) = (intptr_t)wrong_no_nb; break; } qenv(fn) = env; return onevalue(fn); } static CSLbool restore_fn_cell(Lisp_Object a, char *name, int32_t len, setup_type const s[]) { int i; for (i=0; s[i].name != NULL; i++) { if (strlen(s[i].name) == (size_t)len && memcmp(name, s[i].name, len) == 0) break; } if (s[i].name == NULL) return NO; set_fns(a, s[i].one, s[i].two, s[i].n); return YES; } static Lisp_Object Lrestore_c_code(Lisp_Object nil, Lisp_Object a) { char *name; int32_t len; Lisp_Object pn; if (!symbolp(a)) return aerror1("restore-c-code", a); push(a); pn = get_pname(a); pop(a); errexit(); name = (char *)&celt(pn, 0); len = length_of_header(vechdr(pn)) - 4; if (restore_fn_cell(a, name, len, u01_setup) || restore_fn_cell(a, name, len, u02_setup) || restore_fn_cell(a, name, len, u03_setup) || restore_fn_cell(a, name, len, u04_setup) || restore_fn_cell(a, name, len, u05_setup) || restore_fn_cell(a, name, len, u06_setup) || restore_fn_cell(a, name, len, u07_setup) || restore_fn_cell(a, name, len, u08_setup) || restore_fn_cell(a, name, len, u09_setup) || restore_fn_cell(a, name, len, u10_setup) || restore_fn_cell(a, name, len, u11_setup) || restore_fn_cell(a, name, len, u12_setup)) { Lisp_Object env; push(a); #ifdef COMMON env = get(a, funarg, nil); #else env = get(a, funarg); #endif pop(a); errexit(); qenv(a) = env; return onevalue(a); } else return onevalue(nil); } Lisp_Object Lsymbol_set_definition(Lisp_Object nil, Lisp_Object a, Lisp_Object b) /* * The odd case here is where the second argument represents a freshly * created bit of compiled code. In which case the structure is * (nargs . codevec . envvec) * where nargs is an integer indicating the number of arguments, codevec * is a vector of bytecodes, and envvec is something to go in the * environment cell of the symbol. * Here the low 8 bits of nargs indicate the number of required arguments. * The next 8 bits give the number of optional arguments, and the next * two bits are flags. Of these, the first is set if any of the optional * arguments has an initform or supplied-p associate, and the other * indicates that a "&rest" argument is required. * Bits beyond that (if non-zero) indicate that the function definition * is of the form (defun f1 (a b c) (f2 a b)) and the number coded is the * length of the function body. * Standard Lisp does not need &optional or &rest arguments, but it turned * out to be pretty easy to make the bytecode compiler support them. */ { if (!is_symbol(a) || /* * Something flagged with the CODEPTR bit is a gensym manufactured to * stand for a compiled-code object. It should NOT be reset! */ (qheader(a) & (SYM_SPECIAL_FORM | SYM_CODEPTR)) != 0) { if (qheader(a) & SYM_C_DEF) return onevalue(nil); return aerror1("symbol-set-definition", a); } qheader(a) &= ~SYM_TRACED; set_fns(a, undefined1, undefined2, undefinedn); /* Tidy up first */ qenv(a) = a; if ((qheader(a) & SYM_C_DEF) != 0) lose_C_def(a); if (b == nil) return onevalue(b); /* set defn to nil to undefine */ else if (symbolp(b)) { /* * One could imagine a view that the second arg to symbol-set-definition * had to be a codepointer object. I will be kind (?) and permit the NAME * of a function too. However for the second arg to be a macro or a * special form would still be a calamity. * if ((qheader(b) & SYM_CODEPTR) == 0) * return aerror1("symbol-set-definition", b); */ if ((qheader(b) & (SYM_SPECIAL_FORM | SYM_MACRO)) != 0) return aerror1("symbol-set-definition", b); qheader(a) = qheader(a) & ~SYM_MACRO; { set_fns(a, qfn1(b), qfn2(b), qfnn(b)); qenv(a) = qenv(b); /* * In order that checkpoint files can be made there is some very * ugly fooling around here for functions that are defined in the C coded * kernel. Sorry. */ if ((qheader(b) & SYM_C_DEF) != 0) { #ifdef COMMON Lisp_Object c = get(b, unset_var, nil); #else Lisp_Object c = get(b, unset_var); #endif if (c == nil) c = b; push2(c, a); putprop(a, unset_var, c); errexitn(2); pop(a); #ifdef COMMON a = cons(a, get(stack[0], work_symbol, nil)); #else a = cons(a, get(stack[0], work_symbol)); #endif errexitn(1); putprop(stack[0], work_symbol, a); pop(b); errexit(); } } } else if (!consp(b)) return aerror1("symbol-set-definition", b); else if (is_fixnum(qcar(b))) { int32_t nargs = (int)int_of_fixnum(qcar(b)), nopts, flagbits, ntail; nopts = nargs >> 8; flagbits = nopts >> 8; ntail = flagbits >> 2; nargs &= 0xff; nopts &= 0xff; flagbits &= 3; if (ntail != 0) { switch (100*nargs + ntail-1) { case 300: set_fns(a, wrong_no_na, wrong_no_nb, f3_as_0); break; case 301: set_fns(a, wrong_no_na, wrong_no_nb, f3_as_1); break; case 302: set_fns(a, wrong_no_na, wrong_no_nb, f3_as_2); break; case 303: set_fns(a, wrong_no_na, wrong_no_nb, f3_as_3); break; case 200: set_fns(a, too_few_2, f2_as_0, wrong_no_2); break; case 201: set_fns(a, too_few_2, f2_as_1, wrong_no_2); break; case 202: set_fns(a, too_few_2, f2_as_2, wrong_no_2); break; case 100: set_fns(a, f1_as_0, too_many_1, wrong_no_1); break; case 101: set_fns(a, f1_as_1, too_many_1, wrong_no_1); break; case 000: set_fns(a, wrong_no_na, wrong_no_nb, f0_as_0); break; } b = qcdr(b); } else if (flagbits != 0 || nopts != 0) { if ((qheader(a) & SYM_TRACED) == 0) switch(flagbits) { default: case 0: /* easy case optional arguments */ set_fns(a, byteopt1, byteopt2, byteoptn); break; case 1: /* optional args, but non-nil default, or supplied-p extra */ set_fns(a, hardopt1, hardopt2, hardoptn); break; case 2: /* easy opt args, but also a &rest arg */ set_fns(a, byteoptrest1, byteoptrest2, byteoptrestn); break; case 3: /* complicated &options and &rest */ set_fns(a, hardoptrest1, hardoptrest2, hardoptrestn); break; } else switch (flagbits) { default: case 0: /* easy case optional arguments */ set_fns(a, tracebyteopt1, tracebyteopt2, tracebyteoptn); break; case 1: /* optional args, but non-nil default, or supplied-p extra */ set_fns(a, tracehardopt1, tracehardopt2, tracehardoptn); break; case 2: /* easy opt args, but also a &rest arg */ set_fns(a, tracebyteoptrest1, tracebyteoptrest2, tracebyteoptrestn); break; case 3: /* complicated &options and &rest */ set_fns(a, tracehardoptrest1, tracehardoptrest2, tracehardoptrestn); break; } } else { if (nargs > 4) nargs = 4; if ((qheader(a) & SYM_TRACED) != 0) nargs += 5; qheader(a) = qheader(a) & ~SYM_MACRO; switch (nargs) { case 0: set_fns(a, wrong_no_0a, wrong_no_0b, bytecoded0); break; case 1: set_fns(a, bytecoded1, too_many_1, wrong_no_1); break; case 2: set_fns(a, too_few_2, bytecoded2, wrong_no_2); break; case 3: set_fns(a, wrong_no_3a, wrong_no_3b, bytecoded3); break; default: case 4: set_fns(a, wrong_no_na, wrong_no_nb, bytecodedn); break; case 5+0: set_fns(a, wrong_no_0a, wrong_no_0b, tracebytecoded0); break; case 5+1: set_fns(a, tracebytecoded1, too_many_1, wrong_no_1); break; case 5+2: set_fns(a, too_few_2, tracebytecoded2, wrong_no_2); break; case 5+3: set_fns(a, wrong_no_3a, wrong_no_3b, tracebytecoded3); break; case 5+4: set_fns(a, wrong_no_na, wrong_no_nb, tracebytecodedn); break; } } qenv(a) = qcdr(b); } else if (qcar(b) == lambda) { Lisp_Object bvl = qcar(qcdr(b)); int nargs = 0; while (consp(bvl)) nargs++, bvl = qcdr(bvl); qheader(a) = qheader(a) & ~SYM_MACRO; if ((qheader(a) & SYM_TRACED) != 0) set_fns(a, traceinterpreted1, traceinterpreted2, traceinterpretedn); else set_fns(a, interpreted1, interpreted2, interpretedn); qenv(a) = qcdr(b); if (qvalue(comp_symbol) != nil && qfn1(compiler_symbol) != undefined1) { push(a); a = ncons(a); errexitn(1); (qfn1(compiler_symbol))(qenv(compiler_symbol), a); pop(a); errexit(); } } else if (qcar(b) == funarg) { Lisp_Object bvl = qcar(qcdr(b)); int nargs = 0; while (consp(bvl)) nargs++, bvl = qcdr(bvl); qheader(a) = qheader(a) & ~SYM_MACRO; if ((qheader(a) & SYM_TRACED) != 0) set_fns(a, tracefunarged1, tracefunarged2, tracefunargedn); else set_fns(a, funarged1, funarged2, funargedn); qenv(a) = qcdr(b); } else return aerror1("symbol-set-definition", b); return onevalue(b); } Lisp_Object Lgetd(Lisp_Object nil, Lisp_Object a) { Header h; Lisp_Object type; CSL_IGNORE(nil); if (a == nil) return onevalue(nil); else if (!is_symbol(a)) return onevalue(nil); h = qheader(a); if ((h & SYM_SPECIAL_FORM) != 0) type = fexpr_symbol; else if ((h & SYM_MACRO) != 0) { a = cons(lambda, qenv(a)); errexit(); type = macro_symbol; } else { a = Lsymbol_function(nil, a); errexit(); if (a == nil) return onevalue(nil); type = expr_symbol; } a = cons(type, a); errexit(); return onevalue(a); } Lisp_Object Lremd(Lisp_Object nil, Lisp_Object a) { Lisp_Object res; CSL_IGNORE(nil); if (!is_symbol(a) || (qheader(a) & SYM_SPECIAL_FORM) != 0) return aerror1("remd", a); if ((qheader(a) & (SYM_C_DEF | SYM_CODEPTR)) == (SYM_C_DEF | SYM_CODEPTR)) return onevalue(nil); res = Lgetd(nil, a); errexit(); if (res == nil) return onevalue(nil); /* no definition to remove */ /* * I treat an explicit use of remd as a redefinition, and ensure that * restarting a preserved image will not put the definition back. */ qheader(a) = qheader(a) & ~SYM_MACRO; if ((qheader(a) & SYM_C_DEF) != 0) lose_C_def(a); set_fns(a, undefined1, undefined2, undefinedn); qenv(a) = a; return onevalue(res); } /* * For set-autoload the first argument must be a symbol that will name * a function, the second arg is either an atom or a list of atoms, each * of which specified a module to be loaded if the names function is * called. Loading the modules is expected to instate a definition for the * function involved. This function is arranged so it does NOT do anything * if the function being set for autoloading is already defined. This is * on the supposition that the existing definition is in fact the desired * one, say because the relevant module happens to have been loaded already. * An explicit use of remd first can be used to ensure that no previous * definition is present and thus that a real autoload stub will be instated, * if that is what you really want. */ Lisp_Object Lset_autoload(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { Lisp_Object res; CSL_IGNORE(nil); if (!is_symbol(a) || (qheader(a) & SYM_SPECIAL_FORM) != 0) return aerror1("set-autoload", a); if (!(qfn1(a) == undefined1 && qfn2(a) == undefined2 && qfnn(a) == undefinedn)) return onevalue(nil); if ((qheader(a) & (SYM_C_DEF | SYM_CODEPTR)) == (SYM_C_DEF | SYM_CODEPTR)) return onevalue(nil); push2(a, b); if (consp(b)) res = cons(a, b); else res = list2(a, b); pop2(b, a); errexit(); /* * I treat an explicit use of set-autoload as a redefinition, and ensure that * restarting a preserved image will not put the definition back. Note that * I will not allow autoloadable macros... */ qheader(a) = qheader(a) & ~SYM_MACRO; if ((qheader(a) & SYM_C_DEF) != 0) lose_C_def(a); set_fns(a, autoload1, autoload2, autoloadn); qenv(a) = res; return onevalue(res); } #define pack_funtable(a, n) ((((int32_t)(a)) << 16) | (n)) #define funtable_nargs(u) ((u) >> 16) #define funtable_index(u) ((u) & 0xffffU) static one_args *displaced1 = NULL; static two_args *displaced2; static n_args *displacedn; static uint32_t table_entry; static Lisp_Object traced1_function(Lisp_Object env, Lisp_Object a) { Lisp_Object name, nil = C_nil; Lisp_Object r = nil; /* * Worry about errors & garbage collection in following calls to print fns * This MUST be fixed sometime fairly soon... but then it could only bite * people using the trace facility, and their code is already dead! */ freshline_trace(); loop_print_trace(tracedfn); push(tracedfn); trace_printf(" called (1 arg)\narg1: "); loop_print_trace(a); trace_printf("\n"); r = (*displaced1)(env, a); pop(name); errexit(); push(r); freshline_trace(); loop_print_trace(name); trace_printf(" = "); loop_print_trace(r); trace_printf("\n"); pop(r); return onevalue(r); } static Lisp_Object traced2_function(Lisp_Object env, Lisp_Object a, Lisp_Object b) { Lisp_Object name, nil = C_nil; Lisp_Object r = nil; freshline_trace(); loop_print_trace(tracedfn); push(tracedfn); trace_printf(" called (2 args)\narg1:"); loop_print_trace(a); trace_printf("\narg2: "); loop_print_trace(b); trace_printf("\n"); r = (*displaced2)(env, a, b); pop(name); errexit(); push(r); freshline_trace(); loop_print_trace(name); trace_printf(" = "); loop_print_trace(r); trace_printf("\n"); pop(r); return onevalue(r); } static Lisp_Object MS_CDECL tracedn_function(Lisp_Object env, int nargs, ...) { Lisp_Object name, nil = C_nil; Lisp_Object r = nil; int i; va_list a; push(tracedfn); va_start(a, nargs); push_args(a, nargs); freshline_trace(); loop_print_trace(tracedfn); trace_printf(" called (%d args)\n", nargs); for (i=1; i<=nargs; i++) { trace_printf("arg%d: ", i); loop_print_trace(stack[i-nargs]); trace_printf("\n"); } if (nargs <= 15) switch (nargs) { default: /* * Calls with 1 or 2 args can never arise, since those cases have been * split off for separate treatment. */ popv(nargs+1); return aerror("system error in trace mechanism"); case 0: r = (*displacedn)(env, 0); break; case 3: r = (*displacedn)(env, 3, stack[-2], stack[-1], stack[0]); break; case 4: r = (*displacedn)(env, 4, stack[-3], stack[-2], stack[-1], stack[0]); break; case 5: r = (*displacedn)(env, 5, stack[-4], stack[-3], stack[-2], stack[-1], stack[0]); break; case 6: r = (*displacedn)(env, 6, stack[-5], stack[-4], stack[-3], stack[-2], stack[-1], stack[0]); break; case 7: r = (*displacedn)(env, 7, stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1], stack[0]); break; case 8: r = (*displacedn)(env, 8, stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1], stack[0]); break; case 9: r = (*displacedn)(env, 9, stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1], stack[0]); break; case 10: r = (*displacedn)(env, 10, stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1], stack[0]); break; case 11: r = (*displacedn)(env, 11, stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1], stack[0]); break; case 12: r = (*displacedn)(env, 12, stack[-11], stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1], stack[0]); break; case 13: r = (*displacedn)(env, 13, stack[-12], stack[-11], stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1], stack[0]); break; case 14: r = (*displacedn)(env, 14, stack[-13], stack[-12], stack[-11], stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1], stack[0]); break; case 15: r = (*displacedn)(env, 15, stack[-14], stack[-13], stack[-12], stack[-11], stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1], stack[0]); break; } else { trace_printf("Too many arguments to trace a function\n"); /* * Because the above is a horrid mess I will only support traced * calls with at most 15 args (more than I expect most people to * try). And this only applies to thigs that are NOT bytecoded - * I can trace bytecoded things with more args I believe, so users are not * utterly lost I hope. */ return aerror("traced function with > 15 args: not supported"); } popv(nargs); pop(name); errexit(); push(r); freshline_trace(); loop_print_trace(name); trace_printf(" = "); loop_print_trace(r); trace_printf("\n"); pop(r); return onevalue(r); } #define NOT_FOUND 100 static uint32_t find_built_in_function(one_args *f1, two_args *f2, n_args *fn) /* * This take the entrypoint of a function and tries to identify it * by scanning the tables used by the bytecode interpreter. If the * function is found a record is returned indicating how many args * it takes, and what its index is in the relevant table. The code * <NOT_FOUND,NOT_FOUND> is returned to indicate failure if the function * is not found. */ { int32_t index; for (index=0; zero_arg_functions[index]!=NULL; index++) if (fn == zero_arg_functions[index]) return pack_funtable(0, index); for (index=0; one_arg_functions[index]!=NULL; index++) if (f1 == one_arg_functions[index]) return pack_funtable(1, index); for (index=0; two_arg_functions[index]!=NULL; index++) if (f2 == two_arg_functions[index]) return pack_funtable(2, index); for (index=0; three_arg_functions[index]!=NULL; index++) if (fn == three_arg_functions[index]) return pack_funtable(3, index); return pack_funtable(NOT_FOUND, NOT_FOUND); } Lisp_Object Ltrace_all(Lisp_Object nil, Lisp_Object a) { #ifdef DEBUG if (a == nil) trace_all = 0; else trace_all = 1; return onevalue(nil); #else CSL_IGNORE(nil); CSL_IGNORE(a); return aerror("trace-all only supported in DEBUG version"); #endif } Lisp_Object Ltrace(Lisp_Object nil, Lisp_Object a) { Lisp_Object w = a; if (symbolp(a)) { a = ncons(a); errexit(); w = a; } while (consp(w)) { Lisp_Object s = qcar(w); w = qcdr(w); if (symbolp(s)) { one_args *f1 = qfn1(s); two_args *f2 = qfn2(s); n_args *fn = qfnn(s); int fixenv = 0, done = 0; if (f1 == undefined1) { freshline_debug(); debug_printf("+++ "); loop_print_debug(s); debug_printf(" not yet defined\n"); continue; } qheader(s) |= SYM_TRACED; if (f1 == interpreted1) { set_fns(s, traceinterpreted1, traceinterpreted2, traceinterpretedn); fixenv = done = 1; } if (f1 == funarged1) { set_fns(s, tracefunarged1, tracefunarged2, tracefunargedn); fixenv = done = 1; } if (fn == bytecoded0) ifnn(s) = (intptr_t)tracebytecoded0, done = 1; if (f1 == bytecoded1) ifn1(s) = (intptr_t)tracebytecoded1, done = 1; if (f2 == bytecoded2) ifn2(s) = (intptr_t)tracebytecoded2, done = 1; if (fn == bytecoded3) ifnn(s) = (intptr_t)tracebytecoded3, done = 1; if (fn == bytecodedn) ifnn(s) = (intptr_t)tracebytecodedn, done = 1; if (f1 == byteopt1) ifn1(s) = (intptr_t)tracebyteopt1, done = 1; if (f2 == byteopt2) ifn2(s) = (intptr_t)tracebyteopt2, done = 1; if (fn == byteoptn) ifnn(s) = (intptr_t)tracebyteoptn, done = 1; if (f1 == hardopt1) ifn1(s) = (intptr_t)tracehardopt1, done = 1; if (f2 == hardopt2) ifn2(s) = (intptr_t)tracehardopt2, done = 1; if (fn == hardoptn) ifnn(s) = (intptr_t)tracehardoptn, done = 1; if (f1 == byteoptrest1) ifn1(s) = (intptr_t)tracebyteoptrest1, done = 1; if (f2 == byteoptrest2) ifn2(s) = (intptr_t)tracebyteoptrest2, done = 1; if (fn == byteoptrestn) ifnn(s) = (intptr_t)tracebyteoptrestn, done = 1; if (f1 == hardoptrest1) ifn1(s) = (intptr_t)tracehardoptrest1, done = 1; if (f2 == hardoptrest2) ifn2(s) = (intptr_t)tracehardoptrest2, done = 1; if (fn == hardoptrestn) ifnn(s) = (intptr_t)tracehardoptrestn, done = 1; if (fixenv) { push2(a, s); a = cons(s, qenv(s)); errexitn(2); pop(s); qenv(s) = a; pop(a); } if (done) continue; /* * I permit the tracing of just one function from the kernel, and achieve * this by installing a wrapper function in place of the real definition. * Indeed this is just like Lisp-level embedding, except that I can get at the * entrypoint table used by the bytecode interpreter and so trap calls made * via there, and I can use that table to tell me how many arguments the * traced function needed. */ if (displaced1 == NULL) { int nargs = funtable_nargs(table_entry); /* * Remember what function was being traced, so that it can eventually be * invoked, and its name printed. */ displaced1 = f1; displaced2 = f2; displacedn = fn; tracedfn = s; /* * This makes calls via the regular interpreter see the traced version... */ set_fns(s, traced1_function, traced2_function, tracedn_function); table_entry = find_built_in_function(f1, f2, fn); nargs = funtable_nargs(table_entry); table_entry = funtable_index(table_entry); if (nargs != NOT_FOUND) { /* * .. and now I make calls via short-form bytecodes do likewise. */ switch (nargs) { default: case 0: zero_arg_functions[funtable_index(table_entry)] = tracedn_function; break; case 1: one_arg_functions[funtable_index(table_entry)] = traced1_function; break; case 2: two_arg_functions[funtable_index(table_entry)] = traced2_function; break; case 3: three_arg_functions[funtable_index(table_entry)] = tracedn_function; break; } } } continue; } } return onevalue(a); } Lisp_Object Luntrace(Lisp_Object nil, Lisp_Object a) { Lisp_Object w = a; CSL_IGNORE(nil); if (symbolp(a)) { a = ncons(a); errexit(); w = a; } while (consp(w)) { Lisp_Object s = qcar(w); w = qcdr(w); if (symbolp(s)) { one_args *f1 = qfn1(s); two_args *f2 = qfn2(s); n_args *fn = qfnn(s); if (f1 == traceinterpreted1) { set_fns(a, interpreted1, interpreted2, interpretedn); qenv(s) = qcdr(qenv(s)); } else if (f1 == tracefunarged1) { set_fns(s, funarged1, funarged2, funargedn); qenv(s) = qcdr(qenv(s)); } if (f1 == tracebytecoded1) ifn1(s) = (intptr_t)bytecoded1; if (f2 == tracebytecoded2) ifn2(s) = (intptr_t)bytecoded2; if (fn == tracebytecoded0) ifnn(s) = (intptr_t)bytecoded0; if (fn == tracebytecoded3) ifnn(s) = (intptr_t)bytecoded3; if (fn == tracebytecodedn) ifnn(s) = (intptr_t)bytecodedn; if (f1 == tracebyteopt1) ifn1(s) = (intptr_t)byteopt1; if (f2 == tracebyteopt2) ifn2(s) = (intptr_t)byteopt2; if (fn == tracebyteoptn) ifnn(s) = (intptr_t)byteoptn; if (f1 == tracebyteoptrest1) ifn1(s) = (intptr_t)byteoptrest1; if (f2 == tracebyteoptrest2) ifn2(s) = (intptr_t)byteoptrest2; if (fn == tracebyteoptrestn) ifnn(s) = (intptr_t)byteoptrestn; if (f1 == tracehardopt1) ifn1(s) = (intptr_t)hardopt1; if (f2 == tracehardopt2) ifn2(s) = (intptr_t)hardopt2; if (fn == tracehardoptn) ifnn(s) = (intptr_t)hardoptn; if (f1 == tracehardoptrest1) ifn1(s) = (intptr_t)hardoptrest1; if (f2 == tracehardoptrest2) ifn2(s) = (intptr_t)hardoptrest2; if (fn == tracehardoptrestn) ifnn(s) = (intptr_t)hardoptrestn; if (f1 == traced1_function) { int nargs = funtable_nargs(table_entry); set_fns(s, displaced1, displaced2, displacedn); if (nargs != NOT_FOUND) switch (nargs) { default: case 0: zero_arg_functions[funtable_index(table_entry)] = displacedn; break; case 1: one_arg_functions[funtable_index(table_entry)] = displaced1; break; case 2: two_arg_functions[funtable_index(table_entry)] = displaced2; break; case 3: three_arg_functions[funtable_index(table_entry)] = displacedn; break; } displaced1 = NULL; displaced2 = NULL; displacedn = NULL; } qheader(s) &= ~SYM_TRACED; } } return onevalue(a); } Lisp_Object Ldouble(Lisp_Object nil, Lisp_Object a) { Lisp_Object w = a; if (symbolp(a)) { a = ncons(a); errexit(); w = a; } while (consp(w)) { Lisp_Object s = qcar(w); w = qcdr(w); if (symbolp(s)) { one_args *f1 = qfn1(s); two_args *f2 = qfn2(s); n_args *fn = qfnn(s); int fixenv = 0, done = 0; if (f1 == undefined1) continue; if (f1 == interpreted1) { set_fns(s, double_interpreted1, double_interpreted2, double_interpretedn); fixenv = done = 1; } if (f1 == funarged1) { set_fns(s, double_funarged1, double_funarged2, double_funargedn); fixenv = done = 1; } if (fn == bytecoded0) ifnn(s) = (intptr_t)double_bytecoded0, done = 1; if (f1 == bytecoded1) ifn1(s) = (intptr_t)double_bytecoded1, done = 1; if (f2 == bytecoded2) ifn2(s) = (intptr_t)double_bytecoded2, done = 1; if (fn == bytecoded3) ifnn(s) = (intptr_t)double_bytecoded3, done = 1; if (fn == bytecodedn) ifnn(s) = (intptr_t)double_bytecodedn, done = 1; if (f1 == byteopt1) ifn1(s) = (intptr_t)double_byteopt1, done = 1; if (f2 == byteopt2) ifn2(s) = (intptr_t)double_byteopt2, done = 1; if (fn == byteoptn) ifnn(s) = (intptr_t)double_byteoptn, done = 1; if (f1 == hardopt1) ifn1(s) = (intptr_t)double_hardopt1, done = 1; if (f2 == hardopt2) ifn2(s) = (intptr_t)double_hardopt2, done = 1; if (fn == hardoptn) ifnn(s) = (intptr_t)double_hardoptn, done = 1; if (f1 == byteoptrest1) ifn1(s) = (intptr_t)double_byteoptrest1, done = 1; if (f2 == byteoptrest2) ifn2(s) = (intptr_t)double_byteoptrest2, done = 1; if (fn == byteoptrestn) ifnn(s) = (intptr_t)double_byteoptrestn, done = 1; if (f1 == hardoptrest1) ifn1(s) = (intptr_t)double_hardoptrest1, done = 1; if (f2 == hardoptrest2) ifn2(s) = (intptr_t)double_hardoptrest2, done = 1; if (fn == hardoptrestn) ifnn(s) = (intptr_t)double_hardoptrestn, done = 1; if (fixenv) { push2(a, s); a = cons(s, qenv(s)); errexitn(2); pop(s); qenv(s) = a; pop(a); } if (done) continue; debug_printf("Unable to execution-double: "); loop_print_debug(s); trace_printf("\n"); continue; } } return onevalue(a); } Lisp_Object Lundouble(Lisp_Object nil, Lisp_Object a) { Lisp_Object w = a; CSL_IGNORE(nil); if (symbolp(a)) { a = ncons(a); errexit(); w = a; } while (consp(w)) { Lisp_Object s = qcar(w); w = qcdr(w); if (symbolp(s)) { one_args *f1 = qfn1(s); two_args *f2 = qfn2(s); n_args *fn = qfnn(s); if (f1 == double_interpreted1) { set_fns(a, interpreted1, interpreted2, interpretedn); qenv(s) = qcdr(qenv(s)); } else if (f1 == double_funarged1) { set_fns(s, funarged1, funarged2, funargedn); qenv(s) = qcdr(qenv(s)); } else if (f1 == double_bytecoded1) ifn1(s) = (intptr_t)bytecoded1; else if (f2 == double_bytecoded2) ifn2(s) = (intptr_t)bytecoded2; else if (fn == double_bytecoded0) ifnn(s) = (intptr_t)bytecoded0; else if (fn == double_bytecoded3) ifnn(s) = (intptr_t)bytecoded3; else if (fn == double_bytecodedn) ifnn(s) = (intptr_t)bytecodedn; else if (f1 == double_byteopt1) ifn1(s) = (intptr_t)byteopt1; else if (f2 == double_byteopt2) ifn2(s) = (intptr_t)byteopt2; else if (fn == double_byteoptn) ifnn(s) = (intptr_t)byteoptn; else if (f1 == double_byteoptrest1) ifn1(s) = (intptr_t)byteoptrest1; else if (f2 == double_byteoptrest2) ifn2(s) = (intptr_t)byteoptrest2; else if (fn == double_byteoptrestn) ifnn(s) = (intptr_t)byteoptrestn; else if (f1 == double_hardopt1) ifn1(s) = (intptr_t)hardopt1; else if (f2 == double_hardopt2) ifn2(s) = (intptr_t)hardopt2; else if (fn == double_hardoptn) ifnn(s) = (intptr_t)hardoptn; else if (f1 == double_hardoptrest1) ifn1(s) = (intptr_t)hardoptrest1; else if (f2 == double_hardoptrest2) ifn2(s) = (intptr_t)hardoptrest2; else if (fn == double_hardoptrestn) ifnn(s) = (intptr_t)hardoptrestn; } } return onevalue(a); } #ifdef JIT /* * This is at present EXPERIMENTAL and INCOMPLETE so do not try it * unless you are involved in debugging the code involved. It will NOT * work for general code! */ Lisp_Object Ljit(Lisp_Object nil, Lisp_Object a) { Lisp_Object w = a; if (symbolp(a)) { a = ncons(a); errexit(); w = a; } while (consp(w)) { Lisp_Object s = qcar(w); w = qcdr(w); if (symbolp(s)) { one_args *f1 = qfn1(s); two_args *f2 = qfn2(s); n_args *fn = qfnn(s); int done = 0; if (fn == bytecoded0) ifnn(s) = (intptr_t)jitcompileme0, done = 1; if (f1 == bytecoded1) ifn1(s) = (intptr_t)jitcompileme1, done = 1; if (f2 == bytecoded2) ifn2(s) = (intptr_t)jitcompileme2, done = 1; if (fn == bytecoded3) ifnn(s) = (intptr_t)jitcompileme3, done = 1; if (fn == bytecodedn) ifnn(s) = (intptr_t)jitcompilemen, done = 1; if (!done) { freshline_debug(); debug_printf("+++ "); loop_print_debug(s); debug_printf(" not a candidate for JIT\n"); } continue; } } return onevalue(a); } #endif /* JIT */ Lisp_Object Lmacro_function(Lisp_Object nil, Lisp_Object a) { if (!symbolp(a)) return onevalue(nil); else if ((qheader(a) & SYM_MACRO) == 0) return onevalue(nil); /* If the MACRO bit is set in the header I know there is a definition */ else return onevalue(cons(lambda, qenv(a))); } Lisp_Object get_pname(Lisp_Object a) { Lisp_Object name = qpname(a); #ifndef COMMON /* * When a gensym is first created its pname field points at a string that * will form the base of its name, and a magic bit is set in its header. * If at some stage it is necessary to inspect the print name (mainly in * order to print the symbol) it becomes necessary to create a new string * and insert a serial number. Doing things this way means that the serial * numbers that users see will tend to be smaller, and space for per-gensym * strings does not get allocated unless really needed. The down side is * that every time I want to grab the pname of anything I have to check for * this case and admit the possibility of garbage collection or even * failure. */ if (qheader(a) & SYM_UNPRINTED_GENSYM) { uintptr_t len; Lisp_Object nil = C_nil; char genname[64]; len = length_of_header(vechdr(name)) - CELL; if (len > 60) len = 60; /* Unpublished truncation of the string */ sprintf(genname, "%.*s%lu", (int)len, (char *)name + (CELL - TAG_VECTOR), (long)gensym_ser++); push(a); name = make_string(genname); pop(a); errexit(); qpname(a) = name; qheader(a) &= ~SYM_UNPRINTED_GENSYM; } #endif return name; } Lisp_Object Lsymbol_name(Lisp_Object nil, Lisp_Object a) { if (!symbolp(a)) return aerror1("symbol-name", a); a = get_pname(a); errexit(); return onevalue(a); } #ifdef COMMON Lisp_Object Lsymbol_package(Lisp_Object nil, Lisp_Object a) { if (!symbolp(a)) return aerror1("symbol-package", a); a = qpackage(a); return onevalue(a); } #endif static Lisp_Object Lrestart_csl2(Lisp_Object nil, Lisp_Object a, Lisp_Object b) /* * If the argument is given as nil then this is a cold-start, and when * I begin again it would be a VERY good idea to do a (load!-module 'compat) * rather promptly (otherwise some Lisp functions will not work at all). * I do not automate that because this function is intended for use in * delicate system rebuilding contexts and I want the user to have ultimate * control. (restart!-csl t) reloads a heap-image in the normal way. * (restart!-csl 'xx) where xx is neither nil nor t starts by reloading a * heap image, but then it looks for a function with the same name as xx * (since a heap image is reloaded it is NOT easy (possible?) to keep the * symbol) and calls it as a function. Finally the case * (restart!-csl '(module fn)) restart the system, then calls load-module * on the named module and finally calls the given restart function. * This last option can be useful since otherwise the function to be called * in (restart!-csl 'xx) would need to be in the base image as re-loaded. */ { int n; char *v; #ifdef SOCKETS /* * Security measure - deny restart-csl to remote users */ if (socket_server != 0) return aerror("restart-csl"); #endif ensure_screen(); n = 0; v = NULL; /* * A comment seems in order here. The case b==SPID_NOARG should only * arise if I came from Lrestart_csl: it indicates that there was * no second argument provided. */ if (b != SPID_NOARG) { Lisp_Object b1; push(a); b1 = b = Lexploden(nil, b); pop(a); errexit(); while (b1 != nil) { n++; /* number of chars of arg */ b1 = qcdr(b1); } v = (char *)malloc(n+1); if (v == NULL) return aerror("space exhausted in restart-csl"); n = 0; while (b != nil) { v[n++] = (char)int_of_fixnum(qcar(b)); b = qcdr(b); } v[n] = 0; } term_printf("\nThe system is about to do a restart...\n"); /* Almost all unpicking of the argument is done back in csl.c */ exit_value = a; exit_tag = fixnum_of_int(2); /* Flag to say "restart" */ exit_reason = UNWIND_RESTART; exit_charvec = v; flip_exception(); return nil; } static Lisp_Object Lrestart_csl(Lisp_Object nil, Lisp_Object a) { return Lrestart_csl2(nil, a, SPID_NOARG); } static Lisp_Object Lpreserve(Lisp_Object nil, Lisp_Object startup, Lisp_Object banner) /* * (preserve <startup-fn>) saves a Lisp image in a standard place * and arranges that when restarted the saved image will call the specified * startup function. In the process of doing all this it unwinds down to * the top level of Lisp. If a startup function is not given then the * previously active one is used. If nil is specified then the previously * active startup function is retained. If banner is non-nil (well really * I want a string) is is a message of up to 40 characters to display * when the system restart. */ { char filename[LONGEST_LEGAL_FILENAME]; CSLbool failed; #ifdef SOCKETS /* * Security measure - deny preserve to remote users */ if (socket_server != 0) return aerror("preserve"); #endif if (startup != nil) supervisor = startup; failed = Iwriterootp(filename); /* Can I open image file for writing? */ term_printf("\nThe system will be preserved on file \"%s\"\n", filename); if (failed) return aerror("preserve"); ensure_screen(); exit_count = 0; nil = C_nil; exit_value = banner; exit_tag = fixnum_of_int(1); /* Flag to say "preserve" */ exit_reason = UNWIND_RESTART; flip_exception(); return nil; } static Lisp_Object MS_CDECL Lpreserve_0(Lisp_Object nil, int nargs, ...) { argcheck(nargs, 0, "preserve"); return Lpreserve(nil, nil, nil); } static Lisp_Object Lpreserve_1(Lisp_Object nil, Lisp_Object startup) { return Lpreserve(nil, startup, nil); } /* * This is an experimental addition - a version of PRESERVE that allows * CSL to continue executing after it has written out an image file. */ static Lisp_Object Lcheckpoint(Lisp_Object nil, Lisp_Object startup, Lisp_Object banner) { char filename[LONGEST_LEGAL_FILENAME]; CSLbool failed = 0; char *msg = ""; #ifdef SOCKETS /* * Security measure - deny checkpoint to remote users */ if (socket_server != 0) return aerror("checkpoint"); #endif ensure_screen(); if (startup != nil) supervisor = startup; failed = Iwriterootp(filename); /* Can I open image file for writing? */ term_printf("\nThe system will be preserved on file \"%s\"\n", filename); if (failed) return aerror("checkpoint"); if (is_vector(banner) && type_of_header(vechdr(banner)) == TYPE_STRING) msg = &celt(banner, 0); /* * Note, with some degree of nervousness, that things on the C stack will * be updated by the garbage collection that happens during the processing * of the call to preserve(), but they will be neither adjusted into * relative addresses nor unadjusted (and hence restored) by in the * image-writing. But the image writing will not actually move any data * around so all is still OK, I hope! */ push5(codevec, litvec, catch_tags, faslvec, faslgensyms); preserve(msg); nil = C_nil; if (exception_pending()) failed = 1, flip_exception(); adjust_all(); pop5(faslgensyms, faslvec, catch_tags, litvec, codevec); eq_hash_tables = eq_hash_table_list; equal_hash_tables = equal_hash_table_list; eq_hash_table_list = equal_hash_table_list = nil; { Lisp_Object qq; for (qq = eq_hash_tables; qq!=nil; qq=qcdr(qq)) rehash_this_table(qcar(qq)); for (qq = equal_hash_tables; qq!=nil; qq=qcdr(qq)) rehash_this_table(qcar(qq)); } set_up_functions(YES); if (failed) return aerror("checkpoint"); return onevalue(nil); } static Lisp_Object MS_CDECL Lcheckpoint_0(Lisp_Object nil, int nargs, ...) { argcheck(nargs, 0, "checkpoint"); return Lcheckpoint(nil, nil, nil); } static Lisp_Object Lcheckpoint_1(Lisp_Object nil, Lisp_Object startup) { return Lcheckpoint(nil, startup, nil); } #ifdef COMMON static CSLbool eql_numbers(Lisp_Object a, Lisp_Object b) /* * This is only called from eql, and then only when a and b are both tagged * as ratios or complex numbers. */ { Lisp_Object p, q; p = *(Lisp_Object *)(a + (CELL - TAG_NUMBERS)); q = *(Lisp_Object *)(b + (CELL - TAG_NUMBERS)); if (!eql(p, q)) return NO; p = *(Lisp_Object *)(a + (2*CELL - TAG_NUMBERS)); q = *(Lisp_Object *)(b + (2*CELL - TAG_NUMBERS)); return eql(p, q); } #endif CSLbool eql_fn(Lisp_Object a, Lisp_Object b) /* * This seems incredible - all the messing about that is needed to * check that numeric values compare properly. Ugh. */ { /* * (these tests done before eql_fn is called). * if (a == b) return YES; * if ((((int32_t)a ^ (int32_t)b) & TAG_BITS) != 0) return NO; * * Actually in Common Lisp mode where I have short floats as immediate data * I have further pain here with (eql 0.0 -0.0). */ #ifdef COMMON if ((a == TAG_SFLOAT && b == (TAG_SFLOAT|0x80000000)) || (a == (TAG_SFLOAT|0x80000000) && b == TAG_SFLOAT)) return YES; #endif if (!is_number(a) || is_immed_or_cons(a)) return NO; if (is_bfloat(a)) { Header h = flthdr(a); if (h != flthdr(b)) return NO; #ifdef COMMON if (type_of_header(h) == TYPE_SINGLE_FLOAT) return (single_float_val(a) == single_float_val(b)); else #endif /* * For the moment I view all non-single floats as double floats. Extra * stuff will be needed here if I ever implement long floats as 3-word * objects. */ return (double_float_val(a) == double_float_val(b)); } else /* ratio, complex or bignum */ { Header h = numhdr(a); if (h != numhdr(b)) return NO; if (type_of_header(h) == TYPE_BIGNUM) { intptr_t hh = (intptr_t)length_of_header(h) - TAG_NUMBERS; while (hh > (intptr_t)(CELL - TAG_NUMBERS)) { hh -= 4; if (*(uint32_t *)((char *)a + hh) != *(uint32_t *)((char *)b + hh)) return NO; } return YES; } #ifdef COMMON else return eql_numbers(a, b); #else else return NO; #endif } } static CSLbool cl_vec_equal(Lisp_Object a, Lisp_Object b) /* * here a and b are known to be vectors or arrays. This should compare * them following the Common Lisp recipe, where strings or bitvectors * (simple or complex) have their contents compared, while all other types of * vector or array are tested using EQ. */ { Header ha = vechdr(a), hb = vechdr(b); intptr_t offa = 0, offb = 0; int ta = type_of_header(ha), tb = type_of_header(hb); intptr_t la = length_of_header(ha), lb = length_of_header(hb); #ifdef COMMON if (header_of_bitvector(ha)) ta = TYPE_BITVEC1; if (header_of_bitvector(hb)) tb = TYPE_BITVEC1; #endif switch (ta) { /* case TYPE_ARRAY: /* My moan here is that, as noted above, I ought to process even * non-simple strings and bit-vectors by comparing contents, but as a * matter of idleness I have not yet got around to that. In fact if I get * arrays to compare here I will pretend that they are not strings or * bit-vectors and compare using EQ... */ case TYPE_STRING: switch (tb) { /* /* case TYPE_ARRAY: */ case TYPE_STRING: goto compare_strings; default:return NO; } #ifdef COMMON case TYPE_BITVEC1: switch (tb) { /* /* case TYPE_ARRAY: */ case TYPE_BITVEC1: goto compare_bits; default:return NO; } #endif default: return (a == b); } compare_strings: if (la != lb) return NO; while (la > 0) { la--; if (*((char *)a + la + offa - TAG_VECTOR) != *((char *)b + la + offb - TAG_VECTOR)) return NO; } return YES; #ifdef COMMON compare_bits: if (la != lb) return NO; while (la > 0) { la--; if (*((char *)a + la + offa - TAG_VECTOR) != *((char *)b + la + offb - TAG_VECTOR)) return NO; } return YES; #endif } CSLbool cl_equal_fn(Lisp_Object a, Lisp_Object b) /* * a and b are not EQ at this stage.. I guarantee to have checked that * before entering this general purpose code. */ { Lisp_Object nil = C_nil; CSL_IGNORE(nil); /* * The for loop at the top here is so that cl_equal can iterate along the * length of linear lists. */ #ifdef CHECK_STACK if (check_stack(__FILE__,__LINE__)) { err_printf("Stack too deep in cl_equal\n"); my_exit(EXIT_FAILURE); } #endif for (;;) { int32_t ta = (int32_t)a & TAG_BITS; if (ta == TAG_CONS #ifdef COMMON && a != nil #endif ) { if (!consp(b) #ifdef COMMON || b == nil #endif ) return NO; else { Lisp_Object ca = qcar(a), cb = qcar(b); if (ca == cb) { a = qcdr(a); b = qcdr(b); if (a == b) return YES; continue; } /* * And here, because cl_equal() seems to be a very important low-level * primitive, I unwind one level of the recursion that would arise * with nested lists. */ for (;;) { int32_t tca = (int32_t)ca & TAG_BITS; if (tca == TAG_CONS #ifdef COMMON && ca != nil #endif ) { if (!consp(cb) #ifdef COMMON || cb == nil #endif ) return NO; else { Lisp_Object cca = qcar(ca), ccb = qcar(cb); if (cca == ccb) { ca = qcdr(ca); cb = qcdr(cb); if (ca == cb) break; continue; } /* * Do a real recursion when I get down to args like * ((x ...) ...) ((y ...) ...) */ if (!cl_equal(cca, ccb)) return NO; ca = qcdr(ca); cb = qcdr(cb); if (ca == cb) break; continue; } } else if (tca <= TAG_SYMBOL || ((int32_t)cb & TAG_BITS) != tca) return NO; else switch (tca) { case TAG_NUMBERS: { Header h = numhdr(ca); if (h != numhdr(cb)) return NO; if (type_of_header(h) == TYPE_BIGNUM) { intptr_t hh = (intptr_t)length_of_header(h) - TAG_NUMBERS; while (hh > (intptr_t)(CELL - TAG_NUMBERS)) { hh -= 4; if (*(uint32_t *)((char *)ca + hh) != *(uint32_t *)((char *)cb + hh)) return NO; } break; } #ifdef COMMON else if (!eql_numbers(ca, cb)) return NO; else break; #else else return NO; #endif } case TAG_VECTOR: if (!cl_vec_equal(ca, cb)) return NO; break; default: case TAG_BOXFLOAT: { Header h = flthdr(ca); if (h != flthdr(cb)) return NO; #ifdef COMMON if (type_of_header(h) == TYPE_SINGLE_FLOAT) { if (single_float_val(ca) != single_float_val(cb)) return NO; else break; } else #endif { if (double_float_val(ca) != double_float_val(cb)) return NO; else break; } } } break; /* out of the for (;;) loop */ } a = qcdr(a); b = qcdr(b); if (a == b) return YES; continue; } } else if (ta <= TAG_SYMBOL || ((int32_t)b & TAG_BITS) != ta) return NO; /* * OK - now a and b both have the same type and neither are immediate data * conses or symbols. That leaves vectors (including strings) and boxed * numbers. */ else switch (ta) { case TAG_NUMBERS: { Header h = numhdr(a); if (h != numhdr(b)) return NO; if (type_of_header(h) == TYPE_BIGNUM) { intptr_t hh = (intptr_t)length_of_header(h) - TAG_NUMBERS; while (hh > (intptr_t)(CELL - TAG_NUMBERS)) { hh -= 4; if (*(uint32_t *)((char *)a + hh) != *(uint32_t *)((char *)b + hh)) return NO; } return YES; } #ifdef COMMON else return eql_numbers(a, b); #else else return NO; #endif } case TAG_VECTOR: return cl_vec_equal(a, b); default: case TAG_BOXFLOAT: { Header h = flthdr(a); if (h != flthdr(b)) return NO; #ifdef COMMON if (type_of_header(h) == TYPE_SINGLE_FLOAT) { if (single_float_val(a) != single_float_val(b)) return NO; else return YES; } else #endif /* * For the moment I view all non-single floats as double floats. Extra * stuff will be needed here if I ever implement long floats as 3-word * objects. */ { if (double_float_val(a) != double_float_val(b)) return NO; else return YES; } } } } } static CSLbool vec_equal(Lisp_Object a, Lisp_Object b); #ifdef TRACED_EQUAL #define LOG_SIZE 10000 typedef struct equal_record { char file[24]; int line; int depth; int count; } equal_record; static equal_record equal_counts[LOG_SIZE]; static void record_equal(char *file, int line, int depth) { int hash = 169*line + depth; char *p = file; while (*p != 0) hash = 169*hash + (*p++ & 0xff); hash = ((169*hash) & 0x7fffffff) % LOG_SIZE; while (equal_counts[hash].count != 0) { if (equal_counts[hash].line == line && equal_counts[hash].depth == depth && strncmp(equal_counts[hash].file, file, 24) == 0) { equal_counts[hash].count++; return; } hash = (hash + 1) % LOG_SIZE; } strncpy(equal_counts[hash].file, file, 24); equal_counts[hash].line = line; equal_counts[hash].depth = depth; equal_counts[hash].count = 1; return; } void dump_equals() { int i; FILE *log = fopen("equal.log", "w"); if (log == NULL) log = stdout; fprintf(log, "\nCalls to equal...\n"); for (i=0; i<LOG_SIZE; i++) if (equal_counts[i].count != 0) fprintf(log, "%24.24s %5d %5d %10d\n", equal_counts[i].file, equal_counts[i].line, equal_counts[i].depth, equal_counts[i].count); fprintf(log, "end of counts\n"); if (log != stdout) fclose(log); } CSLbool traced_equal_fn(Lisp_Object a, Lisp_Object b, char *file, int line, int depth) /* * a and b are not EQ at this stage.. I guarantee to have checked that * before entering this general purpose code. */ { Lisp_Object nil = C_nil; record_equal(file, line, depth); #undef equal_fn #define equal_fn(a, b) traced_equal_fn(a, b, file, line, depth+1) #else CSLbool equal_fn(Lisp_Object a, Lisp_Object b) /* * a and b are not EQ at this stage.. I guarantee to have checked that * before entering this general purpose code. I will also have checked that * the types of the two args agree, and that they are not both immediate * date. */ { Lisp_Object nil = C_nil; CSL_IGNORE(nil); #endif /* * The for loop at the top here is so that equal can iterate along the * length of linear lists. */ #ifdef CHECK_STACK if (check_stack(__FILE__,__LINE__)) { err_printf("Stack too deep in equal\n"); my_exit(EXIT_FAILURE); } #endif for (;;) { int32_t ta = (int32_t)a & TAG_BITS; if (ta == TAG_CONS #ifdef COMMON && a != nil #endif ) { if (!consp(b) #ifdef COMMON || b == nil #endif ) return NO; else { Lisp_Object ca = qcar(a), cb = qcar(b); if (ca == cb) { a = qcdr(a); b = qcdr(b); if (a == b) return YES; continue; } /* * And here, because equal() seems to be a very important low-level * primitive, I unwind one level of the recursion that would arise * with nested lists. */ for (;;) { int32_t tca = (int32_t)ca & TAG_BITS; if (tca == TAG_CONS #ifdef COMMON && ca != nil #endif ) { if (!consp(cb) #ifdef COMMON || cb == nil #endif ) return NO; else { Lisp_Object cca = qcar(ca), ccb = qcar(cb); if (cca == ccb) { ca = qcdr(ca); cb = qcdr(cb); if (ca == cb) break; continue; } /* * Do a real recursion when I get down to args like * ((x ...) ...) ((y ...) ...) */ if (!equal(cca, ccb)) return NO; ca = qcdr(ca); cb = qcdr(cb); if (ca == cb) break; continue; } } else if (tca <= TAG_SYMBOL || ((int32_t)cb & TAG_BITS) != tca) return NO; else switch (tca) { case TAG_NUMBERS: { Header h = numhdr(ca); if (h != numhdr(cb)) return NO; if (type_of_header(h) == TYPE_BIGNUM) { intptr_t hh = (intptr_t)length_of_header(h) - TAG_NUMBERS; while (hh > (intptr_t)(CELL - TAG_NUMBERS)) { hh -= 4; if (*(uint32_t *)((char *)ca + hh) != *(uint32_t *)((char *)cb + hh)) return NO; } break; } #ifdef COMMON else if (!eql_numbers(ca, cb)) return NO; else break; #else else return NO; #endif } case TAG_VECTOR: if (!vec_equal(ca, cb)) return NO; break; default: case TAG_BOXFLOAT: { Header h = flthdr(ca); if (h != flthdr(cb)) return NO; #ifdef COMMON if (type_of_header(h) == TYPE_SINGLE_FLOAT) { if (single_float_val(ca) != single_float_val(cb)) return NO; else break; } else #endif { if (double_float_val(ca) != double_float_val(cb)) return NO; else break; } } } break; /* out of the for (;;) loop */ } a = qcdr(a); b = qcdr(b); if (a == b) return YES; continue; } } else if (ta <= TAG_SYMBOL || ((int32_t)b & TAG_BITS) != ta) return NO; else switch (ta) { case TAG_NUMBERS: { Header h = numhdr(a); if (h != numhdr(b)) return NO; if (type_of_header(h) == TYPE_BIGNUM) { intptr_t hh = (intptr_t)length_of_header(h) - TAG_NUMBERS; while (hh > (intptr_t)(CELL - TAG_NUMBERS)) { hh -= 4; if (*(uint32_t *)((char *)a + hh) != *(uint32_t *)((char *)b + hh)) return NO; } return YES; } #ifdef COMMON else return eql_numbers(a, b); #else else return NO; #endif } case TAG_VECTOR: return vec_equal(a, b); default: case TAG_BOXFLOAT: { Header h = flthdr(a); if (h != flthdr(b)) return NO; #ifdef COMMON if (type_of_header(h) == TYPE_SINGLE_FLOAT) { if (single_float_val(a) != single_float_val(b)) return NO; else return YES; } else #endif /* * For the moment I view all non-single floats as double floats. Extra * stuff will be needed here if I ever implement long floats as 3-word * objects. */ { if (double_float_val(a) != double_float_val(b)) return NO; else return YES; } } } } } #ifdef TRACED_EQUAL #undef equal_fn #define equal_fn(a, b) traced_equal(a, b, __FILE__, __LINE__, 0) #endif static CSLbool vec_equal(Lisp_Object a, Lisp_Object b) /* * Here a and b are known to be vectors. Compare using recursive calls to * EQUAL on all components. */ { Header ha = vechdr(a), hb = vechdr(b); int32_t l; if (ha != hb) return NO; l = (int32_t)doubleword_align_up(length_of_header(ha)); if (vector_holds_binary(ha)) { while ((l -= 4) != 0) if (*((uint32_t *)((char *)a + l - TAG_VECTOR)) != *((uint32_t *)((char *)b + l - TAG_VECTOR))) return NO; return YES; } else { if (is_mixed_header(ha)) { while (l > 16) { uint32_t ea = *((uint32_t *)((char *)a + l - TAG_VECTOR - 4)), eb = *((uint32_t *)((char *)b + l - TAG_VECTOR - 4)); if (ea != eb) return NO; l -= 4; } } while ((l -= CELL) != 0) { Lisp_Object ea = *((Lisp_Object *)((char *)a + l - TAG_VECTOR)), eb = *((Lisp_Object *)((char *)b + l - TAG_VECTOR)); if (ea == eb) continue; if (!equal(ea, eb)) return NO; } return YES; } } CSLbool equalp(Lisp_Object a, Lisp_Object b) /* * a and b are not EQ at this stage.. I guarantee to have checked that * before entering this general purpose code. */ { Lisp_Object nil = C_nil; CSL_IGNORE(nil); /* * The for loop at the top here is so that equalp can iterate along the * length of linear lists. */ #ifdef CHECK_STACK if (check_stack(__FILE__,__LINE__)) { err_printf("Stack too deep in equalp\n"); my_exit(EXIT_FAILURE); } #endif for (;;) { int32_t ta = (int32_t)a & TAG_BITS; if (ta == TAG_CONS #ifdef COMMON && a != nil #endif ) { if (!consp(b) #ifdef COMMON || b == nil #endif ) return NO; else { Lisp_Object ca = qcar(a), cb = qcar(b); if (ca == cb) { a = qcdr(a); b = qcdr(b); if (a == b) return YES; continue; } /* * And here, because equalp() seems to be a very important low-level * primitive, I unwind one level of the recursion that would arise * with nested lists. */ for (;;) { int32_t tca = (int32_t)ca & TAG_BITS; if (tca == TAG_CONS #ifdef COMMON && ca != nil #endif ) { if (!consp(cb) #ifdef COMMON || cb == nil #endif ) return NO; else { Lisp_Object cca = qcar(ca), ccb = qcar(cb); if (cca == ccb) { ca = qcdr(ca); cb = qcdr(cb); if (ca == cb) break; continue; } /* * Do a real recursion when I get down to args like * ((x ...) ...) ((y ...) ...) */ if (!equalp(cca, ccb)) return NO; ca = qcdr(ca); cb = qcdr(cb); if (ca == cb) break; continue; } } else if (tca <= TAG_SYMBOL || ((int32_t)cb & TAG_BITS) != tca) return NO; else switch (tca) { case TAG_NUMBERS: { Header h = numhdr(ca); if (h != numhdr(cb)) return NO; if (type_of_header(h) == TYPE_BIGNUM) { intptr_t hh = (intptr_t)length_of_header(h) - TAG_NUMBERS; while (hh > (intptr_t)(CELL - TAG_NUMBERS)) { hh -= 4; if (*(uint32_t *)((char *)ca + hh) != *(uint32_t *)((char *)cb + hh)) return NO; } break; } #ifdef COMMON else if (!eql_numbers(ca, cb)) return NO; else break; #else else return NO; #endif } case TAG_VECTOR: /* /* At present vec_equal() is not right here */ if (!vec_equal(ca, cb)) return NO; break; default: case TAG_BOXFLOAT: { Header h = flthdr(ca); if (h != flthdr(cb)) return NO; #ifdef COMMON if (type_of_header(h) == TYPE_SINGLE_FLOAT) { if (single_float_val(ca) != single_float_val(cb)) return NO; else break; } else #endif { if (double_float_val(ca) != double_float_val(cb)) return NO; else break; } } } break; /* out of the for (;;) loop */ } a = qcdr(a); b = qcdr(b); if (a == b) return YES; continue; } } else if (ta <= TAG_SYMBOL || ((int32_t)b & TAG_BITS) != ta) return NO; /* What is left is vectors, strings and boxed numbers */ else switch (ta) { case TAG_NUMBERS: { Header h = numhdr(a); if (h != numhdr(b)) return NO; if (type_of_header(h) == TYPE_BIGNUM) { intptr_t hh = (intptr_t)length_of_header(h) - TAG_NUMBERS; while (hh > (intptr_t)(CELL - TAG_NUMBERS)) { hh -= 4; if (*(uint32_t *)((char *)a + hh) != *(uint32_t *)((char *)b + hh)) return NO; } return YES; } #ifdef COMMON else return eql_numbers(a, b); #else else return NO; #endif } case TAG_VECTOR: /* /* wrong for Common Lisp */ return vec_equal(a, b); default: case TAG_BOXFLOAT: { Header h = flthdr(a); if (h != flthdr(b)) return NO; #ifdef COMMON if (type_of_header(h) == TYPE_SINGLE_FLOAT) { if (single_float_val(a) != single_float_val(b)) return NO; else return YES; } else #endif /* * For the moment I view all non-single floats as double floats. Extra * stuff will be needed here if I ever implement long floats as 3-word * objects. */ { if (double_float_val(a) != double_float_val(b)) return NO; else return YES; } } } } } Lisp_Object Leq(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { return onevalue(Lispify_predicate(a == b)); } Lisp_Object Leql(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { return onevalue(Lispify_predicate(eql(a, b))); } Lisp_Object Leqcar(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { if (!consp(a)) return onevalue(nil); a = qcar(a); #ifdef COMMON return onevalue(Lispify_predicate(eql(a, b))); #else return onevalue(Lispify_predicate(a == b)); #endif } Lisp_Object Lequalcar(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { if (!consp(a)) return onevalue(nil); a = qcar(a); if (a == b) return lisp_true; else return onevalue(Lispify_predicate(equal(a, b))); } Lisp_Object Lcl_equal(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { if (a == b) return onevalue(lisp_true); else return onevalue(Lispify_predicate(cl_equal(a, b))); } Lisp_Object Lequal(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { if (a == b) return onevalue(lisp_true); else return onevalue(Lispify_predicate(equal(a, b))); } Lisp_Object Lequalp(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { if (a == b) return onevalue(lisp_true); else return onevalue(Lispify_predicate(equalp(a, b))); } Lisp_Object Lneq(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { CSLbool r; #ifdef COMMON r = cl_equal(a, b); #else r = equal(a, b); #endif return onevalue(Lispify_predicate(!r)); } Lisp_Object Lnull(Lisp_Object nil, Lisp_Object a) { return onevalue(Lispify_predicate(a == nil)); } Lisp_Object Lendp(Lisp_Object nil, Lisp_Object a) { if (a == nil) return onevalue(lisp_true); else if (is_cons(a)) return onevalue(nil); else return error(1, err_bad_endp, a); } Lisp_Object Lnreverse(Lisp_Object nil, Lisp_Object a) { Lisp_Object b = nil; #ifdef COMMON if (is_vector(a)) { int32_t n = Llength(nil, a) - 0x10; int32_t i = TAG_FIXNUM; while (n > i) { Lisp_Object w = Laref2(nil, a, i); Laset(nil, 3, a, i, Laref2(nil, a, n)); Laset(nil, 3, a, n, w); i += 0x10; n -= 0x10; } return onevalue(a); } #endif while (consp(a)) { Lisp_Object c = a; a = qcdr(a); qcdr(c) = b; b = c; } return onevalue(b); } Lisp_Object Lnreverse2(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { CSL_IGNORE(nil); while (consp(a)) { Lisp_Object c = a; a = qcdr(a); qcdr(c) = b; b = c; } return onevalue(b); } #ifdef COMMON /* * nreverse0 is like nreverse except that if its input is atomic it gets * returned intact rather than being converted to nil. */ Lisp_Object Lnreverse0(Lisp_Object nil, Lisp_Object a) { Lisp_Object b = nil; if (!consp(a)) return onevalue(a); b = a; a = qcdr(a); qcdr(b) = nil; while (consp(a)) { Lisp_Object c = a; a = qcdr(a); qcdr(c) = b; b = c; } return onevalue(b); } #endif Lisp_Object Lreverse(Lisp_Object nil, Lisp_Object a) { Lisp_Object r; stackcheck1(0, a); nil = C_nil; r = nil; while (consp(a)) { push(a); r = cons(qcar(a), r); pop(a); errexit(); a = qcdr(a); } return onevalue(r); } Lisp_Object Lassoc(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { #ifdef TRACED_EQUAL Lisp_Object save_b = b; int pos = 0; #endif if (is_symbol(a) || is_fixnum(a)) { while (consp(b)) { Lisp_Object c = qcar(b); if (consp(c) && a == qcar(c)) return onevalue(c); b = qcdr(b); } return onevalue(nil); } while (consp(b)) { Lisp_Object c = qcar(b); if (consp(c)) { Lisp_Object cc = qcar(c); #ifdef COMMON if (cl_equal(a, cc)) return onevalue(c); #else if (equal(a, cc)) { #ifdef TRACED_EQUAL /* * In silly cases the length might not be a fixnum! */ trace_printf("Assoc YES %3d %3d ", pos, int_of_fixnum(Llength(nil,save_b))); prin_to_stdout(a); trace_printf("\n"); #endif return onevalue(c); } #endif } b = qcdr(b); #ifdef TRACED_EQUAL pos++; #endif } #ifdef TRACED_EQUAL /* beware stuplidly lonng lists... */ trace_printf("Assoc NO %3d %3d ", pos, int_of_fixnum(Llength(nil,save_b))); prin_to_stdout(a); trace_printf("\n"); #endif return onevalue(nil); } Lisp_Object Latsoc(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { #ifdef COMMON if (is_symbol(a) || is_fixnum(a)) { while (consp(b)) { Lisp_Object c = qcar(b); if (consp(c) && a == qcar(c)) return onevalue(c); b = qcdr(b); } return onevalue(nil); } #endif while (consp(b)) { Lisp_Object c = qcar(b); /* * eql() can neither fail nor call the garbage collector, so I do * not need to stack things here. */ #ifdef COMMON if (consp(c) && eql(a, qcar(c))) return onevalue(c); #else if (consp(c) && a == qcar(c)) return onevalue(c); #endif b = qcdr(b); } return onevalue(nil); } Lisp_Object Lmember(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { if (is_symbol(a) || is_fixnum(a)) { while (consp(b)) { if (a == qcar(b)) return onevalue(b); b = qcdr(b); } return onevalue(nil); } while (consp(b)) { Lisp_Object cb = qcar(b); #ifdef COMMON if (cl_equal(a, cb)) return onevalue(b); #else if (equal(a, cb)) return onevalue(b); #endif b = qcdr(b); } return onevalue(nil); } Lisp_Object Lmemq(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { #ifdef COMMON if (is_symbol(a) || is_fixnum(a)) { while (consp(b)) { if (a == qcar(b)) return onevalue(b); b = qcdr(b); } return onevalue(nil); } #endif while (consp(b)) /* * Note that eql() can never fail, and so checking for errors * and stacking a and b across the call to it is not necessary. */ { #ifdef COMMON if (eql(a, qcar(b))) return onevalue(b); #else if (a == qcar(b)) return onevalue(b); #endif b = qcdr(b); } return onevalue(nil); } static CSLbool smemq(Lisp_Object a, Lisp_Object b) { /* * /* This is a bit worrying - it can use C recursion to arbitrary * depth without any checking for overflow, and hence it can ESCAPE * if (e.g.) given cyclic structures. Some alteration is needed. As * things stand the code can never give wrong answers via GC rearrangement - * the problem is closer to being that it can never call the GC. */ #ifdef COMMON Lisp_Object nil = C_nil; #else nil_as_base #endif while (consp(b)) { Lisp_Object w = qcar(b); if (w == quote_symbol) return NO; else if (smemq(a, w)) return YES; else b = qcdr(b); } return (a == b); } Lisp_Object Lsmemq(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { CSLbool r; r = smemq(a, b); errexit(); return onevalue(Lispify_predicate(r)); } /* * (defun contained (x y) * (cond ((atom y) (equal x y)) * ((equal x y) 't) * ('t (or (contained x (car y)) (contained x (cdr y)))))) */ static CSLbool containedeq(Lisp_Object nil, Lisp_Object x, Lisp_Object y) { while (consp(y)) { if (containedeq(nil, x, qcar(y))) return YES; y = qcdr(y); } return (x == y); } static CSLbool containedequal(Lisp_Object nil, Lisp_Object x, Lisp_Object y) { while (consp(y)) { if (equal(x, y)) return YES; if (containedequal(nil, x, qcar(y))) return YES; y = qcdr(y); } return equal(x, y); } static Lisp_Object Lcontained(Lisp_Object nil, Lisp_Object x, Lisp_Object y) { CSLbool r; if (is_symbol(x) || is_fixnum(x)) r = containedeq(nil, x, y); else r = containedequal(nil, x, y); errexit(); return onevalue(Lispify_predicate(r)); } Lisp_Object Llast(Lisp_Object nil, Lisp_Object a) { Lisp_Object b; CSL_IGNORE(nil); if (!consp(a)) return aerror1("last", a); while (b = qcdr(a), consp(b)) a = b; return onevalue(qcar(a)); } Lisp_Object Llastpair(Lisp_Object nil, Lisp_Object a) { Lisp_Object b; CSL_IGNORE(nil); if (!consp(a)) return onevalue(a); /* aerror1("lastpair", a); */ while (b = qcdr(a), consp(b)) a = b; return onevalue(a); } Lisp_Object Llength(Lisp_Object nil, Lisp_Object a) { /* * The use of 64-bit computers generates a real jolly here. In CSL at * present FIXNUMs are 28 bits, so the largest positive fixnum is around * 131,000,000. If I count lengths in fixnums as I did in my initial * version here and I look at a list longer than that (which of necessity * uses around 2G of memory on a 64 bit machine) I get an overflow and a * silly answer. I might JUST be able to get the same overflow on a 32-bit * system using just a bit over 1G of memory. * * If I do the counts in 32-bit "unsigned int" arithmetic then the * world is safe until I have filled 64G with cons cells. Right now that * feels safe... but having been bitten here once I will arrange to go * distinctly beyond that just to be certain - or at least to put off the * issue for long enough that I can be confident that it will not matter. */ if (a == nil) return onevalue(fixnum_of_int(0)); if (is_cons(a)) { uint32_t n, nhigh; /* * Possibly I should do something to trap cyclic lists.. But doing so * would tend to be extra cost so unless it becomes a vital issue because * of some bug I will not worry. */ n = 1; nhigh = 0; /* * I have unrolled the loop here 4 times since I expect length to be * tolerably heavily used. Look at the assembly code generated for * this to see if it was useful or counterproductive! */ for (;;) { a = qcdr(a); if (!consp(a)) break; a = qcdr(a); if (!consp(a)) { n += 1; break; } a = qcdr(a); if (!consp(a)) { n += 2; break; } a = qcdr(a); if (!consp(a)) { n += 3; break; } n += 4; if ((n & 0x80000000) != 0) { n = 0; nhigh++; } } if (nhigh != 0) return make_two_word_bignum(nhigh, n); else if ((n & fix_mask) != 0) return make_one_word_bignum(n); else return fixnum_of_int(n); } #ifndef COMMON return onevalue(fixnum_of_int(0)); /* aerror("length");??? */ #else /* * Common Lisp expects length to find the length of vectors * as well as lists. I believe that because of my "page size" limit no * bector can have more than 2^27 entries. The first possible dodgy case * would be a bit-vector, and one of those that was 16Mbytes long would * hit the limit... while at the time of writing this I use 4 Mbyte pages * even on 64-bit machines. */ else if (!is_vector(a)) return aerror1("length", a); else { Header h = vechdr(a); int32_t n = length_of_header(h) - CELL; if (type_of_header(h) == TYPE_ARRAY) { Lisp_Object dims = elt(a, 1); Lisp_Object fillp = elt(a, 5); if (consp(dims) && !consp(qcdr(dims))) dims = qcar(dims); else return aerror1("length", a); /* Not one-dimensional */ if (is_fixnum(fillp)) dims = fillp; return onevalue(dims); } if (header_of_bitvector(h)) { n = (n - 1)*8; /* Dodgy constant on next line - critically dependent on tag codes used! */ n += ((h & 0x380) >> 7) + 1; } else if (type_of_header(h) != TYPE_STRING) n = n/CELL; return onevalue(fixnum_of_int(n)); } #endif } #ifdef COMMON Lisp_Object MS_CDECL Lappend_n(Lisp_Object nil, int nargs, ...) { va_list a; int i; Lisp_Object r; if (nargs == 0) return onevalue(nil); va_start(a, nargs); push_args(a, nargs); /* * The actual args have been passed a C args - I can not afford to * risk garbage collection until they have all been moved somewhere safe, * and here that safe place is the Lisp stack. I have to delay checking for * overflow on same until all args have been pushed. */ stackcheck0(nargs); nil = C_nil; r = nil; /* * rearrange order of items on the stack... * The idea is that I will then reverse-copy the args in the order a1, * a2 , ... to make a result list. But I want to pop the stack as soon as * I can, so I need arg1 on the TOP of the stack. */ for (i = 0; 2*i+1<nargs; i++) { Lisp_Object temp = stack[-i]; stack[-i] = stack[i+1-nargs]; stack[i+1-nargs] = temp; } for (i = 0; i<nargs; i++) { Lisp_Object w; pop(w); while (consp(w)) { push(w); nil = C_nil; if (!exception_pending()) r = cons(qcar(w), r); pop(w); w = qcdr(w); } } nil = C_nil; if (exception_pending()) return C_nil; return onevalue(nreverse(r)); } Lisp_Object Lappend_1(Lisp_Object nil, Lisp_Object a) { CSL_IGNORE(nil); return onevalue(a); } #endif /* COMMON */ Lisp_Object Lappend(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { Lisp_Object r = nil; push(b); stackcheck2(1, a, r); while (consp(a)) { push(a); r = cons(qcar(a), r); pop(a); errexitn(1); a = qcdr(a); } pop(b); while (r != nil) { a = qcdr(r); qcdr(r) = b; b = r; r = a; } return onevalue(b); } Lisp_Object Ldelete(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { Lisp_Object r; push2(a, b); r = nil; if (is_symbol(a) || is_fixnum(a)) { while (consp(b)) { Lisp_Object q = qcar(b); if (q == stack[-1]) { b = qcdr(b); break; } stack[0] = qcdr(b); r = cons(qcar(b), r); errexitn(2); b = stack[0]; } } else { while (consp(b)) { Lisp_Object q = qcar(b); #ifdef COMMON if (cl_equal(q, a)) #else if (equal(q, a)) #endif { b = qcdr(b); break; } stack[0] = qcdr(b); r = cons(qcar(b), r); errexitn(2); b = stack[0]; a = stack[-1]; } } popv(2); while (r != nil) { Lisp_Object w = qcdr(r); qcdr(r) = b; b = r; r = w; } return onevalue(b); } Lisp_Object Ldeleq(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { Lisp_Object r; push2(a, b); r = nil; while (consp(b)) { Lisp_Object q = qcar(b); if (q == stack[-1]) { b = qcdr(b); break; } stack[0] = qcdr(b); r = cons(qcar(b), r); errexitn(2); b = stack[0]; } popv(2); while (r != nil) { Lisp_Object w = qcdr(r); qcdr(r) = b; b = r; r = w; } return onevalue(b); } Lisp_Object Lnconc(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { Lisp_Object c; CSL_IGNORE(nil); if (!consp(a)) return onevalue(b); c = a; for (;;) { Lisp_Object next = qcdr(c); if (!consp(next)) { qcdr(c) = b; return onevalue(a); } else c = next; } } /* #ifndef COMMON */ static Lisp_Object Lsubstq(Lisp_Object a, Lisp_Object b, Lisp_Object c) { Lisp_Object w, nil = C_nil; if (c == b) return onevalue(a); #ifdef CHECK_STACK if (check_stack(__FILE__,__LINE__)) return aerror("substq"); #endif stackcheck3(0, a, b, c); push3(a, b, c); if (c == b) { popv(2); pop(a); errexit(); return onevalue(a); } if (!consp(stack[0])) { pop(c); popv(2); return c; } w = Lsubstq(stack[-2], stack[-1], qcar(stack[0])); errexitn(3); pop2(c, b); a = stack[0]; stack[0] = w; w = Lsubstq(a, b, qcdr(c)); pop(a); errexit(); a = cons(a, w); errexit(); return onevalue(a); } Lisp_Object MS_CDECL Lsubst(Lisp_Object nil, int nargs, ...) { Lisp_Object w, a, b, c; va_list aa; argcheck(nargs, 3, "subst"); #ifdef CHECK_STACK if (check_stack(__FILE__,__LINE__)) return aerror("subst"); #endif va_start(aa, nargs); a = va_arg(aa, Lisp_Object); b = va_arg(aa, Lisp_Object); c = va_arg(aa, Lisp_Object); va_end(aa); if (c == b) return onevalue(a); if (is_symbol(b) || is_fixnum(b)) return Lsubstq(a, b, c); stackcheck3(0, a, b, c); push3(a, b, c); #ifdef COMMON if (cl_equal(c, b)) #else if (equal(c, b)) #endif { popv(2); pop(a); errexit(); return onevalue(a); } if (!consp(stack[0])) { pop(c); popv(2); return c; } w = Lsubst(nil, 3, stack[-2], stack[-1], qcar(stack[0])); errexitn(3); pop2(c, b); a = stack[0]; stack[0] = w; w = Lsubst(nil, 3, a, b, qcdr(c)); pop(a); errexit(); a = cons(a, w); errexit(); return onevalue(a); } /* #endif */ Lisp_Object Lsublis(Lisp_Object nil, Lisp_Object al, Lisp_Object x) { stackcheck2(0, al, x); errexit(); #ifdef CHECK_STACK if (check_stack(__FILE__,__LINE__)) return aerror("sublis"); #endif push5(al, x, al, nil, nil); #define carx stack[0] #define cdrx stack[-1] #define w stack[-2] #define x stack[-3] #define al stack[-4] for (;;) { if (!consp(w)) { if (!consp(x)) { Lisp_Object temp = x; popv(5); return temp; } carx = Lsublis(nil, al, qcar(x)); errexitn(5); cdrx = Lsublis(nil, al, qcdr(x)); errexitn(5); if (carx == qcar(x) && cdrx == qcdr(x)) { Lisp_Object temp = x; popv(5); return temp; } else { Lisp_Object a1 = carx, a2 = cdrx; popv(5); return cons(a1, a2); } } { Lisp_Object temp = qcar(w); if (consp(temp)) { Lisp_Object v = qcar(temp); #ifdef COMMON if (cl_equal(v, x)) #else if (equal(v, x)) #endif { temp = qcdr(temp); popv(5); return temp; } } } w = qcdr(w); } } #undef carx #undef cdrx #undef w #undef x #undef al Lisp_Object Lsubla(Lisp_Object nil, Lisp_Object al, Lisp_Object x) /* * as sublis, but uses eq test rather than equal */ { stackcheck2(0, al, x); errexit(); #ifdef CHECK_STACK if (check_stack(__FILE__,__LINE__)) return aerror("subla"); #endif push5(al, x, al, nil, nil); #define carx stack[0] #define cdrx stack[-1] #define w stack[-2] #define x stack[-3] #define al stack[-4] for (;;) { if (!consp(w)) { if (!consp(x)) { Lisp_Object temp = x; popv(5); return temp; } carx = Lsubla(nil, al, qcar(x)); errexitn(5); cdrx = Lsubla(nil, al, qcdr(x)); errexitn(5); if (carx == qcar(x) && cdrx == qcdr(x)) { Lisp_Object temp = x; popv(5); return temp; } else { Lisp_Object a1 = carx, a2 = cdrx; popv(5); return cons(a1, a2); } } { Lisp_Object temp = qcar(w); if (consp(temp)) { Lisp_Object v = qcar(temp); if (v == x) { temp = qcdr(temp); popv(5); return temp; } } } w = qcdr(w); } } #undef carx #undef cdrx #undef w #undef x #undef al setup_type const funcs2_setup[] = { {"assoc", too_few_2, Lassoc, wrong_no_2}, /* * assoc** is expected to remain as the Standard Lisp version even if in * a Common Lisp world I redefine assoc to be someting messier. xassoc was * an earlier name I used for the same purpose, and is being withdrawn. */ {"assoc**", too_few_2, Lassoc, wrong_no_2}, {"xassoc", too_few_2, Lassoc, wrong_no_2}, {"atsoc", too_few_2, Latsoc, wrong_no_2}, {"member", too_few_2, Lmember, wrong_no_2}, {"member**", too_few_2, Lmember, wrong_no_2}, {"memq", too_few_2, Lmemq, wrong_no_2}, {"contained", too_few_2, Lcontained, wrong_no_2}, {"restart-csl", Lrestart_csl, Lrestart_csl2, wrong_no_1}, {"eq", too_few_2, Leq, wrong_no_2}, {"iequal", too_few_2, Leq, wrong_no_2}, {"eqcar", too_few_2, Leqcar, wrong_no_2}, {"equalcar", too_few_2, Lequalcar, wrong_no_2}, {"eql", too_few_2, Leql, wrong_no_2}, {"equalp", too_few_2, Lequalp, wrong_no_2}, {"endp", Lendp, too_many_1, wrong_no_1}, {"getd", Lgetd, too_many_1, wrong_no_1}, {"last", Llast, too_many_1, wrong_no_1}, {"lastpair", Llastpair, too_many_1, wrong_no_1}, {"length", Llength, too_many_1, wrong_no_1}, {"make-bps", Lget_bps, too_many_1, wrong_no_1}, {"make-native", Lget_native, too_many_1, wrong_no_1}, {"symbol-env", Lsymbol_env, too_many_1, wrong_no_1}, {"symbol-make-fastget", Lsymbol_make_fastget1, Lsymbol_make_fastget, wrong_no_2}, {"symbol-fastgets", Lsymbol_fastgets, too_many_1, wrong_no_1}, {"symbol-fn-cell", Lsymbol_fn_cell, too_many_1, wrong_no_1}, {"symbol-argcode", Lsymbol_argcount, too_many_1, wrong_no_1}, {"symbol-restore-fns", Lsymbol_restore_fns, too_many_1, wrong_no_1}, {"symbol-argcount", Lsymbol_argcount, too_many_1, wrong_no_1}, {"symbol-set-env", too_few_2, Lsymbol_set_env, wrong_no_2}, {"symbol-set-native", wrong_no_na, wrong_no_nb, Lsymbol_set_native}, {"symbol-set-definition", too_few_2, Lsymbol_set_definition, wrong_no_2}, {"restore-c-code", Lrestore_c_code, too_many_1, wrong_no_1}, {"set-autoload", too_few_2, Lset_autoload, wrong_no_2}, {"remd", Lremd, too_many_1, wrong_no_1}, {"trace", Ltrace, too_many_1, wrong_no_1}, #ifdef JIT {"jit", Ljit, too_many_1, wrong_no_1}, #endif {"untrace", Luntrace, too_many_1, wrong_no_1}, {"trace-all", Ltrace_all, too_many_1, wrong_no_1}, {"double-execute", Ldouble, too_many_1, wrong_no_1}, {"undouble-execute", Lundouble, too_many_1, wrong_no_1}, {"macro-function", Lmacro_function, too_many_1, wrong_no_1}, {"symbol-name", Lsymbol_name, too_many_1, wrong_no_1}, {"plist", Lplist, too_many_1, wrong_no_1}, {"delete", too_few_2, Ldelete, wrong_no_2}, {"deleq", too_few_2, Ldeleq, wrong_no_2}, {"preserve", Lpreserve_1, Lpreserve, Lpreserve_0}, {"checkpoint", Lcheckpoint_1, Lcheckpoint, Lcheckpoint_0}, {"mkvect", Lmkvect, too_many_1, wrong_no_1}, {"nconc", too_few_2, Lnconc, wrong_no_2}, {"neq", too_few_2, Lneq, wrong_no_2}, {"not", Lnull, too_many_1, wrong_no_1}, {"null", Lnull, too_many_1, wrong_no_1}, {"reverse", Lreverse, too_many_1, wrong_no_1}, {"reversip", Lnreverse, Lnreverse2, wrong_no_1}, /* I make the name nreverse generally available as well as reversip */ {"nreverse", Lnreverse, Lnreverse2, wrong_no_1}, {"smemq", too_few_2, Lsmemq, wrong_no_2}, {"subla", too_few_2, Lsubla, wrong_no_2}, {"sublis", too_few_2, Lsublis, wrong_no_2}, {"subst", wrong_no_3a, wrong_no_3b, Lsubst}, {"symbol-protect", too_few_2, Lsymbol_protect, wrong_no_2}, #ifdef COMMON {"symbol-package", Lsymbol_package, too_many_1, wrong_no_1}, {"symbol-plist", Lplist, too_many_1, wrong_no_1}, {"append", Lappend_1, Lappend, Lappend_n}, /* * In Common Lisp mode I make EQUAL do what Common Lisp says it should, but * also have EQUALS that is much the same but which also descends vectors. */ {"equal", too_few_2, Lcl_equal, wrong_no_2}, {"equals", too_few_2, Lequal, wrong_no_2}, {"nreverse0", Lnreverse0, too_many_1, wrong_no_1}, #else {"append", too_few_2, Lappend, wrong_no_2}, /* In Standard Lisp mode EQUAL descends vectors (but does not case fold) */ /* I provide cl-equal to do what Common Lisp does. */ {"cl-equal", too_few_2, Lcl_equal, wrong_no_2}, {"equal", too_few_2, Lequal, wrong_no_2}, {"member", too_few_2, Lmember, wrong_no_2}, {"member", too_few_2, Lmember, wrong_no_2}, #endif {NULL, 0, 0, 0} }; /* end of fns2.c */