Skip to content

Commit

Permalink
[MOD] XQuery: Tail-call optimization optimizations. #2314
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristianGruen committed Jul 13, 2024
1 parent 6cb355d commit a16dea2
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 24 deletions.
19 changes: 8 additions & 11 deletions basex-core/src/main/java/org/basex/query/QueryContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,8 @@ public final class QueryContext extends Job implements Closeable {

/** Available collations. */
public TokenObjMap<Collation> collations;

/** Number of successive tail calls. */
public int tailCalls;
/** Maximum number of successive tail calls (will be set before compilation). */
public int maxCalls;
/** Perform tail-call optimizations. */
public boolean tco;

/** Function for the next tail call. */
private XQFunction tcFunc;
Expand Down Expand Up @@ -275,7 +272,7 @@ public void compile() throws QueryException {
run(info.compiling, () -> {
// assign tail call option after compiling options
options.compile();
maxCalls = context.options.get(MainOptions.TAILCALLS);
tco = context.options.get(MainOptions.TAILCALLS) >= 0;

// bind external variables
if(parent == null) {
Expand Down Expand Up @@ -546,11 +543,11 @@ public void set(final Var var, final Value value) throws QueryException {
/**
* Registers a tail-called function and its arguments to this query context.
* @param fn function to call
* @param arg arguments to pass to {@code fn}
* @param args arguments to pass to {@code fn}
*/
public void registerTailCall(final XQFunction fn, final Value[] arg) {
public void registerTailCall(final XQFunction fn, final Value[] args) {
tcFunc = fn;
tcArgs = arg;
tcArgs = args;
}

/**
Expand All @@ -568,9 +565,9 @@ public XQFunction pollTailCall() {
* @return argument values if a tail call was registered, {@code null} otherwise
*/
public Value[] pollTailArgs() {
final Value[] as = tcArgs;
final Value[] args = tcArgs;
tcArgs = null;
return as;
return args;
}

/**
Expand Down
16 changes: 7 additions & 9 deletions basex-core/src/main/java/org/basex/query/func/XQFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ public interface XQFunction extends XQFunctionExpr {
default Value invoke(final QueryContext qc, final InputInfo info, final Value... args)
throws QueryException {

XQFunction fn = this;
Value[] values = args;
final int fp = qc.stack.enterFrame(fn.stackFrameSize());
final int fp = qc.stack.enterFrame(stackFrameSize());
try {
XQFunction fn = this;
Value[] values = args;
while(true) {
qc.checkStop();
final Value value = fn.invokeInternal(qc, info, values);
fn = qc.pollTailCall();
if(fn == null) return value;
qc.stack.reuseFrame(fn.stackFrameSize());
values = qc.pollTailArgs();
qc.stack.reuseFrame(fn.stackFrameSize());
}
} catch(final QueryException ex) {
throw ex.add(info);
Expand All @@ -56,21 +56,19 @@ default Value invokeTail(final QueryContext qc, final InputInfo info, final Valu
throws QueryException {

qc.checkStop();
final int calls = qc.tailCalls, max = qc.maxCalls;
if(max >= 0 && calls >= max) {
final int size = stackFrameSize();
if(qc.tco && qc.stack.tco(size)) {
// too many tail calls on the stack, eliminate them
qc.registerTailCall(this, args);
return Empty.VALUE;
}

qc.tailCalls++;
final int fp = qc.stack.enterFrame(stackFrameSize());
final int fp = qc.stack.enterFrame(size);
try {
return invokeInternal(qc, info, args);
} catch(final QueryException ex) {
throw ex.add(info);
} finally {
qc.tailCalls = calls;
qc.stack.exitFrame(fp);
}
}
Expand Down
17 changes: 13 additions & 4 deletions basex-core/src/main/java/org/basex/query/var/QueryStack.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*/
public final class QueryStack {
/** Initial stack size. */
private static final int INIT = 1 << 3;
private static final int INIT = 1 << 5;
/** The currently assigned values. */
private Value[] stack = new Value[INIT];
/** The currently assigned variables. */
Expand Down Expand Up @@ -50,21 +50,30 @@ public void reuseFrame(final int size) {

/**
* Exits a stack frame and makes all bound variables eligible for garbage collection.
* @param frame frame pointer of the underlying stack frame
* @param fp frame pointer of the underlying stack frame
*/
public void exitFrame(final int frame) {
public void exitFrame(final int fp) {
final int s = start;
final Value[] stck = stack;
for(int en = end; --en >= s;) stck[en] = null;
end = s;
start = frame;
start = fp;

final int sl = stck.length;
int len = sl;
while(len > INIT && sl <= len >> 2) len >>= 1;
if(len != sl) resize(len);
}

/**
* Checks if tail calls should be eliminated.
* @param size new frame size
* @return result of check
*/
public boolean tco(final int size) {
return end + (size << 1) > INIT;
}

/**
* Ensures that the query stack has at least the given size.
* @param size required stack size
Expand Down

0 comments on commit a16dea2

Please sign in to comment.