Fixed variable lifetime in loops
This commit is contained in:
parent
1825a72f8c
commit
f9d72fe490
@ -22,8 +22,12 @@ func (a *Assembler) Call(name string) {
|
||||
|
||||
// Return returns back to the caller.
|
||||
func (a *Assembler) Return() {
|
||||
if len(a.Instructions) > 0 && a.Instructions[len(a.Instructions)-1].Mnemonic == RETURN {
|
||||
return
|
||||
if len(a.Instructions) > 0 {
|
||||
lastMnemonic := a.Instructions[len(a.Instructions)-1].Mnemonic
|
||||
|
||||
if lastMnemonic == RETURN || lastMnemonic == JUMP {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
a.Instructions = append(a.Instructions, Instruction{Mnemonic: RETURN})
|
||||
|
@ -9,6 +9,10 @@ func Count(body AST, kind token.Kind, name string) int {
|
||||
for _, node := range body {
|
||||
switch node := node.(type) {
|
||||
case *Assign:
|
||||
if node.Name.Kind == kind && node.Name.Text() == name {
|
||||
count++
|
||||
}
|
||||
|
||||
count += node.Value.Count(kind, name)
|
||||
|
||||
case *Call:
|
||||
@ -18,7 +22,9 @@ func Count(body AST, kind token.Kind, name string) int {
|
||||
count += node.Value.Count(kind, name)
|
||||
|
||||
case *Return:
|
||||
count += node.Value.Count(kind, name)
|
||||
if node.Value != nil {
|
||||
count += node.Value.Count(kind, name)
|
||||
}
|
||||
|
||||
case *If:
|
||||
count += node.Condition.Count(kind, name)
|
||||
|
@ -30,6 +30,10 @@ func toASTNode(tokens token.List) (Node, error) {
|
||||
word := tokens[0].Text()
|
||||
|
||||
if word == keyword.Return {
|
||||
if len(tokens) == 1 {
|
||||
return &Return{}, nil
|
||||
}
|
||||
|
||||
value := expression.Parse(tokens[1:])
|
||||
return &Return{Value: value}, nil
|
||||
}
|
||||
|
@ -51,6 +51,10 @@ func (f *Function) CompileCall(root *expression.Expression) error {
|
||||
f.Call(funcName)
|
||||
}
|
||||
|
||||
for _, register := range registers {
|
||||
f.Scope().Free(register)
|
||||
}
|
||||
|
||||
// Pop
|
||||
for i := len(f.cpu.General) - 1; i >= 0; i-- {
|
||||
register := f.cpu.General[i]
|
||||
|
@ -44,26 +44,35 @@ func (f *Function) AddVariable(variable *Variable) {
|
||||
f.Comment("%s = %s (%s, %d uses)", variable.Name, variable.Value, variable.Register, variable.Alive)
|
||||
}
|
||||
|
||||
f.Scope().variables[variable.Name] = variable
|
||||
f.Scope().Reserve(variable.Register)
|
||||
f.Scope().Use(variable.Register)
|
||||
scope := f.Scope()
|
||||
variable.Scope = scope
|
||||
|
||||
scope.variables[variable.Name] = variable
|
||||
scope.Reserve(variable.Register)
|
||||
scope.Use(variable.Register)
|
||||
}
|
||||
|
||||
func (f *Function) useVariable(variable *Variable) {
|
||||
for _, scope := range f.scopes {
|
||||
for i, scope := range f.scopes {
|
||||
if scope.inLoop && variable.Scope != scope {
|
||||
continue
|
||||
}
|
||||
|
||||
local := scope.variables[variable.Name]
|
||||
|
||||
if local != nil {
|
||||
local.Alive--
|
||||
if local == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
local.Alive--
|
||||
|
||||
if local.Alive < 0 {
|
||||
panic("incorrect number of variable use calls")
|
||||
}
|
||||
|
||||
if local.Alive == 0 {
|
||||
if config.Comments {
|
||||
f.Comment("%s died (%s)", local.Name, local.Register)
|
||||
f.Comment("%s died (%s) in scope %d", local.Name, local.Register, i)
|
||||
}
|
||||
|
||||
scope.Free(local.Register)
|
||||
|
@ -12,9 +12,10 @@ func (f *Function) CompileLoop(loop *ast.Loop) error {
|
||||
f.count.loop++
|
||||
label := fmt.Sprintf("%s_loop_%d", f.Name, f.count.loop)
|
||||
f.AddLabel(label)
|
||||
f.pushScope(loop.Body)
|
||||
scope := f.pushScope(loop.Body)
|
||||
scope.inLoop = true
|
||||
err := f.CompileAST(loop.Body)
|
||||
f.popScope()
|
||||
f.Jump(asm.JUMP, label)
|
||||
f.popScope()
|
||||
return err
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package core
|
||||
|
||||
import (
|
||||
"git.akyoto.dev/cli/q/src/build/ast"
|
||||
"git.akyoto.dev/cli/q/src/build/config"
|
||||
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||
"git.akyoto.dev/cli/q/src/build/token"
|
||||
)
|
||||
@ -10,6 +11,7 @@ import (
|
||||
type Scope struct {
|
||||
cpu.State
|
||||
variables map[string]*Variable
|
||||
inLoop bool
|
||||
}
|
||||
|
||||
// Scope returns the current scope.
|
||||
@ -18,13 +20,14 @@ func (s *state) Scope() *Scope {
|
||||
}
|
||||
|
||||
// pushScope pushes a new scope to the top of the stack.
|
||||
func (s *state) pushScope(body ast.AST) {
|
||||
func (f *Function) pushScope(body ast.AST) *Scope {
|
||||
scope := &Scope{}
|
||||
|
||||
if len(s.scopes) > 0 {
|
||||
lastScope := s.scopes[len(s.scopes)-1]
|
||||
if len(f.scopes) > 0 {
|
||||
lastScope := f.scopes[len(f.scopes)-1]
|
||||
scope.State = lastScope.State
|
||||
scope.variables = make(map[string]*Variable, len(lastScope.variables))
|
||||
scope.inLoop = lastScope.inLoop
|
||||
|
||||
for k, v := range lastScope.variables {
|
||||
count := ast.Count(body, token.Identifier, v.Name)
|
||||
@ -44,10 +47,20 @@ func (s *state) pushScope(body ast.AST) {
|
||||
scope.variables = map[string]*Variable{}
|
||||
}
|
||||
|
||||
s.scopes = append(s.scopes, scope)
|
||||
f.scopes = append(f.scopes, scope)
|
||||
|
||||
if config.Comments {
|
||||
f.Comment("scope %d start", len(f.scopes))
|
||||
}
|
||||
|
||||
return scope
|
||||
}
|
||||
|
||||
// popScope removes the scope at the top of the stack.
|
||||
func (s *state) popScope() {
|
||||
s.scopes = s.scopes[:len(s.scopes)-1]
|
||||
func (f *Function) popScope() {
|
||||
if config.Comments {
|
||||
f.Comment("scope %d end", len(f.scopes))
|
||||
}
|
||||
|
||||
f.scopes = f.scopes[:len(f.scopes)-1]
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ type Variable struct {
|
||||
Name string
|
||||
Register cpu.Register
|
||||
Alive int
|
||||
Scope *Scope
|
||||
}
|
||||
|
||||
// Variable returns the variable with the given name or `nil` if it doesn't exist.
|
||||
|
17
tests/programs/loop-lifetime.q
Normal file
17
tests/programs/loop-lifetime.q
Normal file
@ -0,0 +1,17 @@
|
||||
main() {
|
||||
n := 10
|
||||
x := 1
|
||||
|
||||
loop {
|
||||
if n == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
f(x)
|
||||
n -= 1
|
||||
}
|
||||
}
|
||||
|
||||
f(x) {
|
||||
return x
|
||||
}
|
11
tests/programs/loop.q
Normal file
11
tests/programs/loop.q
Normal file
@ -0,0 +1,11 @@
|
||||
main() {
|
||||
n := 10
|
||||
|
||||
loop {
|
||||
if n == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
n -= 1
|
||||
}
|
||||
}
|
@ -31,6 +31,8 @@ var programs = []struct {
|
||||
{"branch-or", "", 0},
|
||||
{"branch-both", "", 0},
|
||||
{"jump-near", "", 0},
|
||||
{"loop", "", 0},
|
||||
{"loop-lifetime", "", 0},
|
||||
}
|
||||
|
||||
func TestPrograms(t *testing.T) {
|
||||
|
Loading…
Reference in New Issue
Block a user