summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSøren Sandmann Pedersen <ssp@redhat.com>2014-02-02 17:38:08 -0500
committerSøren Sandmann Pedersen <ssp@redhat.com>2014-02-02 17:38:08 -0500
commita3a06259fbb2b9d224467cdfbd793990f4bf81af (patch)
treefb8b285a793ecd67d4eab468a34e708993c8ea48
parentbe0983a56afc1bedf9e5cdc12e2891789891bba1 (diff)
TODO
-rw-r--r--TODO236
-rw-r--r--examples/pc.nl1
-rw-r--r--examples/popbug3.nl14
-rw-r--r--offsets.c6
4 files changed, 133 insertions, 124 deletions
diff --git a/TODO b/TODO
index 7e15a9f..8c3d8a6 100644
--- a/TODO
+++ b/TODO
@@ -1,119 +1,3 @@
-* Expressions with goto in them
-
- A simple example is
-
- F() { goto blah; }
-
- print 7 + F();
-
- @blah:;
-
- where we jump to blah, leaving 7 on the stack.
-
- There is another case, captured in popbug3.nl, where we keep
- returning from the same function. Each time the result is added to
- something popped from the stack. This results in a stack underflow.
-
- Part of the solution will have to be to keep a reference to the
- expression stack. This of course implies that popping the stack can't
- actually remove the stack entries.
-
- A different problem is if the stack references a variable. Should
- that variable be evaluated every time? My intuition says no. Consider
-
- ret: label = null;
-
- x = 0;
-
- F() { ret = back; @back: return 10; }
-
- print x + F();
-
- x++;
-
- if (x < 100)
- goto ret;
-
- if you do this, F() will return 100 times, but the evaluation of
- the 'x' in 'x + F()' will be skipped every time.
-
- It seems the expression stack will have to become a tree with heap
- allocated entries. An environment will then contain a pointer to a
- node of this tree.
-
- What does this mean for the bytecode? For example, an ADD operation
- should now take the two values on top of the stack and create a new
- value whose 'next' pointer is the same as the next pointer of the
- second value on the stack.
-
- Should this be done at the bytecode level? It may be that there
- should be separate 'dynamic' versions of all the stack ops. These
- might operate on a leaf pointer on the stack.
-
- In the case where we can detect that an expression doesn't escape,
- the array based stack could then be used. Is detection as simple as
- there being no function calls in the expression? And no
- statement-expressions, presumably. Not sure. This will have to be
- considered an optimization.
-
- With these changes, will it be possible to move the return address
- back onto the 'stack'? If so, the contents of an environment will
- look like this:
-
- env_t *outer;
- env_t *prev;
- stack_t *stack;
- [variables]
-
- A label consists of a code pointer, an environment, and a stack
- pointer. Jumping to a label consists of activating the
- environment, resetting the stack pointer, and then going to the
- code pointer.
-
- Is is in fact going to be the case that a label will *always* have
- a stack height of zero? Then jumping to a label just needs to set
- the stack pointer to NULL. Jumping into the middle of an expression
- that hasn't been activated seems ill-defined, which would indicate
- that the (expression) stack height is indeed NULL. (This probably
- then precludes having the return pointer stored on the stack).
-
- Preliminary conclusions:
-
- - expression stack must become a heap allocated tree
- - there is a global current_stack
-
- - environments need to contain
- - prev_env [the environment to restore after return]
- - outer_env [pointer to containing environment]
- - return address [where to jump when returning]
- - return stack [stack to restore when returning]
-
- = Expressions:
- - Operate on current stack
-
- = Call:
- - Allocate new env
- - Store current env as prev_env
- - Store closure env as outer_env
- - Store current stack as prev_stack
- - Set stack pointer to NULL
- - Set return address to current address
- - Jump to function
-
- = Returning:
- - push the return value on top of prev_stack
- - restore outer environment
- - set stack to prev_stack
- - jump to return address
-
- = Generating a label:
- - push env and code pointer
-
- = Goto:
- - restore env
- - set expression stack pointer to NULL
- - goto code pointer
-
- Short-term tasks:
- Test suite
@@ -520,6 +404,10 @@ These will need similar code, so some of it may be sharable.
type int_func = fn int32 -> int32;
+- Expressions with goto and the spaghetti stack is now done. However,
+ if we ever add support for statement expressions, we may need to
+ make it so that goto always sets the stack to NULL.
+
- Calling convention:
/* On top of the stack is the closure, followed by all
@@ -1003,6 +891,122 @@ These will need similar code, so some of it may be sharable.
DONE:
+* Expressions with goto in them
+
+ A simple example is
+
+ F() { goto blah; }
+
+ print 7 + F();
+
+ @blah:;
+
+ where we jump to blah, leaving 7 on the stack.
+
+ There is another case, captured in popbug3.nl, where we keep
+ returning from the same function. Each time the result is added to
+ something popped from the stack. This results in a stack underflow.
+
+ Part of the solution will have to be to keep a reference to the
+ expression stack. This of course implies that popping the stack can't
+ actually remove the stack entries.
+
+ A different problem is if the stack references a variable. Should
+ that variable be evaluated every time? My intuition says no. Consider
+
+ ret: label = null;
+
+ x = 0;
+
+ F() { ret = back; @back: return 10; }
+
+ print x + F();
+
+ x++;
+
+ if (x < 100)
+ goto ret;
+
+ if you do this, F() will return 100 times, but the evaluation of
+ the 'x' in 'x + F()' will be skipped every time.
+
+ It seems the expression stack will have to become a tree with heap
+ allocated entries. An environment will then contain a pointer to a
+ node of this tree.
+
+ What does this mean for the bytecode? For example, an ADD operation
+ should now take the two values on top of the stack and create a new
+ value whose 'next' pointer is the same as the next pointer of the
+ second value on the stack.
+
+ Should this be done at the bytecode level? It may be that there
+ should be separate 'dynamic' versions of all the stack ops. These
+ might operate on a leaf pointer on the stack.
+
+ In the case where we can detect that an expression doesn't escape,
+ the array based stack could then be used. Is detection as simple as
+ there being no function calls in the expression? And no
+ statement-expressions, presumably. Not sure. This will have to be
+ considered an optimization.
+
+ With these changes, will it be possible to move the return address
+ back onto the 'stack'? If so, the contents of an environment will
+ look like this:
+
+ env_t *outer;
+ env_t *prev;
+ stack_t *stack;
+ [variables]
+
+ A label consists of a code pointer, an environment, and a stack
+ pointer. Jumping to a label consists of activating the
+ environment, resetting the stack pointer, and then going to the
+ code pointer.
+
+ Is is in fact going to be the case that a label will *always* have
+ a stack height of zero? Then jumping to a label just needs to set
+ the stack pointer to NULL. Jumping into the middle of an expression
+ that hasn't been activated seems ill-defined, which would indicate
+ that the (expression) stack height is indeed NULL. (This probably
+ then precludes having the return pointer stored on the stack).
+
+ Preliminary conclusions:
+
+ - expression stack must become a heap allocated tree
+ - there is a global current_stack
+
+ - environments need to contain
+ - outer_env [pointer to containing environment]
+ - prev_env [the environment to restore after return]
+ - return address [where to jump when returning]
+ - return stack [stack to restore when returning]
+
+ = Expressions:
+ - Operate on current stack
+
+ = Call:
+ - Allocate new env
+ - Store current env as prev_env
+ - Store closure env as outer_env
+ - Store current stack as prev_stack
+ - Set stack pointer to NULL
+ - Set return address to current address
+ - Jump to function
+
+ = Returning:
+ - push the return value on top of prev_stack
+ - restore outer environment
+ - set stack to prev_stack
+ - jump to return address
+
+ = Generating a label:
+ - push env and code pointer
+
+ = Goto:
+ - restore env
+ - set expression stack pointer to NULL
+ - goto code pointer
+
- consolidate while and for into a new "loop" that takes two expressions,
a test, and an increment. then
diff --git a/examples/pc.nl b/examples/pc.nl
index 5a1243a..1382e0d 100644
--- a/examples/pc.nl
+++ b/examples/pc.nl
@@ -60,7 +60,6 @@ thread_exit()
goto process_exit;
}
-
/*
* Demonstration of cooperative thread system
*/
diff --git a/examples/popbug3.nl b/examples/popbug3.nl
index 5a8cd59..aebc7be 100644
--- a/examples/popbug3.nl
+++ b/examples/popbug3.nl
@@ -1,8 +1,10 @@
/* This program makes B return multiple times, and each time
- * the "7" is popped from the stack, causing the stack to become
- * underfull
+ * the "7" was popped from the stack, causing the stack to become
+ * underfull.
*/
+i: int32 = 10;
+
ret: label = null;
B() -> int32
@@ -11,11 +13,13 @@ B() -> int32
goto out;
@back:
- return 20;
+ return i;
}
-print 7 + B();
+print i + B();
@out:
print "asdf";
-goto ret;
+
+if (--i > 0)
+ goto ret;
diff --git a/offsets.c b/offsets.c
index ccdaa78..8627e59 100644
--- a/offsets.c
+++ b/offsets.c
@@ -119,9 +119,11 @@ do_offsets (ast_t *ast, int offset, int *max)
symbols = ast->definition.function.symbol_table;
/* FIXME: we should probably fix pointers at 32 bits - see TODO */
- offset = 3 * sizeof (gpointer); /* The 'outer' pointer,
+ offset = 4 * sizeof (gpointer); /* The 'outer' pointer,
* the old env,
- * the return address */
+ * the return address
+ * the return stack
+ */
function->env_size = offset;