diff options
author | Søren Sandmann Pedersen <ssp@redhat.com> | 2014-02-02 17:38:08 -0500 |
---|---|---|
committer | Søren Sandmann Pedersen <ssp@redhat.com> | 2014-02-02 17:38:08 -0500 |
commit | a3a06259fbb2b9d224467cdfbd793990f4bf81af (patch) | |
tree | fb8b285a793ecd67d4eab468a34e708993c8ea48 | |
parent | be0983a56afc1bedf9e5cdc12e2891789891bba1 (diff) |
TODO
-rw-r--r-- | TODO | 236 | ||||
-rw-r--r-- | examples/pc.nl | 1 | ||||
-rw-r--r-- | examples/popbug3.nl | 14 | ||||
-rw-r--r-- | offsets.c | 6 |
4 files changed, 133 insertions, 124 deletions
@@ -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; @@ -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; |