Improved SSA and added unused value checks
All checks were successful
/ test (push) Successful in 17s

This commit is contained in:
Eduard Urbach 2025-06-23 23:11:05 +02:00
parent cc2e98ca49
commit 2b703e9af2
Signed by: akyoto
GPG key ID: 49226B848C78F6C8
25 changed files with 519 additions and 213 deletions

16
src/core/CheckDeadCode.go Normal file
View file

@ -0,0 +1,16 @@
package core
import (
"git.urbach.dev/cli/q/src/errors"
)
// CheckDeadCode checks for dead values.
func (f *Function) CheckDeadCode() error {
for instr := range f.Values {
if instr.IsConst() && instr.Alive() == 0 {
return errors.New(&UnusedValue{Value: instr.String()}, f.File, instr.Token().Position)
}
}
return nil
}

View file

@ -26,4 +26,6 @@ func (f *Function) Compile() {
return
}
}
f.Err = f.CheckDeadCode()
}

View file

@ -15,9 +15,10 @@ func (f *Function) CompileReturn(tokens token.List) error {
return err
}
f.Append(ssa.Value{
Type: ssa.Return,
Args: []*ssa.Value{value},
f.Append(&ssa.Return{
Arguments: ssa.Arguments{
Args: []ssa.Value{value},
},
})
return nil

View file

@ -10,7 +10,7 @@ import (
)
// Evaluate converts an expression to an SSA value.
func (f *Function) Evaluate(expr *expression.Expression) (*ssa.Value, error) {
func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
if expr.IsLeaf() {
switch expr.Token.Kind {
case token.Identifier:
@ -30,12 +30,16 @@ func (f *Function) Evaluate(expr *expression.Expression) (*ssa.Value, error) {
return nil, err
}
return f.AppendInt(number), nil
v := f.AppendInt(number)
v.Source = expr.Token
return v, nil
case token.String:
data := expr.Token.Bytes(f.File.Bytes)
data = Unescape(data)
return f.AppendBytes(data), nil
v := f.AppendBytes(data)
v.Source = expr.Token
return v, nil
}
return nil, errors.New(InvalidExpression, f.File, expr.Token.Position)
@ -44,7 +48,7 @@ func (f *Function) Evaluate(expr *expression.Expression) (*ssa.Value, error) {
switch expr.Token.Kind {
case token.Call:
children := expr.Children
typ := ssa.Call
isSyscall := false
if children[0].Token.Kind == token.Identifier {
funcName := children[0].String(f.File.Bytes)
@ -56,11 +60,11 @@ func (f *Function) Evaluate(expr *expression.Expression) (*ssa.Value, error) {
if funcName == "syscall" {
children = children[1:]
typ = ssa.Syscall
isSyscall = true
}
}
args := make([]*ssa.Value, len(children))
args := make([]ssa.Value, len(children))
for i, child := range children {
value, err := f.Evaluate(child)
@ -72,12 +76,27 @@ func (f *Function) Evaluate(expr *expression.Expression) (*ssa.Value, error) {
args[i] = value
}
call := f.Append(ssa.Value{Type: typ, Args: args})
return call, nil
if isSyscall {
v := f.Append(&ssa.Syscall{
Arguments: ssa.Arguments{Args: args},
HasToken: ssa.HasToken{Source: expr.Token},
})
return v, nil
} else {
v := f.Append(&ssa.Call{
Arguments: ssa.Arguments{Args: args},
HasToken: ssa.HasToken{Source: expr.Token},
})
return v, nil
}
case token.Dot:
name := fmt.Sprintf("%s.%s", expr.Children[0].String(f.File.Bytes), expr.Children[1].String(f.File.Bytes))
return f.AppendFunction(name), nil
v := f.AppendFunction(name)
v.Source = expr.Children[1].Token
return v, nil
}
return nil, nil

View file

@ -10,14 +10,14 @@ import (
// Function is the smallest unit of code.
type Function struct {
ssa.Function
ssa.IR
Name string
UniqueName string
File *fs.File
Input []*Parameter
Output []*Parameter
Body token.List
Identifiers map[string]*ssa.Value
Identifiers map[string]ssa.Value
Err error
}
@ -27,9 +27,11 @@ func NewFunction(name string, file *fs.File) *Function {
Name: name,
File: file,
UniqueName: fmt.Sprintf("%s.%s", file.Package, name),
Identifiers: make(map[string]*ssa.Value),
Function: ssa.Function{
Blocks: []*ssa.Block{{}},
Identifiers: make(map[string]ssa.Value),
IR: ssa.IR{
Blocks: []*ssa.Block{
{Instructions: make([]ssa.Value, 0, 8)},
},
},
}
}

View file

@ -24,4 +24,13 @@ func (err *UnknownIdentifier) Error() string {
}
return fmt.Sprintf("Unknown identifier '%s'", err.Name)
}
// UnusedValue error is created when a value is never used.
type UnusedValue struct {
Value string
}
func (err *UnusedValue) Error() string {
return fmt.Sprintf("Unused value '%s'", err.Value)
}