+** Much effort has been invested to make XEmacs Lisp faster:
+
+*** Many basic lisp operations are now faster.
+This is especially the case when running a Mule-enabled XEmacs.
+
+A general overhaul of the lisp engine should produce a speedup of 1.4
+in a Latin-1 XEmacs, and 2.1 in a Mule XEmacs. These numbers were
+obtained running `(byte-compile "simple.el")', which should be a
+pretty typical test of "pure" Lisp.
+
+*** Lisp hash tables have been re-implemented. The Common Lisp style
+hash table interface has been made standard, and moved from cl.el into
+fast C code (See the section on hash tables in the XEmacs Lisp
+Reference). A speedup factor of 3 can be expected with code that
+makes intensive use of hash tables.
+
+*** The garbage collector has been tuned, leading to a speedup of
+1.16.
+
+*** The family of functions that iterate over lists, like `memq', and
+`rassq', have been made a little faster (typically 1.3).
+
+*** Lisp function calls are faster, by approximately a factor of two.
+However, defining inline functions (via defsubst) still makes sense
+for tight loops.
+
+*** Finally, a few functions have had dramatic performance
+improvements. For example, `(last long-list)' is now 30 times faster.
+
+Of course, your mileage will vary.
+
+Many operations do not see any improvement. Surprisingly, running
+(font-lock-fontify-buffer) does not use the Lisp engine much at all.
+Speeding up your favorite slow operation is an excellent project to
+improve XEmacs. Don't forget to profile!
+
+** XEmacs finally has an automated test suite!
+Although this is not yet very sophisticated, it is already responsible
+for several important bug fixes in XEmacs. To try it out, simply use
+the makefile target `make check' after building XEmacs.
+
+** Hash tables have been reimplemented.
+As was pointed out above, the standard interface to hash tables is now
+the Common Lisp interface, as described in Common Lisp, the Language
+(CLtL2, by Steele). The older interface (functions with names
+containing the phrase `hashtable') will continue to work, but the
+preferred interface now has names containing the phrase `hash-table'.
+
+Here's the executive overview: create hash tables using
+make-hash-table, and use gethash, puthash, remhash, maphash and
+clrhash to manipulate entries in the hash table. See the (updated)
+Lisp Reference Manual for details.
+
+** Lisp code handles circular lists much more robustly.
+Many basic lisp functions used to loop forever when given a circular
+list, expecting you to C-g (quit) out of the loop. Now this is more
+likely to trigger a `circular-list' error. Printing a circular list
+now results in something like this:
+
+ (let ((x (cons 'foo 'foo)))
+ (setcdr x x)
+ x)
+ => (foo ... <circular list>)
+
+An extra bonus is that checking for circularities is not just
+friendlier, but actually faster than checking for C-g.
+
+** The new form `ignore-file-errors', similar to `ignore-errors' may
+be used as a short-hand for condition-case when you wish to ignore
+file-related error. For example:
+
+ (ignore-file-errors (delete-file "foo"))
+
+** The arguments to `locate-file' are now much more "lispy". As
+before, the usage is:
+
+ (locate-file FILENAME PATH-LIST &optional SUFFIXES MODE)
+
+Except that SUFFIXES are now a list of strings instead of a single,
+colon-separated string. MODE is now a symbol or a list of symbols
+(symbols `exists', `executable', `writable', and `readable' are
+supported) instead of an integer code. See the documentation for
+details.
+
+Of course, the old form is still accepted for backward compatibility.
+
+** `translate-region' has been improved in several ways. Its TABLE
+argument used to be a 256-character string. In addition to this, it
+can now also be a vector or a char-table (which is useful for Mule.)
+If TABLE a vector or a generic char-table, you can map characters to
+strings instead of to other characters. For instance:
+
+ (let ((table (make-char-table 'generic)))
+ (put-char-table ?a "the letter a" table)
+ (put-char-table ?b "" table)
+ (put-char-table ?c ?\n table)
+ (translate-region (point-min) (point-max) table))
+
+** The `keywordp' function now returns non-nil only on symbols
+interned in the global obarray. For example:
+
+ (keywordp (intern ":foo" [0]))
+ => nil
+ (keywordp (intern ":foo")) ; The same as (keywordp :foo)
+ => t
+
+This behaviour is compatible with other code which treats symbols
+beginning with colon as keywords only if they are interned in the
+global obarray. `keywordp' used to wrongly return t in both cases
+above.
+
+** The first argument to `intern-soft' may now also be a symbol, like
+with `unintern'. If given a symbol, `intern-soft' will look for that
+exact symbol rather than for any string. This is useful when you want
+to check whether a specific symbol is interned in an obarray, e.g.:
+
+ (intern "foo")
+ (intern-soft "foo")
+ => foo
+ (intern-soft (make-symbol "foo"))
+ => nil
+