Skip to content

Commit

Permalink
remove low-level subroutines (#17)
Browse files Browse the repository at this point in the history
## Summary

Remove subroutine support from the VM and L3-0. This significantly
simplifies the VM and brings the target languages closer together, thus
reducing future work and simplifying the overall compiler architecture.

## Details

* remove the subroutine-related VM instructions
* simplify the VM's exception handling; the dedicated EH code is
  obsolete and thus removed
* adjust the `err_goto` grammar and associated tests
* remove the `Subroutine` grammar from L3-0
* remove the now-unused `Subroutine` and `List` node kinds
* remove tests for subroutines

### Motivation

The motivation behind removing low-level subroutines is two-fold:
1. they only exist for the VM target - asm.js, Wasm, and C don't have
   them -, which complicates the compiler architecture
2. they have a fairly high implementation complexity in the VM

Number 1 is the main reason for the removal. Languages without low-
level subroutines need a local + `Select` dispatcher to emulate them.

This lowering would be best be implemented at a higher-level, so that
the optimization passed can take the locals' data-flow into account,
but this requires all ILs below to support two dialects: one with and 
one without subroutines.

Alternatively, the lowering could be done at a lower level, but this
would result in the same dialect problem as the other approach
(although to a lesser degree), and it'd also result in worse code
generation.

Since low-level subroutines are only an optimization, and the VM is the
only target out of the planned ones to support it, removing them is the
overall better choice, as it reduced the surface area of ILs and the
VM, as well as making lowering passes from subroutines into dispatchers
unnecessary.
  • Loading branch information
zerbina authored Aug 18, 2024
1 parent 5847552 commit 6ef36e1
Show file tree
Hide file tree
Showing 18 changed files with 50 additions and 543 deletions.
3 changes: 1 addition & 2 deletions passes/lang0.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ cont_name ::= <int>
goto ::= (Continue <cont_name>)
err_goto ::= (Unwind)
| (List (Continue <cont_name>)* (Unwind)?)
| (Continue <cont_name>)
choice ::= (Choice <intVal> <goto>)
| (Choice <floatVal> <goto>)
Expand Down Expand Up @@ -91,7 +91,6 @@ stmt ::= (Asgn <local> <value>)
continuation ::= (Continuation (Params) stack:<int> <stmt>* <exit>)
| (Continuation (Params <type_id>?)) # return continuation
| (Subroutine (Params) stack:<int> <stmt>* <exit>)
| (Except <local> stack:<int> <stmt>* <exit>)
procdef ::= (ProcDef <type_id> (Locals <type_id>*) (Continuations <continuation>+))
Expand Down
2 changes: 0 additions & 2 deletions passes/lang1.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,9 @@ Each continuation names the locals alive for the duration of it:

```grammar
continuation -= (Continuation (Params) stack:<int> <stmt>* <exit>)
| (Subroutine (Params) stack:<int> <stmt>* <exit>)
| (Except <local> stack:<int> <stmt>* <exit>)
continuation += (Continuation (Params) (Locals <local>*) <stmt>* <exit>)
| (Subroutine (Params) (Locals <local>*) <stmt>* <exit>)
| (Except <local> (Locals <local>*) <stmt>* <exit>)
```

Expand Down
49 changes: 10 additions & 39 deletions passes/pass0.nim
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ type
code: seq[Instr]
locals: seq[ValueType]
constants: seq[TypedValue]
ehCode: seq[EhInstr]
ehTable: seq[HandlerTableEntry]
ehTable: seq[EhMapping]

# per-procedure temporary state:
current: int
Expand All @@ -47,8 +46,7 @@ type
## needed for the procedure.
code: seq[Instr]
locals: seq[ValueType]
ehTable: seq[HandlerTableEntry]
ehCode: seq[EhInstr]
ehTable: seq[EhMapping]
constants: seq[TypedValue]

# shorten some common parameter definitions:
Expand Down Expand Up @@ -430,25 +428,14 @@ proc genChoice(c; tree; typ: Type0, val, choice: NodeIndex) =
c.join(x)

proc genEh(c; tree; exit: NodeIndex) =
## Generates the EH code for the `exit` and registers an EH mapping entry.
## Registers an EH table entry corresponding to `exit`, if necessary.
case tree[exit].kind
of Unwind:
discard "attach nothing"
of List:
of Continue:
# register an EH mapping:
c.ehTable.add (c.code.high.uint32, c.ehCode.len.uint32)

let L = tree.len(exit)
for i, it in tree.pairs(exit):
case tree[it].kind
of Continue:
c.ehCode.add ((if i == L-1: ehoExcept else: ehoSubroutine), 0'u16)
# the jump destination is filled in later:
c.ehPatch.add (tree[it, 0].imm.int, c.ehCode.high)
of Unwind:
c.ehCode.add (ehoEnd, 0'u16)
else:
unreachable()
c.ehTable.add (c.code.high.uint32, 0'u32) # patched later
c.ehPatch.add (tree[exit, 0].imm.int, c.ehTable.high)
else:
unreachable()

Expand All @@ -457,8 +444,6 @@ proc genExit(c; tree; exit: NodeIndex) =
case tree[exit].kind
of Continue:
case tree.len(exit)
of 0:
c.instr(opcEnd) # end the subroutine
of 1:
c.jump(opcJmp, tree[exit, 0].imm)
of 2:
Expand All @@ -473,13 +458,6 @@ proc genExit(c; tree; exit: NodeIndex) =
c.genExpr(tree, tree.child(exit, 0))
c.instr(opcRaise)
c.genEh(tree, tree.child(exit, 1))
of Enter:
c.jump(opcEnter, tree[exit, 0].imm)
c.jump(opcJmp, tree[exit, 1].imm)
of Leave:
# TODO: merge subsequent leaves into a single instruction
let target = c.prepareJump(tree[exit, 0].imm)
c.instr(opcLeave, target, int16 1)
of SelectBool:
let (sel, a, b) = triplet(tree, exit)
c.genExpr(tree, sel)
Expand Down Expand Up @@ -518,10 +496,10 @@ proc start(c; idx: int) =
inc i

i = 0
# patch EH instructions:
# patch EH mappings:
while i < c.ehPatch.len:
if c.ehPatch[i][0] == idx:
c.ehCode[c.ehPatch[i][1]].a = c.code.len.uint16
c.ehTable[c.ehPatch[i][1]].dst = c.code.len.uint16
c.ehPatch.del(i)
else:
inc i
Expand Down Expand Up @@ -582,8 +560,6 @@ proc gen(c; tree; n: NodeIndex) =
c.instr(opcExcept)
# assign the passed-along value to the provided local
c.instr(opcPopLocal, tree[n, 0].id)
of Subroutine:
c.instr(opcBegin)
of Continuation:
discard "no special instruction needed"
else:
Expand Down Expand Up @@ -642,8 +618,8 @@ proc translate(tree; types, def: NodeIndex): ProcResult =
c.start(i)
c.instr(opcRet)

result = ProcResult(code: c.code, locals: c.locals, ehCode: c.ehCode,
ehTable: c.ehTable, constants: c.constants)
result = ProcResult(code: c.code, locals: c.locals, ehTable: c.ehTable,
constants: c.constants)

proc slice[T](old, with: seq[T]): Slice[uint32] =
old.len.uint32 .. uint32(old.len + with.len - 1)
Expand Down Expand Up @@ -717,10 +693,5 @@ proc translate*(module: PackedTree[NodeKind], env: var VmEnv) =
env.procs[i].locals = hoSlice(env.locals, prc.locals)
env.locals.add prc.locals

# patch the EH table:
for it in prc.ehTable.mitems:
it.offset += env.ehCode.len.uint32

env.procs[i].eh = hoSlice(env.ehTable, prc.ehTable)
env.ehTable.add prc.ehTable
env.ehCode.add prc.ehCode
4 changes: 2 additions & 2 deletions passes/spec.nim
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ type
Conv, Reinterp

Continue, Loop, Enter, Leave, Raise, Unreachable, Select, SelectBool
CheckedCall, CheckedCallAsgn, Unwind, Choice, List
CheckedCall, CheckedCallAsgn, Unwind, Choice

Module, TypeDefs, ProcDefs, ProcDef, Locals, Continuations, Continuation,
Subroutine, Except, Params, GlobalDefs, GlobalDef
Except, Params, GlobalDefs, GlobalDef

template isAtom*(x: NodeKind): bool =
ord(x) <= ord(Global)
Expand Down
12 changes: 0 additions & 12 deletions tests/pass0/t03_basic_subroutine.expected

This file was deleted.

26 changes: 0 additions & 26 deletions tests/pass0/t03_basic_subroutine.test

This file was deleted.

2 changes: 1 addition & 1 deletion tests/pass0/t04_except.expected
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
.local lo0 int
LdImmInt 10
Raise
.eh ((Except L2))
.eh L2
.label L2
Except
PopLocal lo0
Expand Down
2 changes: 1 addition & 1 deletion tests/pass0/t04_except.test
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ discard """
(Locals (Type 0))
(Continuations
(Continuation (Params) 0
(Raise (IntVal 10) (List (Continue 1)))
(Raise (IntVal 10) (Continue 1))
)
(Except (Local 0) 0
(Continue 2 (Copy (Local 0)))
Expand Down
13 changes: 0 additions & 13 deletions tests/pass0/t04_leave_subroutine.expected

This file was deleted.

26 changes: 0 additions & 26 deletions tests/pass0/t04_leave_subroutine.test

This file was deleted.

10 changes: 0 additions & 10 deletions tests/pass0/t04_raise_intercept_resume.expected

This file was deleted.

20 changes: 0 additions & 20 deletions tests/pass0/t04_raise_intercept_resume.test

This file was deleted.

Loading

0 comments on commit 6ef36e1

Please sign in to comment.