- Use three-address code
- Build cfg/basic-blocks from that
- Generate WASM!
- Done!!!
High-level steps to complete this plan:
- Convert AST to three-address code
- Scan thru three-address code (3AC) to form cfg
- [this is where I would insert optimizations on the basic blocks when I do some]
- Convert 3AC/cfg BACK to some kind of stack VR, probably tree structured?
- Convert this to WASM and emit! (probably hard)
Step 1 - substeps
- Strongly typed? - I would like this to possibly be checked at compile time
- Create a data structure for 3AC
- Needs an entire program type
- mapping of string names to 3AC functions
- function is tuple of (string -> Int) and [Instructions]
- Needs the actual 3AC type. I would love the requirements in the following description to
be enforced by Haskell's type system.
- A = B
$op$ C- A cannot be a literal
- B and C cannot be sub-expressions themselves - they must be literals or environment symbols
- A =
$literal$ - A cannot be a literal
- if [$symbol$ | B
$op$ C |$literal$ ] == false goto$label$ - B and C cannot be sub-expressions
- goto
$label$
- A = B
- Needs an entire program type
- Traverse AST to generate 3AC
- Fold across the list of functions that represents a program
- initial state = empty program
- run a stateful action that converts AST node to a function
- add this function to the current program
- Fold across the list of functions that represents a program
Item 3.1.2
- Initial state will be empty function generation state
- Need to keep track of variable names, label table, instruction #, and current list of instruction
- Somewhat similar to original bytecode, although hopefully easier?
type Local = String type InstResult = ([Inst], Local) instsE :: ExprAug t -> TacGenerator InstResult instsE (LitInt n) = do result <- nextLocal return ([Assignment (Literal n)], result)
Step 2 - "This shouldn't be too hard"
- Create CFG data structure