Fixed variable lifetime in loops

This commit is contained in:
2024-07-16 20:22:28 +02:00
parent 1825a72f8c
commit f9d72fe490
11 changed files with 90 additions and 18 deletions

View File

@ -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]

View File

@ -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)

View File

@ -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
}

View File

@ -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]
}

View File

@ -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.