Commit a8e678eb authored by Per Bothner's avatar Per Bothner

Initial check-in of Andrea's call/cc implementation.

parent 6433e670
2015-07-07 Andrea Bernardini <andrebask@gmail.com>
* build.xml (java-classes): Add gnu/expr/continuations.
2015-06-03 Per Bothner <per@bothner.com>
* build.xml (kawa.sh): Add dependency on build-settings.
......
......@@ -503,6 +503,7 @@
<exclude name="gnu/expr/KawaScriptBindings.java" unless="has-java6"/>
<exclude name="gnu/expr/KawaScriptEngine.java" unless="has-java6"/>
<exclude name="kawa/standard/SchemeScriptEngineFactory.java" unless="has-java6"/>
<include name="gnu/expr/continuations/"/>
<include name="gnu/kawa/functions/"/>
<include name="gnu/kawa/lispexpr/*.java"/>
<exclude name="gnu/kawa/lispexpr/MakeXmlElement.java" unless="${enable-xml}"/>
......
2015-07-07 Andrea Bernardini <andrebask@gmail.com>
* Makefile.am: Add sources for the new package gnu/expr/continuations.
2014-08-31 Per Bothner <per@bothner.com>
* Makefile.am: Merge Makefile.am fragments from
......
......@@ -73,6 +73,13 @@ GNU_EXPR_JSOURCES = \
expr/TypeValue.java \
expr/VarValueTracker.java
GNU_CONTINUATIONS_JSOURCES = \
expr/continuations/Helpers.java \
expr/continuations/CallCC.java \
expr/continuations/Continuation.java \
expr/continuations/TopLevelHandler.java \
expr/FragmentAndInstrument.java
JAVAX_SCRIPT_EXTRA = \
expr/AbstractScriptEngineFactory.java \
expr/KawaScriptBindings.java \
......@@ -541,7 +548,7 @@ EXTRA_DIST = \
text/package.html \
kawa/reflect/package.html \
$(GNU_KAWA_ANT_JSOURCES)
EXTRA_CLEAN = */*.class kawa/*/*.class
EXTRA_CLEAN = */*.class */*/*.class kawa/*/*.class
PACKAGE_FNAME = gnu
......@@ -550,6 +557,7 @@ java_sources = \
$(GNU_TEXT_JSOURCES) \
$(GNU_MAPPING_JSOURCES) \
$(GNU_EXPR_JSOURCES) \
$(GNU_CONTINUATIONS_JSOURCES) \
$(GNU_KAWA_IO_JSOURCES) \
$(GNU_KAWA_UTIL_JSOURCES) \
$(GNU_KAWA_REFLECT_JSOURCES) \
......
......@@ -312,6 +312,13 @@ GNU_EXPR_JSOURCES = \
expr/TypeValue.java \
expr/VarValueTracker.java
GNU_CONTINUATIONS_JSOURCES = \
expr/continuations/Helpers.java \
expr/continuations/CallCC.java \
expr/continuations/Continuation.java \
expr/continuations/TopLevelHandler.java \
expr/FragmentAndInstrument.java
JAVAX_SCRIPT_EXTRA = \
expr/AbstractScriptEngineFactory.java \
expr/KawaScriptBindings.java \
......@@ -780,10 +787,11 @@ EXTRA_DIST = \
kawa/reflect/package.html \
$(GNU_KAWA_ANT_JSOURCES)
EXTRA_CLEAN = */*.class kawa/*/*.class
EXTRA_CLEAN = */*.class */*/*.class kawa/*/*.class
PACKAGE_FNAME = gnu
java_sources = $(GNU_LISTS_JSOURCES) $(GNU_TEXT_JSOURCES) \
$(GNU_MAPPING_JSOURCES) $(GNU_EXPR_JSOURCES) \
$(GNU_CONTINUATIONS_JSOURCES) \
$(GNU_KAWA_IO_JSOURCES) $(GNU_KAWA_UTIL_JSOURCES) \
$(GNU_KAWA_REFLECT_JSOURCES) $(GNU_KAWA_FUNCTIONS_JSOURCES) \
$(GNU_KAWA_LISPEXPR_JSOURCES) $(GNU_KAWA_MODELS_JSOURCES) \
......
This diff is collapsed.
2015-07-07 Andrea Bernardini <andrebask@gmail.com>
* Compilation.java: Move again ANormalize pass before InlineCalls.
Add options to enable first class continuations.
(typeGenericProc): New field of type ClassType.
* ANormalize.java: Add custom normalization code for splice and
typeswitch operators, do loop, and define-procedure.
(visitSetExp): Optimize letrec-like forms.
* FragmentAndInstrument.java: New Class. New Visitor that performs the
fragmentation and instrumentation needed to capture and resume first
class continuations.
* QuoteExp.java (validateApply): Disable inlining for fragments.
* continuations: New package.
2015-06-24 Per Bothner <per@bothner.com>
* LambdaExp.java (reverseChildList, pushChild): New methods.
......
......@@ -131,11 +131,18 @@ public class Compilation implements SourceLocator
public static boolean debugPrintANF = false;
public static boolean enableANF = false;
public static boolean debugPrintInstr = false;
public static boolean fullContinuations = false;
public static Options options = new Options();
public static Options.OptionInfo fullTailCallsVariable =
options.add("full-tailcalls",
Options.BOOLEAN_OPTION, Boolean.TRUE,
"support full tailcalls");
public static Options.OptionInfo fullContinuationsVariable =
options.add("full-continuations",
Options.BOOLEAN_OPTION, Boolean.FALSE,
"support first class continuations");
public static Options.OptionInfo mainMethodVariable =
options.add("main",
Options.BOOLEAN_OPTION, Boolean.FALSE,
......@@ -175,6 +182,11 @@ public class Compilation implements SourceLocator
return currentOptions.getBoolean(mainMethodVariable);
}
public boolean fullContinuations ()
{
return currentOptions.getBoolean(fullContinuationsVariable);
}
public boolean warnUnreachable ()
{
return currentOptions.getBoolean(warnUnreachable);
......@@ -197,7 +209,8 @@ public class Compilation implements SourceLocator
}
public boolean warnVoidUsed()
{
return (!enableANF) && currentOptions.getBoolean(warnVoidUsed);
return (!enableANF) && (!fullContinuations())
&& currentOptions.getBoolean(warnVoidUsed);
}
public boolean warnAsError ()
{
......@@ -428,6 +441,8 @@ public class Compilation implements SourceLocator
/* Classes, fields, and methods used wgen usingCPStyle". */
public static ClassType typeCallContext
= ClassType.make("gnu.mapping.CallContext");
public static ClassType typeGenericProc
= ClassType.make("gnu.expr.GenericProc");
public static final ClassType typeConsumer
= ClassType.make("gnu.lists.Consumer");
public static Method getCallContextInstanceMethod
......@@ -1853,8 +1868,7 @@ public class Compilation implements SourceLocator
if (wantedState >= WALKED && getState() < WALKED)
{
InlineCalls.inlineCalls(mexp, this);
if (enableANF)
if (enableANF || fullContinuations())
ANormalize.aNormalize(mexp, this);
if (debugPrintANF) {
options.set("warn-void-used", Boolean.FALSE);
......@@ -1865,6 +1879,17 @@ public class Compilation implements SourceLocator
dout.println(']');
dout.flush();
}
if (fullContinuations())
FragmentAndInstrument.fragmentCode(mexp, this);
if (debugPrintInstr) {
OutPort dout = OutPort.errDefault();
dout.println ("[Instrumented module: "+mexp.getName()
+ " to " + mainClass.getName() + ":");
mexp.print(dout);
dout.println(']');
dout.flush();
}
InlineCalls.inlineCalls(mexp, this);
ChainLambdas.chainLambdas(mexp, this);
FindTailCalls.findTailCalls(mexp, this);
setState(messages.seenErrors() ? ERROR_SEEN : WALKED);
......
This diff is collapsed.
......@@ -316,7 +316,7 @@ public class PrimProcedure extends MethodProc {
public PrimProcedure (String className, String methodName, int numArgs)
{
this(ClassType.make(className).getDeclaredMethod(methodName, numArgs));
this(ClassType.make(className).getDeclaredMethod(methodName, numArgs));
}
public PrimProcedure(java.lang.reflect.Method method, Language language)
......
......@@ -161,7 +161,19 @@ public class QuoteExp extends Expression
spliceCount>0);
if (msg != null)
return visitor.noteError(msg);
Expression inlined = visitor.maybeInline(exp, required, proc);
Expression inlined;
if (! visitor.comp.fullContinuations()) {
inlined = visitor.maybeInline(exp, required, proc);
} else if (exp.args != null && exp.args.length > 0
&& exp.args[0] instanceof ReferenceExp
&& exp.args[0].toString().contains("continue-fragment"))
inlined = null;
else inlined = visitor.maybeInline(exp, required, proc);
// Expression inlined = (visitor.comp.fullContinuations())
// ? null
// : visitor.maybeInline(exp, required, proc);
if (inlined != null)
return inlined;
Expression[] args = exp.args;
......
package gnu.expr.continuations;
import gnu.bytecode.*;
import gnu.expr.*;
import gnu.expr.continuations.Helpers.*;
import gnu.mapping.Procedure;
import gnu.mapping.Procedure1;
/**
* Implementation of the call-with-current-continuation function.
*
* @author Andrea Bernardini <andrebask@gmail.com>
*/
public class CallCC extends Procedure1 {
public static final CallCC callcc = new CallCC();
public Object apply1(Object arg1) throws Throwable {
return call_cc((Procedure) arg1);
}
public void compile(ApplyExp exp, Compilation comp, Target target) {
CodeAttr code = comp.getCode();
Method callccMethod = ClassType.make("gnu.expr.continuations.CallCC")
.getDeclaredStaticMethod("call_cc", 1);
exp.getArg(1).compile(comp, ClassType.make("gnu.mapping.Procedure"));
code.emitInvokeStatic(callccMethod);
}
/**
* Throws a ContinuationException starting to unwind the stack.
* A call to the first computation step of the lambda function passed
* to the call/cc is enclosed in a ContinuationFrame, that is stored into
* the frames list of the ContinuationException.
*/
public static Object call_cc(final Procedure receiver) throws ContinuationException {
try {
// begin unwind the stack
throw new ContinuationException();
} catch (ContinuationException sce) {
sce.extend(new ContinuationFrame(receiver));
throw sce;
}
}
}
2015-07-07 Andrea Bernardini <andrebask@gmail.com>
New package.
* CallCC.java: New class. Extends Procedure1. Implementation
of the call/cc function.
* Continuation.java: New class. Extends Procedure1. Representation
of the continuation object.
* Helpers.java: New class. Support code.
* TopLevelHandler.java: New class. Extends Procedure1. Install
the excepetion handlers needed to capture and resume continuations.
package gnu.expr.continuations;
import gnu.bytecode.Type;
import gnu.bytecode.ClassType;
import gnu.mapping.Procedure0or1;
import gnu.mapping.Values;
import gnu.expr.continuations.Helpers.ContinuationFrame;
import gnu.expr.continuations.Helpers.ContinuationException;
import gnu.expr.continuations.Helpers.ExitException;
import gnu.mapping.Procedure;
import gnu.mapping.Procedure1;
import java.util.ArrayList;
/**
* A Procedure that implements an invokable reified Continuation.
*
* @author Andrea Bernardini <andrebask@gmail.com>
*/
public class Continuation extends Procedure0or1 {
// The type of a continuation
public static Type typeContinuation = ClassType.make(Continuation.class);
// Holds the list of frames that form the continuation
ArrayList<ContinuationFrame> frames;
/**
* Assembles the new frames and the already assembled frames
* into a Continuation Object.
*/
public Continuation(ArrayList<ContinuationFrame> newFrames,
ArrayList<ContinuationFrame> oldFrames) {
frames = (oldFrames != null) ? new ArrayList<ContinuationFrame>(oldFrames)
: new ArrayList<ContinuationFrame>();
// The new frames are appended one by one to the old_frames
// while setting their continuation to the list of frames below.
for(int i = newFrames.size()-1; i >= 0; i--) {
ContinuationFrame newFrame = newFrames.get(i);
if (newFrame.continuation != null) {
throw new Error("Continuation should be empty here");
}
newFrame.continuation = new ArrayList<ContinuationFrame>(frames);
frames.add(newFrame);
}
}
public Object apply0() throws Throwable {
return apply1(Values.empty);
}
public Object apply1(final Object val) throws Throwable {
// When invoked, a continuation does not returns
// (Actually it is not a function). The call has
// the effect of exiting from the current execution
// context and re-establishing the continuation stored
// in the Continuation Object (It replaces the stack).
Procedure t = new Procedure1() {
public Object apply1(Object ignored) throws Throwable {
// reload the continuation.
return reloadFrames(frames.size()-2, val);
}
};
// We use an Exception to exit.
throw new ExitException(t);
}
/**
* Resumes the current continuation.
* When the call/cc is called, the stack is unwinded and saved
* on the heap. Immediately after the call/cc call, the current
* continuation is resumed using this function. see also {@see
* gnu.expr.continuations.TopLevelHandler}.
*/
Object resume(final Object restartValue) throws Throwable {
return reloadFrames(frames.size()-1, restartValue);
}
/**
* Performs the actual reloading.
* Iterates over the list of frames in reverse order to re-establish
* the saved continuation with the passed value.
*/
Object reloadFrames(int endIndex, Object restartValue) throws Throwable {
Object continueValue = restartValue;
for (int i = endIndex; i >= 0; i -= 1) {
ContinuationFrame frame = frames.get(i);
try {
continueValue = frame.computation.apply1(continueValue);
} catch (ContinuationException sce) {
sce.append(frame.continuation);
throw sce;
}
}
return continueValue;
}
public String toString () {
return "#<continuation" + /*this.hashCode() +*/ ">";
}
}
package gnu.expr.continuations;
import gnu.mapping.Procedure;
import java.util.ArrayList;
/**
* Support code for the call/cc implementation.
*
* @author Andrea Bernardini <andrebask@gmail.com>
*/
public class Helpers {
/**
* A computation step with its continuation
* (as a list of ContinuationFrames).
*/
public static class ContinuationFrame {
Procedure computation;
ArrayList<ContinuationFrame> continuation;
public ContinuationFrame(Procedure frame) {
computation = frame;
}
}
static class FastException extends Exception {
@Override
public Throwable fillInStackTrace() {
return this;
}
}
/**
* An Exception used to exit the current execution context
* and containing a thunk that reloads a continuation.
*/
public static class ExitException extends FastException {
Procedure thunk;
public ExitException(Procedure thunk) {
this.thunk = thunk;
}
}
/**
* An Exception used to store in order the computation steps, to be
* later stored in a Continuation Object.
*/
public static class ContinuationException extends FastException {
// A list of frames that have not yet been assembled into the
// continuation.
ArrayList<ContinuationFrame> newCapturedFrames = new ArrayList<ContinuationFrame>();
// A list of frames that already have been assembled into the continuation.
// When unloading the stack, we don't need to unload these frames.
ArrayList<ContinuationFrame> reloadedFrames;
/**
* Push a newly created Frame onto the list of frames that need
* to be assembled into the continuation. This will be done in the
* top level exception handler.
*/
public void extend(ContinuationFrame extension) {
newCapturedFrames.add(extension);
}
/**
* Append the tail of the current continuation to the exception
* Object so that the handler can assemble the new frames onto it.
*/
public void append(ArrayList<ContinuationFrame> oldFrames) {
reloadedFrames = oldFrames;
}
/**
* Assemble and return the continuation.
*/
public Continuation toContinuation() throws Exception {
return new Continuation(newCapturedFrames, reloadedFrames);
}
}
public static void extend(ContinuationException c, ContinuationFrame extension){
c.extend(extension);
}
}
package gnu.expr.continuations;
import gnu.expr.*;
import gnu.bytecode.*;
import gnu.expr.continuations.Helpers.ExitException;
import gnu.expr.continuations.Helpers.ContinuationException;
import gnu.mapping.Procedure;
import gnu.mapping.Procedure1;
/**
* A Procedure to run top level expressions in an Exception handler,
* that resumes captured contiunations or re-establish previously
* saved continuations.
*
* @author Andrea Bernardini <andrebask@gmail.com>
*/
public class TopLevelHandler extends Procedure1 {
public static final TopLevelHandler topLevelHandler = new TopLevelHandler();
public Object apply1(Object arg1) throws Throwable {
return runInTopLevelHandler((Procedure) arg1);
}
public void compile(ApplyExp exp, Compilation comp, Target target) {
CodeAttr code = comp.getCode();
Method initMethod = ClassType.make("gnu.expr.continuations.TopLevelHandler")
.getDeclaredStaticMethod("runInTopLevelHandler", 1);
exp.getArg(1).compile(comp, ClassType.make("gnu.mapping.Procedure"));
code.emitInvokeStatic(initMethod);
}
/**
* Runs inside an exception handler the first computation of a top
* level expression, managing the capture of continuations and the
* invocation of saved continuation.
*/
public static Object runInTopLevelHandler(Procedure initialFrame) throws Throwable {
// runs the received frames until one returns.
while (true) {
try {
return invokeFrame(initialFrame);
} catch (ExitException rce) {
// a capture or a continaution invocation
// exited the previous execution context
// and requires the top level handler to
// run a thunk that re-establish an other
// continuation. At the next cycle of the
// loop we will run the received thunk.
initialFrame = rce.thunk;
}
}
}
private static Object invokeFrame(final Procedure initialFrame) throws Throwable {
try {
// invoke the first computation of a top level expression.
return initialFrame.apply1(null);
} catch (ContinuationException sce) {
// assemble the list of frames in a Continution Object.
final Continuation k = sce.toContinuation();
// send to the top level handler a computation that will
// resume the captured continuation.
Procedure f = new Procedure1() {
public Object apply1(Object arg) throws Throwable {
return k.resume(k);
}
};
throw new ExitException(f);
}
}
}
......@@ -543,10 +543,10 @@ public class CompileMisc
&& (op == NumberPredicate.ODD || op == NumberPredicate.EVEN)) {
Expression arg0 = args[0];
int kind = Arithmetic.classifyType(arg0.getType());
CodeAttr code = comp.getCode();
if (kind <= Arithmetic.INTNUM_CODE) {
PrimType wtype = Type.intType;
Target wtarget = StackTarget.getInstance(wtype);
CodeAttr code = comp.getCode();
if (op == NumberPredicate.EVEN)
code.emitPushInt(1);
arg0.compile(comp, wtarget);
......@@ -554,17 +554,10 @@ public class CompileMisc
code.emitAnd();
if (op == NumberPredicate.EVEN)
code.emitSub(Type.intType);
} else {
arg0.compile(comp, Target.pushObject);
String mname = op == NumberPredicate.EVEN ? "isEven" : "isOdd";
Method m = ClassType
.make("gnu.kawa.functions.NumberPredicate")
.getDeclaredMethod(mname, 1);
code.emitInvokeStatic(m);
}
target.compileFromStack(comp, Type.booleanType);
return true;
}
}
return false;
}
......
......@@ -19,6 +19,11 @@ public class CharArrayOutPort extends OutPort
super(null, printPretty, false, path);
}
public CharArrayOutPort(boolean printPretty)
{
super(null, printPretty, CharArrayInPort.stringPath);
}
public int length ()
{
return bout.bufferFillPointer;
......
2015-07-07 Andrea Bernardini <andrebask@gmail.com>
* lang/Macro.java (typeMacro): New field.
(makeHygienic): New field.
(makeNonHygienic): New field.
(makeSkipScanForm): New field.
(setCapturedScope): New field.
Initialization code for the new fields.
* standard/define_syntax.java (typeMacro): Moved to lang/Macro.java.
(makeHygienic): Moved to lang/Macro.java.
(makeNonHygienic): Moved to lang/Macro.java.
(makeSkipScanForm): Moved to lang/Macro.java.
(setCapturedScope): Moved to lang/Macro.java
* repl.java: Add --full-continuations and --debug-print-instr options.
* standard/define.java (rewriteForm): Use typeGenericProc field declared
in Compilation, instead of creating a new ClassType object using
getDeclaredMethod.
Use GenericProc.add instead of creating a new ClassType object..
2015-06-24 Per Bothner <per@bothner.com>
* standard/object.java: Simplify chaining of methods by
......
package kawa.lang;
import gnu.bytecode.ClassType;
import gnu.expr.*;
import gnu.mapping.*;
import gnu.lists.*;
......@@ -11,6 +12,22 @@ public class Macro extends Syntax implements Printable, Externalizable
Object instance;
public static ClassType typeMacro = ClassType.make("kawa.lang.Macro");
public static PrimProcedure makeHygienic
= new PrimProcedure(typeMacro.getDeclaredMethod("make", 3));
public static PrimProcedure makeNonHygienic
= new PrimProcedure(typeMacro.getDeclaredMethod("makeNonHygienic", 3));
public static PrimProcedure makeSkipScanForm
= new PrimProcedure(typeMacro.getDeclaredMethod("makeSkipScanForm", 3));
public static PrimProcedure setCapturedScope
= new PrimProcedure(typeMacro.getDeclaredMethod("setCapturedScope", 1));
static {
makeHygienic.setSideEffectFree();
makeNonHygienic.setSideEffectFree();
makeSkipScanForm.setSideEffectFree();
}
public static final int HYGIENIC = 1;
/** If this flag is set, then don't expand during the scan-body phase. */
public static final int SKIP_SCAN_FORM = 2;
......
2015-07-07 Andrea Bernardini <andrebask@gmail.com>
* normalized_map.scm: New file, tail recursive implementation of
map and for/each in scheme. It is used insted of the java version
when first class continuations are enabled.
* Makefile.am: Add the new file normalized_map.scm.
* prim_syntax (if): The optimization of the 'and' form, that uses a
BlockExp and an ExitExp, conflicts with the instrumentation required
by first class continuations. Disable the optimization when first
class continuations are enabled.
(rewrite-if-and): New function.
2015-06-24 Per Bothner <per@bothner.com>
* exceptions.scm (guard): Ignore if ExitCalled is caught.
......
......@@ -16,6 +16,7 @@ java_SCM_ALWAYS =\
exceptions.scm \
kawa/expressions.scm \
compile_misc.scm compile_map.scm \
normalized_map.scm \
thread.scm \
characters.scm \
keywords.scm \
......
......@@ -247,6 +247,7 @@ java_SCM_ALWAYS = \
exceptions.scm \
kawa/expressions.scm \
compile_misc.scm compile_map.scm \
normalized_map.scm \
thread.scm \
characters.scm \
keywords.scm \
......
(module-compile-options full-continuations: #t)
(require <kawa.lib.prim_syntax>)
(require <kawa.lib.std_syntax>)
(require <kawa.lib.lists>)
(define map/cc
(case-lambda
((f l) (map1 f l))
((f l . rest) (apply mapn f (cons l rest)))))
(define (map1 f l)
(if (null? l)
'()
(let ((res (cons (f (car l)) '())))
(map-acc f (cdr l) res)
res)))
(define (map-acc f l acc)
(when (not (null? l))
(let ((t (cons (f (car l)) '())))
(set-cdr! acc t)
(map-acc f (cdr l) t))))
(define (mapn f . l)
(if (null? (car l))
'()
(let ((res (cons (apply f (map1 car l)) '())))
(apply mapn-acc res f (map1 cdr l))
res)))
(define (mapn-acc acc f . l)
(when (not (null? (car l)))
(let ((t (cons (apply f (map1