diff --git a/src/codegen/GenerateAssembly.go b/src/codegen/GenerateAssembly.go index 3687580..d5276ad 100644 --- a/src/codegen/GenerateAssembly.go +++ b/src/codegen/GenerateAssembly.go @@ -15,24 +15,9 @@ func (f *Function) GenerateAssembly(ir ssa.IR, build *config.Build, hasStackFram f.build = build f.CPU = selectCPU(build) - // Transform SSA graph to a flat slice of steps we have to execute. + // Transform SSA graph to a flat slice of steps we can execute one by one. f.createSteps(ir) - // Execute all steps. - f.enter() - - for _, step := range f.Steps { - f.exec(step) - } - - if len(f.Steps) == 0 { - f.leave() - return - } - - _, lastIsReturn := f.Steps[len(f.Steps)-1].Value.(*ssa.Return) - - if !lastIsReturn { - f.leave() - } + // Execute all steps to produce assembly code. + f.generate() } \ No newline at end of file diff --git a/src/codegen/exec.go b/src/codegen/exec.go deleted file mode 100644 index ae72698..0000000 --- a/src/codegen/exec.go +++ /dev/null @@ -1,452 +0,0 @@ -package codegen - -import ( - "slices" - - "git.urbach.dev/cli/q/src/asm" - "git.urbach.dev/cli/q/src/cpu" - "git.urbach.dev/cli/q/src/ssa" - "git.urbach.dev/cli/q/src/token" -) - -// exec executes a step which appends it to the assembler's instruction list. -func (f *Function) exec(step *Step) { - switch instr := step.Value.(type) { - case *ssa.Assert: - f.jumpIfFalse(instr.Condition.(*ssa.BinaryOp).Op, "run.crash") - - case *ssa.BinaryOp: - left := f.ValueToStep[instr.Left] - right := f.ValueToStep[instr.Right] - - if instr.Op.IsComparison() { - number, isInt := right.Value.(*ssa.Int) - - if isInt && right.Register == -1 { - f.Assembler.Append(&asm.CompareNumber{Destination: left.Register, Number: number.Int}) - } else { - f.Assembler.Append(&asm.Compare{Destination: left.Register, Source: right.Register}) - } - - return - } - - number, isInt := right.Value.(*ssa.Int) - - if isInt && right.Register == -1 { - switch instr.Op { - case token.Add: - f.Assembler.Append(&asm.AddNumber{ - Destination: step.Register, - Source: left.Register, - Number: number.Int, - }) - - case token.And: - f.Assembler.Append(&asm.AndNumber{ - Destination: step.Register, - Source: left.Register, - Number: number.Int, - }) - - case token.Or: - f.Assembler.Append(&asm.OrNumber{ - Destination: step.Register, - Source: left.Register, - Number: number.Int, - }) - - case token.Shl: - f.Assembler.Append(&asm.ShiftLeftNumber{ - Destination: step.Register, - Source: left.Register, - Number: number.Int, - }) - - case token.Shr: - f.Assembler.Append(&asm.ShiftRightSignedNumber{ - Destination: step.Register, - Source: left.Register, - Number: number.Int, - }) - - case token.Sub: - f.Assembler.Append(&asm.SubtractNumber{ - Destination: step.Register, - Source: left.Register, - Number: number.Int, - }) - - case token.Xor: - f.Assembler.Append(&asm.XorNumber{ - Destination: step.Register, - Source: left.Register, - Number: number.Int, - }) - - default: - panic("not implemented: " + instr.String()) - } - - return - } - - switch instr.Op { - case token.Add: - f.Assembler.Append(&asm.Add{ - Destination: step.Register, - Source: left.Register, - Operand: right.Register, - }) - - case token.Div: - f.Assembler.Append(&asm.Divide{ - Destination: step.Register, - Source: left.Register, - Operand: right.Register, - }) - - case token.Mul: - f.Assembler.Append(&asm.Multiply{ - Destination: step.Register, - Source: left.Register, - Operand: right.Register, - }) - - case token.Sub: - f.Assembler.Append(&asm.Subtract{ - Destination: step.Register, - Source: left.Register, - Operand: right.Register, - }) - - case token.Mod: - f.Assembler.Append(&asm.Modulo{ - Destination: step.Register, - Source: left.Register, - Operand: right.Register, - }) - - case token.And: - f.Assembler.Append(&asm.And{ - Destination: step.Register, - Source: left.Register, - Operand: right.Register, - }) - - case token.Or: - f.Assembler.Append(&asm.Or{ - Destination: step.Register, - Source: left.Register, - Operand: right.Register, - }) - - case token.Xor: - f.Assembler.Append(&asm.Xor{ - Destination: step.Register, - Source: left.Register, - Operand: right.Register, - }) - - case token.Shl: - f.Assembler.Append(&asm.ShiftLeft{ - Destination: step.Register, - Source: left.Register, - Operand: right.Register, - }) - - case token.Shr: - f.Assembler.Append(&asm.ShiftRightSigned{ - Destination: step.Register, - Source: left.Register, - Operand: right.Register, - }) - - default: - panic("not implemented: " + instr.String()) - } - - case *ssa.Bool: - if step.Register == -1 { - return - } - - number := 0 - - if instr.Bool { - number = 1 - } - - f.Assembler.Append(&asm.MoveNumber{ - Destination: step.Register, - Number: number, - }) - - case *ssa.Branch: - var op token.Kind - binaryOp, isBinaryOp := instr.Condition.(*ssa.BinaryOp) - - if isBinaryOp && binaryOp.Op.IsComparison() { - op = binaryOp.Op - } else { - op = token.NotEqual - - f.Assembler.Append(&asm.CompareNumber{ - Destination: f.ValueToStep[instr.Condition].Register, - Number: 0, - }) - } - - f.insertPhiMoves(step) - following := f.Steps[step.Index+1].Value.(*Label) - - switch following.Name { - case instr.Then.Label: - f.jumpIfFalse(op, instr.Else.Label) - case instr.Else.Label: - f.jumpIfTrue(op, instr.Then.Label) - default: - panic("branch instruction must be followed by the 'then' or 'else' block") - } - - case *ssa.Bytes: - f.Count.Data++ - label := f.CreateLabel("data", f.Count.Data) - f.Assembler.SetData(label, instr.Bytes) - - f.Assembler.Append(&asm.MoveLabel{ - Destination: step.Register, - Label: label, - }) - - case *ssa.Call: - args := instr.Arguments - - for i, arg := range args { - if f.ValueToStep[arg].Register == f.CPU.Call.In[i] { - continue - } - - f.Assembler.Append(&asm.Move{ - Destination: f.CPU.Call.In[i], - Source: f.ValueToStep[arg].Register, - }) - } - - f.Assembler.Append(&asm.Call{Label: instr.Func.String()}) - - if step.Register == -1 || step.Register == f.CPU.Call.Out[0] { - return - } - - f.Assembler.Append(&asm.Move{ - Destination: step.Register, - Source: f.CPU.Call.Out[0], - }) - - case *ssa.CallExtern: - args := instr.Arguments - var pushed []cpu.Register - - for i, arg := range slices.Backward(args) { - if i >= len(f.CPU.ExternCall.In) { - pushed = append(pushed, f.ValueToStep[arg].Register) - continue - } - - if f.ValueToStep[arg].Register == f.CPU.ExternCall.In[i] { - continue - } - - f.Assembler.Append(&asm.Move{ - Destination: f.CPU.ExternCall.In[i], - Source: f.ValueToStep[arg].Register, - }) - } - - // Pushing an odd number of registers would not maintain the 16-byte - // stack alignment, so we allocate additional 8 bytes before pushing - // the 5th argument. - if len(pushed)&1 != 0 { - f.Assembler.Append(&asm.SubtractNumber{Destination: f.CPU.StackPointer, Source: f.CPU.StackPointer, Number: 8}) - } - - // TODO: Replace push instructions with store instructions using a fixed offset. - if len(pushed) > 0 { - f.Assembler.Append(&asm.Push{Registers: pushed}) - } - - f.Assembler.Append(&asm.CallExtern{Library: instr.Func.Package, Function: instr.Func.Name}) - - if len(pushed) > 0 { - f.Assembler.Append(&asm.Pop{Registers: pushed}) - } - - if len(pushed)&1 != 0 { - f.Assembler.Append(&asm.AddNumber{Destination: f.CPU.StackPointer, Source: f.CPU.StackPointer, Number: 8}) - } - - if step.Register == -1 || step.Register == f.CPU.ExternCall.Out[0] { - return - } - - f.Assembler.Append(&asm.Move{ - Destination: step.Register, - Source: f.CPU.ExternCall.Out[0], - }) - - case *ssa.Copy: - if step.Register == -1 { - return - } - - f.Assembler.Append(&asm.Move{ - Destination: step.Register, - Source: f.ValueToStep[instr.Value].Register, - }) - - case *ssa.FromTuple: - source := f.CPU.Call.Out[instr.Index] - - if step.Register == source { - return - } - - f.Assembler.Append(&asm.Move{ - Destination: step.Register, - Source: source, - }) - - case *ssa.Int: - if step.Register == -1 { - return - } - - f.Assembler.Append(&asm.MoveNumber{ - Destination: step.Register, - Number: instr.Int, - }) - - case *ssa.Jump: - f.insertPhiMoves(step) - f.Assembler.Append(&asm.Jump{Label: instr.To.Label}) - - case *Label: - f.Assembler.Append(&asm.Label{ - Name: instr.Name, - }) - - case *ssa.Load: - if step.Register == -1 { - return - } - - address := f.ValueToStep[instr.Address] - index := f.ValueToStep[instr.Index] - elementSize := step.Value.Type().Size() - - f.Assembler.Append(&asm.Load{ - Base: address.Register, - Index: index.Register, - Destination: step.Register, - Length: byte(elementSize), - }) - - case *ssa.Parameter: - source := f.CPU.Call.In[instr.Index] - - if step.Register == source { - return - } - - f.Assembler.Append(&asm.Move{ - Destination: step.Register, - Source: source, - }) - - case *ssa.Phi: - // Phi does not generate any machine instructions. - - case *ssa.Return: - defer f.leave() - - if len(instr.Arguments) == 0 { - return - } - - for i, arg := range instr.Arguments { - retVal := f.ValueToStep[arg] - - if retVal.Register == -1 || retVal.Register == f.CPU.Call.Out[i] { - return - } - - f.Assembler.Append(&asm.Move{ - Destination: f.CPU.Call.Out[i], - Source: retVal.Register, - }) - } - - case *ssa.Store: - address := f.ValueToStep[instr.Address] - index := f.ValueToStep[instr.Index] - source := f.ValueToStep[instr.Value] - - if source.Register == -1 { - f.Assembler.Append(&asm.StoreNumber{ - Base: address.Register, - Index: index.Register, - Number: source.Value.(*ssa.Int).Int, - Length: byte(instr.Length), - }) - } else { - f.Assembler.Append(&asm.Store{ - Base: address.Register, - Index: index.Register, - Source: source.Register, - Length: byte(instr.Length), - }) - } - - case *ssa.Syscall: - for i, arg := range instr.Arguments { - if f.ValueToStep[arg].Register != f.CPU.Syscall.In[i] { - f.Assembler.Append(&asm.Move{ - Destination: f.CPU.Syscall.In[i], - Source: f.ValueToStep[arg].Register, - }) - } - } - - f.Assembler.Append(&asm.Syscall{}) - - if step.Register == -1 || step.Register == f.CPU.Syscall.Out[0] { - return - } - - f.Assembler.Append(&asm.Move{ - Destination: step.Register, - Source: f.CPU.Syscall.Out[0], - }) - - case *ssa.UnaryOp: - left := f.ValueToStep[instr.Operand] - - switch instr.Op { - case token.Negate: - f.Assembler.Append(&asm.Negate{ - Destination: step.Register, - Source: left.Register, - }) - - case token.Not: - panic("not implemented: logical not") - - default: - panic("not implemented: " + instr.String()) - } - - default: - panic("not implemented: " + instr.String()) - } -} \ No newline at end of file diff --git a/src/codegen/execute.go b/src/codegen/execute.go new file mode 100644 index 0000000..9efe602 --- /dev/null +++ b/src/codegen/execute.go @@ -0,0 +1,51 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/ssa" +) + +// execute executes a step which appends it to the assembler's instruction list. +func (f *Function) execute(step *Step) { + switch instr := step.Value.(type) { + case *ssa.Assert: + f.executeAssert(instr) + case *ssa.BinaryOp: + f.executeBinaryOp(step, instr) + case *ssa.Bool: + f.executeBool(step, instr) + case *ssa.Branch: + f.executeBranch(step, instr) + case *ssa.Bytes: + f.executeBytes(step, instr) + case *ssa.Call: + f.executeCall(step, instr) + case *ssa.CallExtern: + f.executeCallExtern(step, instr) + case *ssa.Copy: + f.executeCopy(step, instr) + case *ssa.FromTuple: + f.executeFromTuple(step, instr) + case *ssa.Int: + f.executeInt(step, instr) + case *ssa.Jump: + f.executeJump(step, instr) + case *Label: + f.executeLabel(instr) + case *ssa.Load: + f.executeLoad(step, instr) + case *ssa.Parameter: + f.executeParameter(step, instr) + case *ssa.Phi: + f.executePhi(instr) + case *ssa.Return: + f.executeReturn(instr) + case *ssa.Store: + f.executeStore(instr) + case *ssa.Syscall: + f.executeSyscall(step, instr) + case *ssa.UnaryOp: + f.executeUnaryOp(step, instr) + default: + panic("not implemented: " + instr.String()) + } +} \ No newline at end of file diff --git a/src/codegen/executeAssert.go b/src/codegen/executeAssert.go new file mode 100644 index 0000000..e9d59c9 --- /dev/null +++ b/src/codegen/executeAssert.go @@ -0,0 +1,9 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/ssa" +) + +func (f *Function) executeAssert(instr *ssa.Assert) { + f.jumpIfFalse(instr.Condition.(*ssa.BinaryOp).Op, "run.crash") +} \ No newline at end of file diff --git a/src/codegen/executeBinaryOp.go b/src/codegen/executeBinaryOp.go new file mode 100644 index 0000000..47db69f --- /dev/null +++ b/src/codegen/executeBinaryOp.go @@ -0,0 +1,159 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/ssa" + "git.urbach.dev/cli/q/src/token" +) + +func (f *Function) executeBinaryOp(step *Step, instr *ssa.BinaryOp) { + left := f.ValueToStep[instr.Left] + right := f.ValueToStep[instr.Right] + + if instr.Op.IsComparison() { + number, isInt := right.Value.(*ssa.Int) + + if isInt && right.Register == -1 { + f.Assembler.Append(&asm.CompareNumber{Destination: left.Register, Number: number.Int}) + } else { + f.Assembler.Append(&asm.Compare{Destination: left.Register, Source: right.Register}) + } + + return + } + + number, isInt := right.Value.(*ssa.Int) + + if isInt && right.Register == -1 { + switch instr.Op { + case token.Add: + f.Assembler.Append(&asm.AddNumber{ + Destination: step.Register, + Source: left.Register, + Number: number.Int, + }) + + case token.And: + f.Assembler.Append(&asm.AndNumber{ + Destination: step.Register, + Source: left.Register, + Number: number.Int, + }) + + case token.Or: + f.Assembler.Append(&asm.OrNumber{ + Destination: step.Register, + Source: left.Register, + Number: number.Int, + }) + + case token.Shl: + f.Assembler.Append(&asm.ShiftLeftNumber{ + Destination: step.Register, + Source: left.Register, + Number: number.Int, + }) + + case token.Shr: + f.Assembler.Append(&asm.ShiftRightSignedNumber{ + Destination: step.Register, + Source: left.Register, + Number: number.Int, + }) + + case token.Sub: + f.Assembler.Append(&asm.SubtractNumber{ + Destination: step.Register, + Source: left.Register, + Number: number.Int, + }) + + case token.Xor: + f.Assembler.Append(&asm.XorNumber{ + Destination: step.Register, + Source: left.Register, + Number: number.Int, + }) + + default: + panic("not implemented: " + instr.String()) + } + + return + } + + switch instr.Op { + case token.Add: + f.Assembler.Append(&asm.Add{ + Destination: step.Register, + Source: left.Register, + Operand: right.Register, + }) + + case token.Div: + f.Assembler.Append(&asm.Divide{ + Destination: step.Register, + Source: left.Register, + Operand: right.Register, + }) + + case token.Mul: + f.Assembler.Append(&asm.Multiply{ + Destination: step.Register, + Source: left.Register, + Operand: right.Register, + }) + + case token.Sub: + f.Assembler.Append(&asm.Subtract{ + Destination: step.Register, + Source: left.Register, + Operand: right.Register, + }) + + case token.Mod: + f.Assembler.Append(&asm.Modulo{ + Destination: step.Register, + Source: left.Register, + Operand: right.Register, + }) + + case token.And: + f.Assembler.Append(&asm.And{ + Destination: step.Register, + Source: left.Register, + Operand: right.Register, + }) + + case token.Or: + f.Assembler.Append(&asm.Or{ + Destination: step.Register, + Source: left.Register, + Operand: right.Register, + }) + + case token.Xor: + f.Assembler.Append(&asm.Xor{ + Destination: step.Register, + Source: left.Register, + Operand: right.Register, + }) + + case token.Shl: + f.Assembler.Append(&asm.ShiftLeft{ + Destination: step.Register, + Source: left.Register, + Operand: right.Register, + }) + + case token.Shr: + f.Assembler.Append(&asm.ShiftRightSigned{ + Destination: step.Register, + Source: left.Register, + Operand: right.Register, + }) + + default: + panic("not implemented: " + instr.String()) + } +} \ No newline at end of file diff --git a/src/codegen/executeBool.go b/src/codegen/executeBool.go new file mode 100644 index 0000000..ff31a52 --- /dev/null +++ b/src/codegen/executeBool.go @@ -0,0 +1,23 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/ssa" +) + +func (f *Function) executeBool(step *Step, instr *ssa.Bool) { + if step.Register == -1 { + return + } + + number := 0 + + if instr.Bool { + number = 1 + } + + f.Assembler.Append(&asm.MoveNumber{ + Destination: step.Register, + Number: number, + }) +} \ No newline at end of file diff --git a/src/codegen/executeBranch.go b/src/codegen/executeBranch.go new file mode 100644 index 0000000..4cd20a7 --- /dev/null +++ b/src/codegen/executeBranch.go @@ -0,0 +1,35 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/ssa" + "git.urbach.dev/cli/q/src/token" +) + +func (f *Function) executeBranch(step *Step, instr *ssa.Branch) { + var op token.Kind + binaryOp, isBinaryOp := instr.Condition.(*ssa.BinaryOp) + + if isBinaryOp && binaryOp.Op.IsComparison() { + op = binaryOp.Op + } else { + op = token.NotEqual + + f.Assembler.Append(&asm.CompareNumber{ + Destination: f.ValueToStep[instr.Condition].Register, + Number: 0, + }) + } + + f.insertPhiMoves(step) + following := f.Steps[step.Index+1].Value.(*Label) + + switch following.Name { + case instr.Then.Label: + f.jumpIfFalse(op, instr.Else.Label) + case instr.Else.Label: + f.jumpIfTrue(op, instr.Then.Label) + default: + panic("branch instruction must be followed by the 'then' or 'else' block") + } +} \ No newline at end of file diff --git a/src/codegen/executeBytes.go b/src/codegen/executeBytes.go new file mode 100644 index 0000000..6929d42 --- /dev/null +++ b/src/codegen/executeBytes.go @@ -0,0 +1,17 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/ssa" +) + +func (f *Function) executeBytes(step *Step, instr *ssa.Bytes) { + f.Count.Data++ + label := f.CreateLabel("data", f.Count.Data) + f.Assembler.SetData(label, instr.Bytes) + + f.Assembler.Append(&asm.MoveLabel{ + Destination: step.Register, + Label: label, + }) +} \ No newline at end of file diff --git a/src/codegen/executeCall.go b/src/codegen/executeCall.go new file mode 100644 index 0000000..3edf469 --- /dev/null +++ b/src/codegen/executeCall.go @@ -0,0 +1,32 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/ssa" +) + +func (f *Function) executeCall(step *Step, instr *ssa.Call) { + args := instr.Arguments + + for i, arg := range args { + if f.ValueToStep[arg].Register == f.CPU.Call.In[i] { + continue + } + + f.Assembler.Append(&asm.Move{ + Destination: f.CPU.Call.In[i], + Source: f.ValueToStep[arg].Register, + }) + } + + f.Assembler.Append(&asm.Call{Label: instr.Func.String()}) + + if step.Register == -1 || step.Register == f.CPU.Call.Out[0] { + return + } + + f.Assembler.Append(&asm.Move{ + Destination: step.Register, + Source: f.CPU.Call.Out[0], + }) +} \ No newline at end of file diff --git a/src/codegen/executeCallExtern.go b/src/codegen/executeCallExtern.go new file mode 100644 index 0000000..1490e24 --- /dev/null +++ b/src/codegen/executeCallExtern.go @@ -0,0 +1,61 @@ +package codegen + +import ( + "slices" + + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/ssa" +) + +func (f *Function) executeCallExtern(step *Step, instr *ssa.CallExtern) { + args := instr.Arguments + var pushed []cpu.Register + + for i, arg := range slices.Backward(args) { + if i >= len(f.CPU.ExternCall.In) { + pushed = append(pushed, f.ValueToStep[arg].Register) + continue + } + + if f.ValueToStep[arg].Register == f.CPU.ExternCall.In[i] { + continue + } + + f.Assembler.Append(&asm.Move{ + Destination: f.CPU.ExternCall.In[i], + Source: f.ValueToStep[arg].Register, + }) + } + + // Pushing an odd number of registers would not maintain the 16-byte + // stack alignment, so we allocate additional 8 bytes before pushing + // the 5th argument. + if len(pushed)&1 != 0 { + f.Assembler.Append(&asm.SubtractNumber{Destination: f.CPU.StackPointer, Source: f.CPU.StackPointer, Number: 8}) + } + + // TODO: Replace push instructions with store instructions using a fixed offset. + if len(pushed) > 0 { + f.Assembler.Append(&asm.Push{Registers: pushed}) + } + + f.Assembler.Append(&asm.CallExtern{Library: instr.Func.Package, Function: instr.Func.Name}) + + if len(pushed) > 0 { + f.Assembler.Append(&asm.Pop{Registers: pushed}) + } + + if len(pushed)&1 != 0 { + f.Assembler.Append(&asm.AddNumber{Destination: f.CPU.StackPointer, Source: f.CPU.StackPointer, Number: 8}) + } + + if step.Register == -1 || step.Register == f.CPU.ExternCall.Out[0] { + return + } + + f.Assembler.Append(&asm.Move{ + Destination: step.Register, + Source: f.CPU.ExternCall.Out[0], + }) +} \ No newline at end of file diff --git a/src/codegen/executeCopy.go b/src/codegen/executeCopy.go new file mode 100644 index 0000000..9a49f8f --- /dev/null +++ b/src/codegen/executeCopy.go @@ -0,0 +1,17 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/ssa" +) + +func (f *Function) executeCopy(step *Step, instr *ssa.Copy) { + if step.Register == -1 { + return + } + + f.Assembler.Append(&asm.Move{ + Destination: step.Register, + Source: f.ValueToStep[instr.Value].Register, + }) +} \ No newline at end of file diff --git a/src/codegen/executeFromTuple.go b/src/codegen/executeFromTuple.go new file mode 100644 index 0000000..d0626c1 --- /dev/null +++ b/src/codegen/executeFromTuple.go @@ -0,0 +1,19 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/ssa" +) + +func (f *Function) executeFromTuple(step *Step, instr *ssa.FromTuple) { + source := f.CPU.Call.Out[instr.Index] + + if step.Register == source { + return + } + + f.Assembler.Append(&asm.Move{ + Destination: step.Register, + Source: source, + }) +} \ No newline at end of file diff --git a/src/codegen/executeInt.go b/src/codegen/executeInt.go new file mode 100644 index 0000000..3b7ec49 --- /dev/null +++ b/src/codegen/executeInt.go @@ -0,0 +1,17 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/ssa" +) + +func (f *Function) executeInt(step *Step, instr *ssa.Int) { + if step.Register == -1 { + return + } + + f.Assembler.Append(&asm.MoveNumber{ + Destination: step.Register, + Number: instr.Int, + }) +} \ No newline at end of file diff --git a/src/codegen/executeJump.go b/src/codegen/executeJump.go new file mode 100644 index 0000000..14d7eb2 --- /dev/null +++ b/src/codegen/executeJump.go @@ -0,0 +1,11 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/ssa" +) + +func (f *Function) executeJump(step *Step, instr *ssa.Jump) { + f.insertPhiMoves(step) + f.Assembler.Append(&asm.Jump{Label: instr.To.Label}) +} \ No newline at end of file diff --git a/src/codegen/executeLabel.go b/src/codegen/executeLabel.go new file mode 100644 index 0000000..2daa054 --- /dev/null +++ b/src/codegen/executeLabel.go @@ -0,0 +1,11 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/asm" +) + +func (f *Function) executeLabel(instr *Label) { + f.Assembler.Append(&asm.Label{ + Name: instr.Name, + }) +} \ No newline at end of file diff --git a/src/codegen/executeLoad.go b/src/codegen/executeLoad.go new file mode 100644 index 0000000..e3d5d14 --- /dev/null +++ b/src/codegen/executeLoad.go @@ -0,0 +1,23 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/ssa" +) + +func (f *Function) executeLoad(step *Step, instr *ssa.Load) { + if step.Register == -1 { + return + } + + address := f.ValueToStep[instr.Address] + index := f.ValueToStep[instr.Index] + elementSize := step.Value.Type().Size() + + f.Assembler.Append(&asm.Load{ + Base: address.Register, + Index: index.Register, + Destination: step.Register, + Length: byte(elementSize), + }) +} \ No newline at end of file diff --git a/src/codegen/executeParameter.go b/src/codegen/executeParameter.go new file mode 100644 index 0000000..08354c2 --- /dev/null +++ b/src/codegen/executeParameter.go @@ -0,0 +1,19 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/ssa" +) + +func (f *Function) executeParameter(step *Step, instr *ssa.Parameter) { + source := f.CPU.Call.In[instr.Index] + + if step.Register == source { + return + } + + f.Assembler.Append(&asm.Move{ + Destination: step.Register, + Source: source, + }) +} \ No newline at end of file diff --git a/src/codegen/executePhi.go b/src/codegen/executePhi.go new file mode 100644 index 0000000..d3670a9 --- /dev/null +++ b/src/codegen/executePhi.go @@ -0,0 +1,9 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/ssa" +) + +func (f *Function) executePhi(instr *ssa.Phi) { + // Phi does not generate any machine instructions. +} \ No newline at end of file diff --git a/src/codegen/executeReturn.go b/src/codegen/executeReturn.go new file mode 100644 index 0000000..a42781a --- /dev/null +++ b/src/codegen/executeReturn.go @@ -0,0 +1,27 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/ssa" +) + +func (f *Function) executeReturn(instr *ssa.Return) { + defer f.leave() + + if len(instr.Arguments) == 0 { + return + } + + for i, arg := range instr.Arguments { + retVal := f.ValueToStep[arg] + + if retVal.Register == -1 || retVal.Register == f.CPU.Call.Out[i] { + return + } + + f.Assembler.Append(&asm.Move{ + Destination: f.CPU.Call.Out[i], + Source: retVal.Register, + }) + } +} \ No newline at end of file diff --git a/src/codegen/executeStore.go b/src/codegen/executeStore.go new file mode 100644 index 0000000..2910edf --- /dev/null +++ b/src/codegen/executeStore.go @@ -0,0 +1,28 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/ssa" +) + +func (f *Function) executeStore(instr *ssa.Store) { + address := f.ValueToStep[instr.Address] + index := f.ValueToStep[instr.Index] + source := f.ValueToStep[instr.Value] + + if source.Register == -1 { + f.Assembler.Append(&asm.StoreNumber{ + Base: address.Register, + Index: index.Register, + Number: source.Value.(*ssa.Int).Int, + Length: byte(instr.Length), + }) + } else { + f.Assembler.Append(&asm.Store{ + Base: address.Register, + Index: index.Register, + Source: source.Register, + Length: byte(instr.Length), + }) + } +} \ No newline at end of file diff --git a/src/codegen/executeSyscall.go b/src/codegen/executeSyscall.go new file mode 100644 index 0000000..b70e20b --- /dev/null +++ b/src/codegen/executeSyscall.go @@ -0,0 +1,28 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/ssa" +) + +func (f *Function) executeSyscall(step *Step, instr *ssa.Syscall) { + for i, arg := range instr.Arguments { + if f.ValueToStep[arg].Register != f.CPU.Syscall.In[i] { + f.Assembler.Append(&asm.Move{ + Destination: f.CPU.Syscall.In[i], + Source: f.ValueToStep[arg].Register, + }) + } + } + + f.Assembler.Append(&asm.Syscall{}) + + if step.Register == -1 || step.Register == f.CPU.Syscall.Out[0] { + return + } + + f.Assembler.Append(&asm.Move{ + Destination: step.Register, + Source: f.CPU.Syscall.Out[0], + }) +} \ No newline at end of file diff --git a/src/codegen/executeUnaryOp.go b/src/codegen/executeUnaryOp.go new file mode 100644 index 0000000..34bf949 --- /dev/null +++ b/src/codegen/executeUnaryOp.go @@ -0,0 +1,25 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/ssa" + "git.urbach.dev/cli/q/src/token" +) + +func (f *Function) executeUnaryOp(step *Step, instr *ssa.UnaryOp) { + left := f.ValueToStep[instr.Operand] + + switch instr.Op { + case token.Negate: + f.Assembler.Append(&asm.Negate{ + Destination: step.Register, + Source: left.Register, + }) + + case token.Not: + panic("not implemented: logical not") + + default: + panic("not implemented: " + instr.String()) + } +} \ No newline at end of file diff --git a/src/codegen/generate.go b/src/codegen/generate.go new file mode 100644 index 0000000..fe0a1e6 --- /dev/null +++ b/src/codegen/generate.go @@ -0,0 +1,24 @@ +package codegen + +import ( + "git.urbach.dev/cli/q/src/ssa" +) + +// generate executes all steps. +func (f *Function) generate() { + f.enter() + + for _, step := range f.Steps { + f.execute(step) + } + + if len(f.Steps) > 0 { + _, lastIsReturn := f.Steps[len(f.Steps)-1].Value.(*ssa.Return) + + if lastIsReturn { + return + } + } + + f.leave() +} \ No newline at end of file