Reduced the number of IR methods
All checks were successful
/ test (push) Successful in 15s

This commit is contained in:
Eduard Urbach 2025-07-03 19:25:07 +02:00
parent 72ace483e4
commit 0eaeb726b9
Signed by: akyoto
GPG key ID: 49226B848C78F6C8
8 changed files with 112 additions and 101 deletions

View file

@ -27,8 +27,14 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
} }
f.Dependencies.Add(function) f.Dependencies.Add(function)
v := f.AppendFunction(function.UniqueName, function.Type, function.IsExtern())
v.SetSource(expr.Source) v := f.Append(&ssa.Function{
UniqueName: function.UniqueName,
Typ: function.Type,
IsExtern: function.IsExtern(),
Source: ssa.Source(expr.Source),
})
return v, nil return v, nil
} }
@ -41,26 +47,33 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
return nil, err return nil, err
} }
v := f.AppendInt(number) v := f.Append(&ssa.Int{
v.SetSource(expr.Source) Int: number,
Source: ssa.Source(expr.Source),
})
return v, nil return v, nil
case token.String: case token.String:
data := expr.Token.Bytes(f.File.Bytes) data := expr.Token.Bytes(f.File.Bytes)
data = Unescape(data) data = Unescape(data)
length := f.AppendInt(len(data)) length := f.Append(&ssa.Int{
length.SetSource(expr.Source) Int: len(data),
Source: ssa.Source(expr.Source),
})
pointer := f.AppendBytes(data) pointer := f.Append(&ssa.Bytes{
pointer.SetSource(expr.Source) Bytes: data,
Source: ssa.Source(expr.Source),
})
v := f.Append(&ssa.Struct{ v := f.Append(&ssa.Struct{
Arguments: []ssa.Value{pointer, length}, Arguments: []ssa.Value{pointer, length},
Typ: types.String, Typ: types.String,
Source: ssa.Source(expr.Source),
}) })
v.SetSource(expr.Source)
return v, nil return v, nil
} }
@ -75,17 +88,6 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
if children[0].Token.Kind == token.Identifier { if children[0].Token.Kind == token.Identifier {
funcName := children[0].String(f.File.Bytes) funcName := children[0].String(f.File.Bytes)
if funcName == "len" {
identifier := children[1].String(f.File.Bytes)
length, exists := f.Identifiers[identifier+".len"]
if !exists {
return nil, errors.New(&UnknownIdentifier{Name: identifier + ".len"}, f.File, expr.Token.Position)
}
return length, nil
}
if funcName == "syscall" { if funcName == "syscall" {
children = children[1:] children = children[1:]
isSyscall = true isSyscall = true
@ -111,47 +113,57 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
} }
return f.Append(syscall), nil return f.Append(syscall), nil
} else { }
name := args[0].(*ssa.Function).UniqueName
fn := f.All.Functions[name]
parameters := args[1:]
if len(parameters) != len(fn.Input) { name := args[0].(*ssa.Function).UniqueName
return nil, errors.New(&ParameterCountMismatch{Function: name, Count: len(parameters), ExpectedCount: len(fn.Input)}, f.File, expr.Source[0].Position) fn := f.All.Functions[name]
} parameters := args[1:]
for i, param := range slices.Backward(parameters) { if len(parameters) != len(fn.Input) {
if !types.Is(param.Type(), fn.Input[i].Typ) { return nil, errors.New(&ParameterCountMismatch{Function: name, Count: len(parameters), ExpectedCount: len(fn.Input)}, f.File, expr.Source[0].Position)
_, isPointer := fn.Input[i].Typ.(*types.Pointer) }
if isPointer { for i, param := range slices.Backward(parameters) {
number, isInt := param.(*ssa.Int) if !types.Is(param.Type(), fn.Input[i].Typ) {
_, isPointer := fn.Input[i].Typ.(*types.Pointer)
if isInt && number.Int == 0 { if isPointer {
continue number, isInt := param.(*ssa.Int)
}
}
// Temporary hack to allow int64 -> uint32 conversion if isInt && number.Int == 0 {
if types.Is(param.Type(), types.AnyInt) && types.Is(fn.Input[i].Typ, types.AnyInt) {
continue continue
} }
return nil, errors.New(&TypeMismatch{
Encountered: param.Type().Name(),
Expected: fn.Input[i].Typ.Name(),
ParameterName: fn.Input[i].Name,
}, f.File, param.Start())
} }
}
if fn.IsExtern() { // Temporary hack to allow int64 -> uint32 conversion
return f.Append(&ssa.CallExtern{Arguments: args, Source: ssa.Source(expr.Source)}), nil if types.Is(param.Type(), types.AnyInt) && types.Is(fn.Input[i].Typ, types.AnyInt) {
} continue
}
return f.Append(&ssa.Call{Arguments: args, Source: ssa.Source(expr.Source)}), nil return nil, errors.New(&TypeMismatch{
Encountered: param.Type().Name(),
Expected: fn.Input[i].Typ.Name(),
ParameterName: fn.Input[i].Name,
}, f.File, param.Start())
}
} }
if fn.IsExtern() {
v := f.Append(&ssa.CallExtern{
Arguments: args,
Source: ssa.Source(expr.Source),
})
return v, nil
}
v := f.Append(&ssa.Call{
Arguments: args,
Source: ssa.Source(expr.Source),
})
return v, nil
case token.Dot: case token.Dot:
left := expr.Children[0] left := expr.Children[0]
right := expr.Children[1] right := expr.Children[1]
@ -167,8 +179,12 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
return nil, errors.New(&UnknownStructField{StructName: structure.Name(), FieldName: rightText}, f.File, right.Token.Position) return nil, errors.New(&UnknownStructField{StructName: structure.Name(), FieldName: rightText}, f.File, right.Token.Position)
} }
v := f.Append(&ssa.StructField{Struct: identifier, Field: field}) v := f.Append(&ssa.StructField{
v.SetSource(expr.Source) Struct: identifier,
Field: field,
Source: ssa.Source(expr.Source),
})
return v, nil return v, nil
} }
@ -182,13 +198,19 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
f.Dependencies.Add(function) f.Dependencies.Add(function)
} }
v := f.AppendFunction(function.UniqueName, function.Type, function.IsExtern()) v := f.Append(&ssa.Function{
v.SetSource(expr.Source) UniqueName: function.UniqueName,
Typ: function.Type,
IsExtern: function.IsExtern(),
Source: ssa.Source(expr.Source),
})
return v, nil return v, nil
} }
return nil, errors.New(&UnknownIdentifier{Name: label}, f.File, left.Token.Position) return nil, errors.New(&UnknownIdentifier{Name: label}, f.File, left.Token.Position)
}
return nil, nil default:
panic("not implemented")
}
} }

View file

@ -11,12 +11,12 @@ import (
func TestBinaryOp(t *testing.T) { func TestBinaryOp(t *testing.T) {
fn := ssa.IR{} fn := ssa.IR{}
a := fn.AppendInt(1) a := fn.Append(&ssa.Int{Int: 1})
b := fn.AppendInt(2) b := fn.Append(&ssa.Int{Int: 2})
c := fn.Append(&ssa.BinaryOp{Op: token.Add, Left: a, Right: b}) c := fn.Append(&ssa.BinaryOp{Op: token.Add, Left: a, Right: b})
fn.AddBlock() fn.AddBlock()
d := fn.AppendInt(3) d := fn.Append(&ssa.Int{Int: 3})
e := fn.AppendInt(4) e := fn.Append(&ssa.Int{Int: 4})
f := fn.Append(&ssa.BinaryOp{Op: token.Add, Left: d, Right: e}) f := fn.Append(&ssa.BinaryOp{Op: token.Add, Left: d, Right: e})
assert.Equal(t, c.String(), "1 + 2") assert.Equal(t, c.String(), "1 + 2")
@ -26,11 +26,11 @@ func TestBinaryOp(t *testing.T) {
func TestBinaryOpEquals(t *testing.T) { func TestBinaryOpEquals(t *testing.T) {
fn := ssa.IR{} fn := ssa.IR{}
one := fn.AppendInt(1) one := fn.Append(&ssa.Int{Int: 1})
two := fn.AppendInt(2) two := fn.Append(&ssa.Int{Int: 2})
binOp := fn.Append(&ssa.BinaryOp{Op: token.Add, Left: one, Right: two}) binOp := fn.Append(&ssa.BinaryOp{Op: token.Add, Left: one, Right: two})
oneDup := fn.AppendInt(1) oneDup := fn.Append(&ssa.Int{Int: 1})
twoDup := fn.AppendInt(2) twoDup := fn.Append(&ssa.Int{Int: 2})
binOpDup := fn.Append(&ssa.BinaryOp{Op: token.Add, Left: oneDup, Right: twoDup}) binOpDup := fn.Append(&ssa.BinaryOp{Op: token.Add, Left: oneDup, Right: twoDup})
binOpDiff := fn.Append(&ssa.BinaryOp{Op: token.Add, Left: oneDup, Right: oneDup}) binOpDiff := fn.Append(&ssa.BinaryOp{Op: token.Add, Left: oneDup, Right: oneDup})

View file

@ -13,7 +13,7 @@ func TestBytes(t *testing.T) {
hello := fn.Append(&ssa.Bytes{Bytes: []byte("Hello")}) hello := fn.Append(&ssa.Bytes{Bytes: []byte("Hello")})
world := fn.Append(&ssa.Bytes{Bytes: []byte("World")}) world := fn.Append(&ssa.Bytes{Bytes: []byte("World")})
helloDup := fn.Append(&ssa.Bytes{Bytes: []byte("Hello")}) helloDup := fn.Append(&ssa.Bytes{Bytes: []byte("Hello")})
one := fn.AppendInt(1) one := fn.Append(&ssa.Int{Int: 1})
assert.False(t, hello.Equals(world)) assert.False(t, hello.Equals(world))
assert.False(t, hello.Equals(one)) assert.False(t, hello.Equals(one))

View file

@ -12,7 +12,7 @@ func TestCall(t *testing.T) {
fn := ssa.IR{} fn := ssa.IR{}
myfunc := fn.Append(&ssa.Function{UniqueName: "myfunc", Typ: &types.Function{}}) myfunc := fn.Append(&ssa.Function{UniqueName: "myfunc", Typ: &types.Function{}})
call := fn.Append(&ssa.Call{Arguments: ssa.Arguments{myfunc}}) call := fn.Append(&ssa.Call{Arguments: ssa.Arguments{myfunc}})
one := fn.AppendInt(1) one := fn.Append(&ssa.Int{Int: 1})
call2 := fn.Append(&ssa.Call{Arguments: ssa.Arguments{myfunc, one}}) call2 := fn.Append(&ssa.Call{Arguments: ssa.Arguments{myfunc, one}})
assert.True(t, call.Type() == types.Void) assert.True(t, call.Type() == types.Void)
@ -31,8 +31,8 @@ func TestCallEquals(t *testing.T) {
}, },
}) })
one := fn.AppendInt(1) one := fn.Append(&ssa.Int{Int: 1})
two := fn.AppendInt(2) two := fn.Append(&ssa.Int{Int: 2})
call1 := fn.Append(&ssa.Call{Arguments: ssa.Arguments{sum, one, two}}) call1 := fn.Append(&ssa.Call{Arguments: ssa.Arguments{sum, one, two}})
call2 := fn.Append(&ssa.Call{Arguments: ssa.Arguments{sum, one, two}}) call2 := fn.Append(&ssa.Call{Arguments: ssa.Arguments{sum, one, two}})
@ -51,8 +51,8 @@ func TestCallReturnType(t *testing.T) {
}, },
}) })
one := fn.AppendInt(1) one := fn.Append(&ssa.Int{Int: 1})
two := fn.AppendInt(2) two := fn.Append(&ssa.Int{Int: 2})
call := fn.Append(&ssa.Call{Arguments: ssa.Arguments{sum, one, two}}) call := fn.Append(&ssa.Call{Arguments: ssa.Arguments{sum, one, two}})
assert.Equal(t, call.String(), "sum(1, 2)") assert.Equal(t, call.String(), "sum(1, 2)")

View file

@ -1,9 +1,5 @@
package ssa package ssa
import (
"git.urbach.dev/cli/q/src/types"
)
// IR is a list of basic blocks. // IR is a list of basic blocks.
type IR struct { type IR struct {
Blocks []*Block Blocks []*Block
@ -26,12 +22,10 @@ func (f *IR) Append(instr Value) Value {
f.AddBlock() f.AddBlock()
} }
if instr.IsConst() { existing := f.FindExisting(instr)
for existing := range f.Values {
if existing.IsConst() && instr.Equals(existing) { if existing != nil {
return existing return existing
}
}
} }
instr.SetID(f.nextId) instr.SetID(f.nextId)
@ -39,24 +33,19 @@ func (f *IR) Append(instr Value) Value {
return f.Blocks[len(f.Blocks)-1].Append(instr) return f.Blocks[len(f.Blocks)-1].Append(instr)
} }
// AppendInt adds a new integer value to the last block. // FindExisting returns an equal instruction that's already appended or `nil` if none could be found.
func (f *IR) AppendInt(x int) Value { func (f *IR) FindExisting(instr Value) Value {
return f.Append(&Int{Int: x}) if !instr.IsConst() {
} return nil
}
// AppendFunction adds a new function value to the last block. for existing := range f.Values {
func (f *IR) AppendFunction(name string, typ *types.Function, extern bool) Value { if existing.IsConst() && instr.Equals(existing) {
return f.Append(&Function{UniqueName: name, Typ: typ, IsExtern: extern}) return existing
} }
}
// AppendBytes adds a new byte slice value to the last block. return nil
func (f *IR) AppendBytes(s []byte) Value {
return f.Append(&Bytes{Bytes: s})
}
// AppendString adds a new string value to the last block.
func (f *IR) AppendString(s string) Value {
return f.Append(&Bytes{Bytes: []byte(s)})
} }
// Values yields on each value. // Values yields on each value.

View file

@ -1,7 +1,7 @@
package ssa package ssa
import ( import (
"fmt" "strconv"
"git.urbach.dev/cli/q/src/types" "git.urbach.dev/cli/q/src/types"
) )
@ -36,7 +36,7 @@ func (v *Int) Debug(expand bool) string {
} }
func (v *Int) String() string { func (v *Int) String() string {
return fmt.Sprintf("%d", v.Int) return strconv.Itoa(v.Int)
} }
func (v *Int) Type() types.Type { func (v *Int) Type() types.Type {

View file

@ -11,9 +11,9 @@ import (
func TestReturn(t *testing.T) { func TestReturn(t *testing.T) {
fn := ssa.IR{} fn := ssa.IR{}
ret := fn.Append(&ssa.Return{}) ret := fn.Append(&ssa.Return{})
one := fn.AppendInt(1) one := fn.Append(&ssa.Int{Int: 1})
ret2 := fn.Append(&ssa.Return{Arguments: ssa.Arguments{one}}) ret2 := fn.Append(&ssa.Return{Arguments: ssa.Arguments{one}})
two := fn.AppendInt(2) two := fn.Append(&ssa.Int{Int: 2})
ret3 := fn.Append(&ssa.Return{Arguments: ssa.Arguments{one, two}}) ret3 := fn.Append(&ssa.Return{Arguments: ssa.Arguments{one, two}})
ret4 := fn.Append(&ssa.Return{Arguments: ssa.Arguments{two, one}}) ret4 := fn.Append(&ssa.Return{Arguments: ssa.Arguments{two, one}})
ret5 := fn.Append(&ssa.Return{Arguments: ssa.Arguments{one, two}}) ret5 := fn.Append(&ssa.Return{Arguments: ssa.Arguments{one, two}})

View file

@ -11,9 +11,9 @@ import (
func TestSyscall(t *testing.T) { func TestSyscall(t *testing.T) {
fn := ssa.IR{} fn := ssa.IR{}
syscall := fn.Append(&ssa.Syscall{}) syscall := fn.Append(&ssa.Syscall{})
one := fn.AppendInt(1) one := fn.Append(&ssa.Int{Int: 1})
syscall2 := fn.Append(&ssa.Syscall{Arguments: ssa.Arguments{one}}) syscall2 := fn.Append(&ssa.Syscall{Arguments: ssa.Arguments{one}})
two := fn.AppendInt(2) two := fn.Append(&ssa.Int{Int: 2})
syscall3 := fn.Append(&ssa.Syscall{Arguments: ssa.Arguments{one, two}}) syscall3 := fn.Append(&ssa.Syscall{Arguments: ssa.Arguments{one, two}})
syscall4 := fn.Append(&ssa.Syscall{Arguments: ssa.Arguments{one, two}}) syscall4 := fn.Append(&ssa.Syscall{Arguments: ssa.Arguments{one, two}})