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.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156package ssa
import ( "slices" "strings"
"git.urbach.dev/cli/q/src/types")
// mergeIdentifiers merges identifier mappings into the successor,// inserting phi functions where values differ across predecessors.func mergeIdentifiers(predecessor *Block, successor *Block) { if predecessor.Identifiers.After.Raw() == nil { return }
if successor.Identifiers.After.Raw() == nil { if len(successor.Predecessors) == 1 { after := predecessor.Identifiers.After.Raw() successor.Identifiers.Before.Share(after) successor.Identifiers.After.Share(after) return }
fresh := make(map[string]Value, predecessor.Identifiers.After.Count()) successor.Identifiers.Before.Share(fresh) successor.Identifiers.After.Share(fresh) }
var ( modifiedStructs []string names = collectIdentifierNames(predecessor, successor) )
for _, name := range names { mergeIdentifier(predecessor, successor, name, &modifiedStructs) }
// Structs that were modified in branches need to be recreated // to use the new Phi values as their arguments. for _, name := range modifiedStructs { value, _ := successor.Identifiers.Before.Get(name) structure := value.(*Struct) structType := types.Unwrap(structure.Typ).(*types.Struct) newStruct := &Struct{Typ: structure.Typ, Arguments: make(Arguments, len(structure.Arguments))}
for i, field := range structType.Fields { fieldValue, _ := successor.Identifiers.Before.Get(name + "." + field.Name) newStruct.Arguments[i] = fieldValue }
successor.ReplaceIdentifier(name, structure, newStruct) }}
// collectIdentifierNames returns all identifier names from both maps in deterministic order.func collectIdentifierNames(predecessor *Block, successor *Block) []string { keys := make([]string, 0, max(predecessor.Identifiers.After.Count(), successor.Identifiers.Before.Count()))
for name := range successor.Identifiers.Before.Raw() { if !slices.Contains(keys, name) { keys = append(keys, name) } }
for name := range predecessor.Identifiers.After.Raw() { if !slices.Contains(keys, name) { keys = append(keys, name) } }
slices.SortFunc(keys, func(a string, b string) int { return strings.Compare(b, a) })
return keys}
// mergeIdentifier merges a single identifier into the successor.func mergeIdentifier(predecessor *Block, successor *Block, name string, modifiedStructs *[]string) { oldValue, oldExists := successor.Identifiers.Before.Get(name) newValue, newExists := predecessor.Identifiers.After.Get(name)
switch { case oldExists: mergeOldIdentifier(successor, name, oldValue, newValue, newExists, modifiedStructs) case newExists: mergeNewIdentifier(successor, name, newValue) }}
// mergeOldIdentifier handles the case where the successor already has a binding for this name.func mergeOldIdentifier(successor *Block, name string, oldValue Value, newValue Value, newExists bool, modifiedStructs *[]string) { if oldValue == newValue { return }
_, isStruct := oldValue.(*Struct)
if isStruct { *modifiedStructs = append(*modifiedStructs, name) return }
definedLocally := successor.Index(oldValue) != -1
if definedLocally { phi, isPhi := oldValue.(*Phi)
if isPhi { if newExists { phi.Arguments = append(phi.Arguments, newValue) } else { phi.Arguments = append(phi.Arguments, Undefined) } }
return }
phi := &Phi{ Name: name, Arguments: make([]Value, len(successor.Predecessors)-1, len(successor.Predecessors)), Typ: oldValue.Type(), }
for i := range phi.Arguments { phi.Arguments[i] = oldValue }
successor.InsertAt(0, phi) successor.ReplaceIdentifier(name, oldValue, phi)
if newExists { phi.Arguments = append(phi.Arguments, newValue) } else { phi.Arguments = append(phi.Arguments, Undefined) }}
// mergeNewIdentifier handles the case where only the predecessor has a binding for this name.func mergeNewIdentifier(successor *Block, name string, newValue Value) { phi := &Phi{ Name: name, Arguments: make([]Value, len(successor.Predecessors)-1, len(successor.Predecessors)), Typ: newValue.Type(), }
for i := range phi.Arguments { phi.Arguments[i] = Undefined }
successor.InsertAt(0, phi) successor.ReplaceIdentifier(name, nil, phi) phi.Arguments = append(phi.Arguments, newValue)}