Skip to content

Instantly share code, notes, and snippets.

@cotto
Created May 27, 2011 15:21
Show Gist options
  • Save cotto/995467 to your computer and use it in GitHub Desktop.
Save cotto/995467 to your computer and use it in GitHub Desktop.
cps questions
list of questions about CPS and how it relates to M0
* goals *
implement cps fib in M0
* notes from the meeting *
need to add an optional signing chunk that signs everything except the signing chunk itself
- sign a zeroed version of the file
- signing chunk needs to be extensible so that it'll work fine once sha1 is broken
- storing algorithm as a name is more user-friendly
* allison question - when merging files, does the metadata keep track of which files the chunks came from?
-
* need to think about how m0b_merge will work.
* moving to continuations
- when you take a continuation that's later modified, you want those modifiations. The taken coninuation should be a pointer.
* look into dynasm - chromatic says it's a really good thing to look into
* a continuation is a context
* cps is slow IF
- taking a continuation takes a lot of cpu time
- taking a continuation takes a lot of memory
- taking a continuation should be a pointer copy
- what makes a continuation captures is that there's a pointer to it
- a potential GC optimization is to have a context-specific memory pool
* need to consider changing the name "context" to something else. It's not a stack. "Call frame" is a more univeral name.
* andrew's proposal is message-passing only, so each context is isolated from each other
- using message-passing exclusively means we're limited in the languages we can support.
- we need to support experimentation
- Async I/O is the better way since it can emulate synchronous I/O
* GC needs to play nice with M0, M0 needs to play nice with GC
- all M0 needs to know about is that GC is that it has chunks of memory
* for the existing Parrot, we need to move to a compile-time selected GC
- a possibility is to make GC a link-time thing by compiling all GCs to .o files and deciding when libparrot is linked
- equivalent to infnite gc is to modify tuning options to effectively infinite memory
* thinking about memory, there's a p5 targ op that sets up a temporary sv tied to the stack
- in M0, look at references and if they can't escape, make them temporary and tied to the lifetime of the call frame
- think about how to have M0 store escape and reference information tied to references
- if there are 4 possible storage types (global, local, possibly others) you can tag pointers in the variables table
- this involves some analysis, which is part of the purpose of M0
- a possibility is to add a lifespan arg to the malloc op
- need to think about what the GC interface to M0 looks like
- The M0 interface to malloc should be "here's n bytes you can write to and need to free"
* M0 should only require FFI if the running code explicitly uses it.
- the malloc and free ops should work fine in the absence of ffi
- there's another interface to grab a GCable chunk of memory, with flags to indicate stuff
- have extra arg to gc_malloc indicates interesting things such as scope, etc (not needed for all interpreters)
- need to make sure that the chunk doesn't get killed before it's returned (avoid infant mortality)
* in M1, there needs to be a differentiation between the compile-time lexpad, which determines where variables live, and the runtime lexpad, which stores the actual runtime values and of which there can be many
* invoke is just another vtable function
* what's needed for cps m0 fib
- resolve the name of the function
- in the common case, the callee is responsible for setting up the storage for arguments and return values
- if you inline, you're just operating on the registers directly
- the goal is to minimize memory copies
- a call (aka invoke) looks like:
-- set up a return continuation
- this can be as simple as setting the return pc and return chunk in the current context
-- caller executes goto_chunk
-- callee creates a new context
-- callee picks arguments out of the parent context
-- callee does its thing
-- callee puts return value into a register where the return context knows how to find it
-- callee gotos the return continuation (return pc and return chunk)
- when you call a chunk that returns, it doesn't create
- the important thing is that the M0 interpreter knows what pc to go to when a chunk is done
- there's an important distinction between call and return; call
- there should be no difference between invoke and return except that return doesn't change the calling context
* exceptions
- exception handlers should be static so that work only happens when an exception is thrown
- an exception handler is just an invokable object
- throwing an exception is just invoking an exception
- resuming an exception means invoking its calling context (the pc where it exploded + 1)
- when an exception handler can't handle an exception, it invokes its parent exception handler
- the topmost exception handler spits stuff to stderr
- exceptions need - invocation, arg passing, search for next handler
- throw op is M1, M0 is create context, lookup handler, invoke handler with exception
- current exception doesn't need to be a fixed register; it's always passed to handlers
- when setting up a context, exception handlers need to be set up to tailcall into the next register if they don't handle the exception
- when an exception is invoked, it looks up the call graph until it finds a context with a usable exception handler
- this means that exception handlers are very cheap when they're not used
* variables
- there's a static variables segment (from bytecode) and a runtime variables table
- the runtime variables table is for overflow from the register and lexicals
- this isn't optimal
- the new solution is to have a special overflow context for spilling
- to generate code to spill, put registers in the overflow context
- spilling acts like a stack. You spill all existing registers and put a pointer in the overflow context.
- the only special register that's copied in spilling
- if you repeat, it works and the original overflow context is further back
- despilling means restoring all registers *and* the overflow context register of the overflow context
* calling conventions
- callee is responsible
- signatures are an M1 thing
- requisite: make sure that it's possible to dereference into the parent context via M0 code and get/set values
- with that, we're ok
talk about:
- M0 fib
- roadmap
*
* a roadmap:
- m0b disassembler (can happen any time, also needs tests)
- fibonacci in M0
- glossy brochure for M0 - one page w\ motivation, implementation and plan for M0
- complete test coverage
- each op is tested
- complete branch coverage of the assembler and interpreter according to Devel::Cover
- "complete" means anything we can reasonably expect to hit
- final implementation of the M0 assembler and interpreter in C
- it's an option to use gcc extensions
- we also need an C89 or C99 version
- calculate the hash of a string using a non-trivial algorithm (CRC or something basic, not sha1 or md5)
- noop integration with libparrot
- interpreter is linked into libparrot
- assembler isn't needed except for developers
- implement I/O in M0
- implement a very simple PMC in M0 (noop, hello or int) that acts like a core PMC - set and retrieve a value
- TODO: define what the expected distribution method for Parrot-hosted code will be in the distant future
- run with the idea of m0b as the distribution format. See what happens and who balks.
-
-
-
-
-
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment