I have fixed the XPath performance issue (it now runs a little faster than XQuery) by the following two changes:
(a) correct the logic for SubscriptExpression.equals()
(b) introduce a lazily-evaluated hashcode on the Expression object and use this when comparing expressions for equality.
The hotspot (30% of execution time) is now AxisExpression.typeCheck(). I improved this slightly (c) by avoiding some of the checks if the contextItemType is a NodeTest. But I can gain pretty well 30% in bottom line execution time by (d) disabling the "checkPlausibility()" method, whose effect is almost entirely to check for "void" path expressions like child::attribute(). This sounds like a useful configuration option.
There are no other obvious hot-spots. The profiling identifies a few opportunities for getting a percentage point off here and there, for example by:
(e) In UnaryExpression and BinaryExpression, avoiding the general iteration over operands() when typechecking, and just typechecking the fixed child expressions explicitly
(f) avoiding scanning the expression tree to look for updating expressions if updating wasn't enabled in the query parser
These changes aren't appropriate in a maintenance release but I'll consider them for the next major release.