Something went wrong. Try again.
🌱 A minimal programming language and compiler. git.urbach.dev/cli/q
high-performance programming-language compiler
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192package ssa
import ( "maps" "slices")
// Block is a list of instructions that can be targeted in branches.type Block struct { Identifiers Protected map[Value][]Value Loop *Block Label string Instructions []Value Predecessors []*Block}
// NewBlock creates a new basic block.func NewBlock(label string) *Block { return &Block{ Instructions: make([]Value, 0, 8), Label: label, }}
// AddSuccessor adds the given block as a successor.func (b *Block) AddSuccessor(successor *Block) { if slices.Contains(successor.Predecessors, b) { return }
successor.Predecessors = append(successor.Predecessors, b) b.copyProtected(successor) mergeIdentifiers(b, successor)}
// Append adds a new value to the block.func (b *Block) Append(value Value) { b.Instructions = append(b.Instructions, value)}
// CanReachPredecessor checks if the `other` block appears as a predecessor or is the block itself.func (b *Block) CanReachPredecessor(other *Block) bool { return b.canReachPredecessor(other, make(map[*Block]bool))}
// Contains checks if the value exists within the block.func (b *Block) Contains(value Value) bool { return b.Index(value) != -1}
// FindExisting returns an equal instruction that's already appended or `nil` if none could be found.func (b *Block) FindExisting(instr Value) Value { if !instr.IsPure() { return nil }
for _, existing := range slices.Backward(b.Instructions) { if existing.IsPure() && instr.Equals(existing) { return existing }
// If we encounter an instruction with side effects, // we can't be sure that the value is still the same. switch other := existing.(type) { case *Call, *CallExtern, *CallPointer, *Syscall, *Cas: return nil case *Store: load, isLoad := instr.(*Load)
if isLoad && load.Memory.Address == other.Memory.Address && load.Memory.Index == other.Memory.Index && load.Memory.Scale == other.Memory.Scale { return nil } } }
return nil}
// Index returns the position of the value or -1 if it doesn't exist within the block.func (b *Block) Index(search Value) int { for i, value := range b.Instructions { if value == search { return i } }
return -1}
// InsertAt inserts the `value` at the given `index`.func (b *Block) InsertAt(index int, values ...Value) { b.Instructions = slices.Insert(b.Instructions, index, values...)}
// Last returns the last value.func (b *Block) Last() Value { if len(b.Instructions) == 0 { return nil }
return b.Instructions[len(b.Instructions)-1]}
// Phis is an iterator for all phis at the top of the block.func (b *Block) Phis(yield func(*Phi) bool) { for _, instr := range b.Instructions { phi, isPhi := instr.(*Phi)
if !isPhi || !yield(phi) { return } }}
// Protect protects the given value from being accessed before the error value is checked.func (b *Block) Protect(err Value, protected []Value) { if b.Protected == nil { b.Protected = make(map[Value][]Value) }
b.Protected[err] = protected}
// RemoveAt sets the value at the given index to nil.func (b *Block) RemoveAt(index int) { value := b.Instructions[index]
for _, input := range value.Inputs() { input.RemoveUser(value) }
b.Instructions[index] = nil}
// RemoveNilValues removes all nil values from the block.func (b *Block) RemoveNilValues() { b.Instructions = slices.DeleteFunc(b.Instructions, func(value Value) bool { return value == nil })}
// ReplaceAllUses replaces all uses of `old` with `new`.func (b *Block) ReplaceAllUses(old Value, new Value) { for _, instr := range b.Instructions { instr.Replace(old, new) }}
// String returns the block label.func (b *Block) String() string { return CleanLabel(b.Label)}
// Unprotect stops protecting the variables for the given error value.func (b *Block) Unprotect(err Value) { delete(b.Protected, err)}
// canReachPredecessor checks if the `other` block appears as a predecessor or is the block itself.func (b *Block) canReachPredecessor(other *Block, traversed map[*Block]bool) bool { if other == b { return true }
if traversed[b] { return false }
traversed[b] = true
for _, pre := range b.Predecessors { if pre.canReachPredecessor(other, traversed) { return true } }
return false}
// copyProtected transfers protected values to the successor.func (b *Block) copyProtected(successor *Block) { if len(b.Protected) == 0 { return }
if successor.Protected == nil { successor.Protected = make(map[Value][]Value, len(b.Protected)) }
maps.Copy(successor.Protected, b.Protected)}