This commit is contained in:
parent
f357285045
commit
0ece1b092e
23 changed files with 274 additions and 113 deletions
|
@ -18,7 +18,7 @@ func Compile(b *build.Build) (*core.Environment, error) {
|
|||
}
|
||||
|
||||
// Check for existence of `init`
|
||||
init, exists := all.Functions["core.init"]
|
||||
init, exists := all.Functions["run.init"]
|
||||
|
||||
if !exists {
|
||||
return nil, MissingInitFunction
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
// CheckDeadCode checks for dead values.
|
||||
func (f *Function) CheckDeadCode() error {
|
||||
for instr := range f.Values {
|
||||
for _, instr := range f.Values {
|
||||
if !instr.IsConst() {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -180,7 +180,7 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
|
|||
}
|
||||
|
||||
v := f.Append(&ssa.Field{
|
||||
Struct: identifier,
|
||||
Object: identifier,
|
||||
Field: field,
|
||||
Source: ssa.Source(expr.Source),
|
||||
})
|
||||
|
|
|
@ -7,5 +7,9 @@ type Register int8
|
|||
|
||||
// String returns the human readable name of the register.
|
||||
func (r Register) String() string {
|
||||
if r < 0 {
|
||||
return "r?"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("r%d", r)
|
||||
}
|
|
@ -20,7 +20,7 @@ func WriteFile(executable string, b *build.Build, env *core.Environment) error {
|
|||
return err
|
||||
}
|
||||
|
||||
init := env.Functions["core.init"]
|
||||
init := env.Functions["run.init"]
|
||||
traversed := make(map[*core.Function]bool, len(env.Functions))
|
||||
|
||||
final := asm.Assembler{
|
||||
|
|
|
@ -19,7 +19,7 @@ func Scan(b *build.Build) (*core.Environment, error) {
|
|||
}
|
||||
|
||||
go func() {
|
||||
s.queueDirectory(filepath.Join(global.Library, "core"), "core")
|
||||
s.queueDirectory(filepath.Join(global.Library, "run"), "run")
|
||||
s.queue(b.Files...)
|
||||
s.group.Wait()
|
||||
close(s.functions)
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
)
|
||||
|
||||
type Field struct {
|
||||
Struct Value
|
||||
Object Value
|
||||
Field *types.Field
|
||||
Liveness
|
||||
Source
|
||||
|
@ -15,8 +15,8 @@ type Field struct {
|
|||
|
||||
func (v *Field) IsConst() bool { return true }
|
||||
func (v *Field) Type() types.Type { return v.Field.Type }
|
||||
func (v *Field) String() string { return fmt.Sprintf("%s.%s", v.Struct, v.Field) }
|
||||
func (v *Field) Inputs() []Value { return []Value{v.Struct} }
|
||||
func (v *Field) String() string { return fmt.Sprintf("%s.%s", v.Object, v.Field) }
|
||||
func (v *Field) Inputs() []Value { return []Value{v.Object} }
|
||||
|
||||
func (a *Field) Equals(v Value) bool {
|
||||
b, sameType := v.(*Field)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package ssa
|
||||
|
||||
import "slices"
|
||||
|
||||
// IR is a list of basic blocks.
|
||||
type IR struct {
|
||||
Blocks []*Block
|
||||
|
@ -30,13 +32,24 @@ func (f *IR) Append(instr Value) Value {
|
|||
return f.Blocks[len(f.Blocks)-1].Append(instr)
|
||||
}
|
||||
|
||||
// CountValues returns the total number of values.
|
||||
func (f *IR) CountValues() int {
|
||||
count := 0
|
||||
|
||||
for _, block := range f.Blocks {
|
||||
count += len(block.Instructions)
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
// FindExisting returns an equal instruction that's already appended or `nil` if none could be found.
|
||||
func (f *IR) FindExisting(instr Value) Value {
|
||||
if !instr.IsConst() {
|
||||
return nil
|
||||
}
|
||||
|
||||
for existing := range f.Values {
|
||||
for _, existing := range f.Values {
|
||||
if existing.IsConst() && instr.Equals(existing) {
|
||||
return existing
|
||||
}
|
||||
|
@ -46,10 +59,29 @@ func (f *IR) FindExisting(instr Value) Value {
|
|||
}
|
||||
|
||||
// Values yields on each value.
|
||||
func (f *IR) Values(yield func(Value) bool) {
|
||||
func (f *IR) Values(yield func(int, Value) bool) {
|
||||
index := 0
|
||||
|
||||
for _, block := range f.Blocks {
|
||||
for _, instr := range block.Instructions {
|
||||
if !yield(instr) {
|
||||
if !yield(index, instr) {
|
||||
return
|
||||
}
|
||||
|
||||
index++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ValuesBackward yields on each value from the end towards the start.
|
||||
func (f *IR) ValuesBackward(yield func(int, Value) bool) {
|
||||
index := f.CountValues()
|
||||
|
||||
for _, block := range slices.Backward(f.Blocks) {
|
||||
for _, instr := range slices.Backward(block.Instructions) {
|
||||
index--
|
||||
|
||||
if !yield(index, instr) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,13 @@ package ssa2asm
|
|||
import (
|
||||
"git.urbach.dev/cli/q/src/asm"
|
||||
"git.urbach.dev/cli/q/src/cpu"
|
||||
"git.urbach.dev/cli/q/src/ssa"
|
||||
)
|
||||
|
||||
type Compiler struct {
|
||||
UniqueName string
|
||||
Assembler asm.Assembler
|
||||
CPU *cpu.CPU
|
||||
Count Count
|
||||
UniqueName string
|
||||
Assembler asm.Assembler
|
||||
ValueToStep map[ssa.Value]*Step
|
||||
CPU *cpu.CPU
|
||||
Count Count
|
||||
}
|
103
src/ssa2asm/CreateSteps.go
Normal file
103
src/ssa2asm/CreateSteps.go
Normal file
|
@ -0,0 +1,103 @@
|
|||
package ssa2asm
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"git.urbach.dev/cli/q/src/cpu"
|
||||
"git.urbach.dev/cli/q/src/ssa"
|
||||
)
|
||||
|
||||
func (f *Compiler) CreateSteps(ir ssa.IR) []Step {
|
||||
count := ir.CountValues()
|
||||
steps := make([]Step, count)
|
||||
f.ValueToStep = make(map[ssa.Value]*Step, count)
|
||||
|
||||
for i, instr := range ir.Values {
|
||||
steps[i].Index = i
|
||||
steps[i].Value = instr
|
||||
steps[i].Register = -1
|
||||
f.ValueToStep[instr] = &steps[i]
|
||||
}
|
||||
|
||||
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[offset+r])
|
||||
offset++
|
||||
}
|
||||
|
||||
offset--
|
||||
} else {
|
||||
f.ValueToStep[param].Hint(f.CPU.Call[offset+r])
|
||||
}
|
||||
}
|
||||
|
||||
case *ssa.CallExtern:
|
||||
for r, param := range instr.Arguments[1:] {
|
||||
f.ValueToStep[param].Hint(f.CPU.ExternCall[r])
|
||||
}
|
||||
|
||||
case *ssa.Parameter:
|
||||
f.ValueToStep[instr].Register = f.CPU.Call[instr.Index]
|
||||
|
||||
case *ssa.Return:
|
||||
for r, param := range instr.Arguments {
|
||||
f.ValueToStep[param].Hint(f.CPU.Return[r])
|
||||
}
|
||||
|
||||
case *ssa.Syscall:
|
||||
for r, param := range slices.Backward(instr.Arguments) {
|
||||
f.ValueToStep[param].Hint(f.CPU.Syscall[r])
|
||||
}
|
||||
}
|
||||
|
||||
users := instr.Users()
|
||||
|
||||
if len(users) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
liveStart := i
|
||||
liveEnd := f.ValueToStep[users[len(users)-1]].Index
|
||||
instrStep := f.ValueToStep[instr]
|
||||
|
||||
for live := liveStart; live < liveEnd; live++ {
|
||||
steps[live].Live = append(steps[live].Live, instrStep)
|
||||
}
|
||||
}
|
||||
|
||||
for _, step := range steps {
|
||||
for liveIndex, live := range step.Live {
|
||||
if live.Register == -1 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, existing := range step.Live[:liveIndex] {
|
||||
if existing.Register == -1 {
|
||||
continue
|
||||
}
|
||||
|
||||
if existing.Register == live.Register {
|
||||
a := existing.Index
|
||||
b := live.Index
|
||||
freeRegister := cpu.Register(15)
|
||||
|
||||
if a < b {
|
||||
existing.Register = freeRegister
|
||||
} else {
|
||||
live.Register = freeRegister
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return steps
|
||||
}
|
|
@ -1,25 +1,19 @@
|
|||
package ssa2asm
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"git.urbach.dev/cli/q/src/asm"
|
||||
"git.urbach.dev/cli/q/src/cpu"
|
||||
"git.urbach.dev/cli/q/src/ssa"
|
||||
)
|
||||
|
||||
// ValueToRegister moves a value into the given `destination` register.
|
||||
func (f *Compiler) ValueToRegister(instr ssa.Value, destination cpu.Register) {
|
||||
switch instr := instr.(type) {
|
||||
func (f *Compiler) Exec(step *Step) {
|
||||
switch instr := step.Value.(type) {
|
||||
case *ssa.BinaryOp:
|
||||
f.ValueToRegister(instr.Left, destination)
|
||||
f.ValueToRegister(instr.Right, 7)
|
||||
|
||||
f.Assembler.Append(&asm.AddRegisterRegister{
|
||||
Destination: destination,
|
||||
Source: destination,
|
||||
Operand: 7,
|
||||
Destination: step.Register,
|
||||
Source: f.ValueToStep[instr.Left].Register,
|
||||
Operand: f.ValueToStep[instr.Right].Register,
|
||||
})
|
||||
|
||||
case *ssa.Bytes:
|
||||
|
@ -28,106 +22,129 @@ func (f *Compiler) ValueToRegister(instr ssa.Value, destination cpu.Register) {
|
|||
f.Assembler.SetData(label.Name, instr.Bytes)
|
||||
|
||||
f.Assembler.Append(&asm.MoveRegisterLabel{
|
||||
Destination: destination,
|
||||
Destination: step.Register,
|
||||
Label: label.Name,
|
||||
})
|
||||
|
||||
case *ssa.Call:
|
||||
fn := instr.Arguments[0].(*ssa.Function)
|
||||
args := instr.Arguments[1:]
|
||||
offset := 0
|
||||
|
||||
for i := range slices.Backward(args) {
|
||||
for i, arg := range args {
|
||||
structure, isStruct := args[i].(*ssa.Struct)
|
||||
|
||||
if isStruct {
|
||||
for _, field := range structure.Arguments {
|
||||
f.ValueToRegister(field, f.CPU.Call[offset+i])
|
||||
i++
|
||||
if f.ValueToStep[field].Register != f.CPU.Call[offset+i] {
|
||||
f.Assembler.Append(&asm.MoveRegisterRegister{
|
||||
Destination: f.CPU.Call[offset+i],
|
||||
Source: f.ValueToStep[field].Register,
|
||||
})
|
||||
}
|
||||
|
||||
offset++
|
||||
}
|
||||
|
||||
offset--
|
||||
} else {
|
||||
f.ValueToRegister(args[i], f.CPU.Call[offset+i])
|
||||
if f.ValueToStep[arg].Register != f.CPU.Call[offset+i] {
|
||||
f.Assembler.Append(&asm.MoveRegisterRegister{
|
||||
Destination: f.CPU.Call[offset+i],
|
||||
Source: f.ValueToStep[arg].Register,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn := instr.Arguments[0].(*ssa.Function)
|
||||
f.Assembler.Append(&asm.Call{Label: fn.UniqueName})
|
||||
|
||||
if destination == f.CPU.Return[0] {
|
||||
if step.Register == -1 || step.Register == f.CPU.Return[0] {
|
||||
return
|
||||
}
|
||||
|
||||
f.Assembler.Append(&asm.MoveRegisterRegister{
|
||||
Destination: destination,
|
||||
Destination: step.Register,
|
||||
Source: f.CPU.Return[0],
|
||||
})
|
||||
|
||||
case *ssa.CallExtern:
|
||||
fn := instr.Arguments[0].(*ssa.Function)
|
||||
args := instr.Arguments[1:]
|
||||
|
||||
for i := range slices.Backward(args) {
|
||||
f.ValueToRegister(args[i], f.CPU.ExternCall[i])
|
||||
for i, arg := range args {
|
||||
if f.ValueToStep[arg].Register != f.CPU.ExternCall[i] {
|
||||
f.Assembler.Append(&asm.MoveRegisterRegister{
|
||||
Destination: f.CPU.ExternCall[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:]
|
||||
f.Assembler.Append(&asm.CallExtern{Library: library, Function: function})
|
||||
|
||||
if destination == f.CPU.Return[0] {
|
||||
if step.Register == -1 || step.Register == f.CPU.Return[0] {
|
||||
return
|
||||
}
|
||||
|
||||
f.Assembler.Append(&asm.MoveRegisterRegister{
|
||||
Destination: destination,
|
||||
Destination: step.Register,
|
||||
Source: f.CPU.Return[0],
|
||||
})
|
||||
|
||||
case *ssa.Int:
|
||||
f.Assembler.Append(&asm.MoveRegisterNumber{
|
||||
Destination: destination,
|
||||
Destination: step.Register,
|
||||
Number: instr.Int,
|
||||
})
|
||||
|
||||
case *ssa.Parameter:
|
||||
source := f.CPU.Call[instr.Index]
|
||||
|
||||
if source == destination {
|
||||
if step.Register == -1 || step.Register == source {
|
||||
return
|
||||
}
|
||||
|
||||
f.Assembler.Append(&asm.MoveRegisterRegister{
|
||||
Destination: destination,
|
||||
Destination: step.Register,
|
||||
Source: source,
|
||||
})
|
||||
|
||||
case *ssa.Field:
|
||||
parameter := instr.Struct.(*ssa.Parameter)
|
||||
parameter := instr.Object.(*ssa.Parameter)
|
||||
field := instr.Field
|
||||
source := f.CPU.Call[parameter.Index+field.Index]
|
||||
|
||||
if source == destination {
|
||||
if step.Register == -1 || step.Register == source {
|
||||
return
|
||||
}
|
||||
|
||||
f.Assembler.Append(&asm.MoveRegisterRegister{
|
||||
Destination: destination,
|
||||
Destination: step.Register,
|
||||
Source: source,
|
||||
})
|
||||
|
||||
case *ssa.Syscall:
|
||||
for i := range slices.Backward(instr.Arguments) {
|
||||
f.ValueToRegister(instr.Arguments[i], f.CPU.Syscall[i])
|
||||
for i, arg := range instr.Arguments {
|
||||
if f.ValueToStep[arg].Register != f.CPU.Syscall[i] {
|
||||
f.Assembler.Append(&asm.MoveRegisterRegister{
|
||||
Destination: f.CPU.Syscall[i],
|
||||
Source: f.ValueToStep[arg].Register,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
f.Assembler.Append(&asm.Syscall{})
|
||||
|
||||
if destination == f.CPU.Return[0] {
|
||||
if step.Register == -1 || step.Register == f.CPU.Return[0] {
|
||||
return
|
||||
}
|
||||
|
||||
f.Assembler.Append(&asm.MoveRegisterRegister{
|
||||
Destination: destination,
|
||||
Destination: step.Register,
|
||||
Source: f.CPU.Return[0],
|
||||
})
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
package ssa2asm
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"git.urbach.dev/cli/q/src/asm"
|
||||
"git.urbach.dev/cli/q/src/ssa"
|
||||
)
|
||||
|
@ -11,33 +9,21 @@ import (
|
|||
func (f *Compiler) GenerateAssembly(ir ssa.IR, isLeaf bool) {
|
||||
f.Assembler.Append(&asm.Label{Name: f.UniqueName})
|
||||
|
||||
if !isLeaf && f.UniqueName != "core.init" {
|
||||
if !isLeaf && f.UniqueName != "run.init" {
|
||||
f.Assembler.Append(&asm.FunctionStart{})
|
||||
}
|
||||
|
||||
for instr := range ir.Values {
|
||||
if len(instr.Users()) > 0 {
|
||||
continue
|
||||
}
|
||||
steps := f.CreateSteps(ir)
|
||||
|
||||
switch instr := instr.(type) {
|
||||
case *ssa.Call, *ssa.CallExtern, *ssa.Syscall:
|
||||
f.ValueToRegister(instr, f.CPU.Return[0])
|
||||
|
||||
case *ssa.Return:
|
||||
for i := range slices.Backward(instr.Arguments) {
|
||||
f.ValueToRegister(instr.Arguments[i], f.CPU.Return[i])
|
||||
}
|
||||
|
||||
f.Assembler.Append(&asm.Return{})
|
||||
}
|
||||
for _, step := range steps {
|
||||
f.Exec(&step)
|
||||
}
|
||||
|
||||
if !isLeaf && f.UniqueName != "core.init" {
|
||||
if !isLeaf && f.UniqueName != "run.init" {
|
||||
f.Assembler.Append(&asm.FunctionEnd{})
|
||||
}
|
||||
|
||||
if f.UniqueName != "core.exit" {
|
||||
if f.UniqueName != "os.exit" {
|
||||
f.Assembler.Append(&asm.Return{})
|
||||
}
|
||||
}
|
26
src/ssa2asm/Step.go
Normal file
26
src/ssa2asm/Step.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package ssa2asm
|
||||
|
||||
import (
|
||||
"git.urbach.dev/cli/q/src/cpu"
|
||||
"git.urbach.dev/cli/q/src/ssa"
|
||||
)
|
||||
|
||||
type Step struct {
|
||||
Index int
|
||||
Value ssa.Value
|
||||
Live []*Step
|
||||
Hints []cpu.Register
|
||||
Register cpu.Register
|
||||
}
|
||||
|
||||
func (s *Step) Hint(reg cpu.Register) {
|
||||
if len(s.Hints) == 0 {
|
||||
s.Register = reg
|
||||
}
|
||||
|
||||
s.Hints = append(s.Hints, reg)
|
||||
}
|
||||
|
||||
func (s *Step) String() string {
|
||||
return s.Value.String()
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue