Improved SSA and added unused value checks
All checks were successful
/ test (push) Successful in 17s
All checks were successful
/ test (push) Successful in 17s
This commit is contained in:
parent
cc2e98ca49
commit
2b703e9af2
25 changed files with 519 additions and 213 deletions
16
src/core/CheckDeadCode.go
Normal file
16
src/core/CheckDeadCode.go
Normal 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
|
||||
}
|
|
@ -26,4 +26,6 @@ func (f *Function) Compile() {
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
f.Err = f.CheckDeadCode()
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue