diff --git a/src/core/CheckDeadCode.go b/src/core/CheckDeadCode.go index deacd46..e109006 100644 --- a/src/core/CheckDeadCode.go +++ b/src/core/CheckDeadCode.go @@ -16,7 +16,8 @@ func (f *Function) CheckDeadCode() error { continue } - return errors.New(&UnusedValue{Value: instr.String()}, f.File, instr.(ssa.HasSource).Start()) + source := instr.(ssa.HasSource) + return errors.New(&UnusedValue{Value: string(f.File.Bytes[source.Start():source.End()])}, f.File, source.Start()) } return nil diff --git a/src/core/Decompose.go b/src/core/Decompose.go new file mode 100644 index 0000000..05037c6 --- /dev/null +++ b/src/core/Decompose.go @@ -0,0 +1,58 @@ +package core + +import ( + "git.urbach.dev/cli/q/src/errors" + "git.urbach.dev/cli/q/src/expression" + "git.urbach.dev/cli/q/src/ssa" + "git.urbach.dev/cli/q/src/types" +) + +// Decompose creates SSA values from expressions and decomposes structs into their individual fields. +func (f *Function) Decompose(nodes []*expression.Expression, typeCheck []*ssa.Parameter) ([]ssa.Value, error) { + args := make([]ssa.Value, 0, len(nodes)) + + for i, node := range nodes { + value, err := f.Evaluate(node) + + if err != nil { + return nil, err + } + + structure, isStruct := value.(*ssa.Struct) + + if isStruct { + for _, field := range structure.Arguments { + args = append(args, field) + } + + continue + } + + args = append(args, value) + + if typeCheck != nil && !types.Is(value.Type(), typeCheck[i].Typ) { + _, isPointer := typeCheck[i].Typ.(*types.Pointer) + + if isPointer { + number, isInt := value.(*ssa.Int) + + if isInt && number.Int == 0 { + continue + } + } + + // Temporary hack to allow int64 -> uint32 conversion + if types.Is(value.Type(), types.AnyInt) && types.Is(typeCheck[i].Typ, types.AnyInt) { + continue + } + + return nil, errors.New(&TypeMismatch{ + Encountered: value.Type().Name(), + Expected: typeCheck[i].Typ.Name(), + ParameterName: typeCheck[i].Name, + }, f.File, value.(ssa.HasSource).Start()) + } + } + + return args, nil +} \ No newline at end of file diff --git a/src/core/Evaluate.go b/src/core/Evaluate.go index 78a0dfb..73207c2 100644 --- a/src/core/Evaluate.go +++ b/src/core/Evaluate.go @@ -2,7 +2,6 @@ package core import ( "fmt" - "slices" "git.urbach.dev/cli/q/src/errors" "git.urbach.dev/cli/q/src/expression" @@ -68,11 +67,11 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) { Source: ssa.Source(expr.Source), }) - v := f.Append(&ssa.Struct{ + v := &ssa.Struct{ Arguments: []ssa.Value{pointer, length}, Typ: types.String, Source: ssa.Source(expr.Source), - }) + } return v, nil } @@ -82,31 +81,13 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) { switch expr.Token.Kind { case token.Call: - children := expr.Children - isSyscall := false - - if children[0].Token.Kind == token.Identifier { - funcName := children[0].String(f.File.Bytes) - - if funcName == "syscall" { - children = children[1:] - isSyscall = true - } - } - - args := make([]ssa.Value, len(children)) - - for i, child := range slices.Backward(children) { - value, err := f.Evaluate(child) + if expr.Children[0].Token.Kind == token.Identifier && expr.Children[0].String(f.File.Bytes) == "syscall" { + args, err := f.Decompose(expr.Children[1:], nil) if err != nil { return nil, err } - args[i] = value - } - - if isSyscall { syscall := &ssa.Syscall{ Arguments: args, Source: ssa.Source(expr.Source), @@ -115,41 +96,29 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) { return f.Append(syscall), nil } - name := args[0].(*ssa.Function).UniqueName - fn := f.All.Functions[name] - parameters := args[1:] + funcValue, err := f.Evaluate(expr.Children[0]) - if len(parameters) != len(fn.Input) { - return nil, errors.New(&ParameterCountMismatch{Function: name, Count: len(parameters), ExpectedCount: len(fn.Input)}, f.File, expr.Source[0].Position) + if err != nil { + return nil, err } - for i, param := range slices.Backward(parameters) { - if !types.Is(param.Type(), fn.Input[i].Typ) { - _, isPointer := fn.Input[i].Typ.(*types.Pointer) + ssaFunc := funcValue.(*ssa.Function) + fn := f.All.Functions[ssaFunc.UniqueName] + inputExpressions := expr.Children[1:] - if isPointer { - number, isInt := param.(*ssa.Int) + if len(inputExpressions) != len(fn.Input) { + return nil, errors.New(&ParameterCountMismatch{Function: fn.UniqueName, Count: len(inputExpressions), ExpectedCount: len(fn.Input)}, f.File, expr.Source[0].Position) + } - if isInt && number.Int == 0 { - continue - } - } + args, err := f.Decompose(inputExpressions, fn.Input) - // Temporary hack to allow int64 -> uint32 conversion - if types.Is(param.Type(), types.AnyInt) && types.Is(fn.Input[i].Typ, types.AnyInt) { - continue - } - - return nil, errors.New(&TypeMismatch{ - Encountered: param.Type().Name(), - Expected: fn.Input[i].Typ.Name(), - ParameterName: fn.Input[i].Name, - }, f.File, param.(ssa.HasSource).Start()) - } + if err != nil { + return nil, err } if fn.IsExtern() { v := f.Append(&ssa.CallExtern{Call: ssa.Call{ + Func: ssaFunc, Arguments: args, Source: ssa.Source(expr.Source), }}) @@ -158,6 +127,7 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) { } v := f.Append(&ssa.Call{ + Func: ssaFunc, Arguments: args, Source: ssa.Source(expr.Source), }) @@ -176,25 +146,6 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) { return identifier, nil } - // identifier, exists := f.Identifiers[leftText] - - // if exists { - // structType := identifier.Type().(*types.Struct) - // field := structType.FieldByName(rightText) - - // if field == nil { - // return nil, errors.New(&UnknownStructField{StructName: structType.Name(), FieldName: rightText}, f.File, right.Token.Position) - // } - - // v := f.Append(&ssa.Field{ - // Object: identifier, - // Field: field, - // Source: ssa.Source(expr.Source), - // }) - - // return v, nil - // } - function, exists := f.All.Functions[fullName] if exists { diff --git a/src/ssa/Call.go b/src/ssa/Call.go index cebfa9f..a18fa3e 100644 --- a/src/ssa/Call.go +++ b/src/ssa/Call.go @@ -7,6 +7,7 @@ import ( ) type Call struct { + Func *Function Arguments Liveness Source @@ -25,15 +26,13 @@ func (a *Call) Equals(v Value) bool { } func (v *Call) String() string { - return fmt.Sprintf("%s(%s)", v.Arguments[0].String(), v.Arguments[1:].String()) + return fmt.Sprintf("%s(%s)", v.Func.UniqueName, v.Arguments.String()) } func (v *Call) Type() types.Type { - typ := v.Arguments[0].(*Function).Typ - - if len(typ.Output) == 0 { + if len(v.Func.Typ.Output) == 0 { return types.Void } - return typ.Output[0] + return v.Func.Typ.Output[0] } \ No newline at end of file diff --git a/src/ssa/Call_test.go b/src/ssa/Call_test.go index e77cff6..33d4450 100644 --- a/src/ssa/Call_test.go +++ b/src/ssa/Call_test.go @@ -10,10 +10,10 @@ import ( func TestCall(t *testing.T) { fn := ssa.IR{} - myfunc := fn.Append(&ssa.Function{UniqueName: "myfunc", Typ: &types.Function{}}) - call := fn.Append(&ssa.Call{Arguments: ssa.Arguments{myfunc}}) + myfunc := &ssa.Function{UniqueName: "myfunc", Typ: &types.Function{}} + call := fn.Append(&ssa.Call{Func: myfunc, Arguments: ssa.Arguments{}}) one := fn.Append(&ssa.Int{Int: 1}) - call2 := fn.Append(&ssa.Call{Arguments: ssa.Arguments{myfunc, one}}) + call2 := fn.Append(&ssa.Call{Func: myfunc, Arguments: ssa.Arguments{one}}) assert.True(t, call.Type() == types.Void) assert.Equal(t, call.String(), "myfunc()") @@ -23,18 +23,18 @@ func TestCall(t *testing.T) { func TestCallEquals(t *testing.T) { fn := ssa.IR{} - sum := fn.Append(&ssa.Function{ + sum := &ssa.Function{ UniqueName: "sum", Typ: &types.Function{ Input: []types.Type{types.Int, types.Int}, Output: []types.Type{types.Int}, }, - }) + } one := fn.Append(&ssa.Int{Int: 1}) two := fn.Append(&ssa.Int{Int: 2}) - call1 := fn.Append(&ssa.Call{Arguments: ssa.Arguments{sum, one, two}}) - call2 := fn.Append(&ssa.Call{Arguments: ssa.Arguments{sum, one, two}}) + call1 := fn.Append(&ssa.Call{Func: sum, Arguments: ssa.Arguments{one, two}}) + call2 := fn.Append(&ssa.Call{Func: sum, Arguments: ssa.Arguments{one, two}}) assert.False(t, call1.Equals(one)) assert.True(t, call1.Equals(call2)) @@ -43,17 +43,17 @@ func TestCallEquals(t *testing.T) { func TestCallReturnType(t *testing.T) { fn := ssa.IR{} - sum := fn.Append(&ssa.Function{ + sum := &ssa.Function{ UniqueName: "sum", Typ: &types.Function{ Input: []types.Type{types.Int, types.Int}, Output: []types.Type{types.Int}, }, - }) + } one := fn.Append(&ssa.Int{Int: 1}) two := fn.Append(&ssa.Int{Int: 2}) - call := fn.Append(&ssa.Call{Arguments: ssa.Arguments{sum, one, two}}) + call := fn.Append(&ssa.Call{Func: sum, Arguments: ssa.Arguments{one, two}}) assert.Equal(t, call.String(), "sum(1, 2)") assert.True(t, call.Type() == types.Int) diff --git a/src/ssa2asm/CreateSteps.go b/src/ssa2asm/CreateSteps.go index 4ee4279..6549cdb 100644 --- a/src/ssa2asm/CreateSteps.go +++ b/src/ssa2asm/CreateSteps.go @@ -1,8 +1,6 @@ package ssa2asm import ( - "slices" - "git.urbach.dev/cli/q/src/cpu" "git.urbach.dev/cli/q/src/ssa" ) @@ -22,25 +20,12 @@ func (f *Compiler) CreateSteps(ir ssa.IR) []Step { for i, instr := range ir.Values { switch instr := instr.(type) { case *ssa.Call: - offset := 0 - - for r, param := range instr.Arguments[1:] { - structure, isStruct := param.(*ssa.Struct) - - if isStruct { - for _, field := range structure.Arguments { - f.ValueToStep[field].Hint(f.CPU.Call.In[offset+r]) - offset++ - } - - offset-- - } else { - f.ValueToStep[param].Hint(f.CPU.Call.In[offset+r]) - } + for paramIndex, param := range instr.Arguments { + f.ValueToStep[param].Hint(f.CPU.Call.In[paramIndex]) } case *ssa.CallExtern: - for r, param := range instr.Arguments[1:] { + for r, param := range instr.Arguments { if r >= len(f.CPU.ExternCall.In) { // Temporary hack to allow arguments 5 and 6 to be hinted as r10 and r11, then pushed later. f.ValueToStep[param].Hint(f.CPU.ExternCall.Volatile[1+r]) @@ -59,7 +44,7 @@ func (f *Compiler) CreateSteps(ir ssa.IR) []Step { } case *ssa.Syscall: - for r, param := range slices.Backward(instr.Arguments) { + for r, param := range instr.Arguments { f.ValueToStep[param].Hint(f.CPU.Syscall.In[r]) } } diff --git a/src/ssa2asm/Exec.go b/src/ssa2asm/Exec.go index cf9ad43..8ba1d15 100644 --- a/src/ssa2asm/Exec.go +++ b/src/ssa2asm/Exec.go @@ -27,37 +27,20 @@ func (f *Compiler) Exec(step *Step) { }) case *ssa.Call: - args := instr.Arguments[1:] - offset := 0 + args := instr.Arguments for i, arg := range args { - structure, isStruct := args[i].(*ssa.Struct) - - if isStruct { - for _, field := range structure.Arguments { - if f.ValueToStep[field].Register != f.CPU.Call.In[offset+i] { - f.Assembler.Append(&asm.MoveRegisterRegister{ - Destination: f.CPU.Call.In[offset+i], - Source: f.ValueToStep[field].Register, - }) - } - - offset++ - } - - offset-- - } else { - if f.ValueToStep[arg].Register != f.CPU.Call.In[offset+i] { - f.Assembler.Append(&asm.MoveRegisterRegister{ - Destination: f.CPU.Call.In[offset+i], - Source: f.ValueToStep[arg].Register, - }) - } + if f.ValueToStep[arg].Register == f.CPU.Call.In[i] { + continue } + + f.Assembler.Append(&asm.MoveRegisterRegister{ + Destination: f.CPU.Call.In[i], + Source: f.ValueToStep[arg].Register, + }) } - fn := instr.Arguments[0].(*ssa.Function) - f.Assembler.Append(&asm.Call{Label: fn.UniqueName}) + f.Assembler.Append(&asm.Call{Label: instr.Func.UniqueName}) if step.Register == -1 || step.Register == f.CPU.Call.Out[0] { return @@ -70,25 +53,30 @@ func (f *Compiler) Exec(step *Step) { case *ssa.CallExtern: f.Assembler.Append(&asm.CallExternStart{}) - args := instr.Arguments[1:] + args := instr.Arguments for i, arg := range args { if i >= len(f.CPU.ExternCall.In) { f.Assembler.Append(&asm.PushRegister{ Register: f.ValueToStep[arg].Register, }) - } else if f.ValueToStep[arg].Register != f.CPU.ExternCall.In[i] { - f.Assembler.Append(&asm.MoveRegisterRegister{ - Destination: f.CPU.ExternCall.In[i], - Source: f.ValueToStep[arg].Register, - }) + + continue } + + if f.ValueToStep[arg].Register == f.CPU.ExternCall.In[i] { + continue + } + + f.Assembler.Append(&asm.MoveRegisterRegister{ + Destination: f.CPU.ExternCall.In[i], + Source: f.ValueToStep[arg].Register, + }) } - fn := instr.Arguments[0].(*ssa.Function) - dot := strings.IndexByte(fn.UniqueName, '.') - library := fn.UniqueName[:dot] - function := fn.UniqueName[dot+1:] + dot := strings.IndexByte(instr.Func.UniqueName, '.') + library := instr.Func.UniqueName[:dot] + function := instr.Func.UniqueName[dot+1:] f.Assembler.Append(&asm.CallExtern{Library: library, Function: function}) f.Assembler.Append(&asm.CallExternEnd{})