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.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178package core
import ( "git.urbach.dev/cli/q/src/ast" "git.urbach.dev/cli/q/src/errors" "git.urbach.dev/cli/q/src/ssa" "git.urbach.dev/cli/q/src/token" "git.urbach.dev/cli/q/src/types")
// compileLoop compiles an endless loop.func (f *Function) compileLoop(node *ast.Loop) error { f.Count.Loop++ headLabel := f.CreateLabel("loop.head", f.Count.Loop) exitLabel := f.CreateLabel("loop.exit", f.Count.Loop) beforeLoop := f.Block() loopHead := ssa.NewBlock(headLabel) loopExit := ssa.NewBlock(exitLabel) loopBlockIndex := len(f.Blocks)
loop := &Loop{ Head: loopHead, Exit: loopExit, }
if node.Head != nil { // Before the loop starts, we evaluate the lower limit // and identify it as the loop counter. name, from, to := f.parseLoopHeader(node.Head)
if from == nil { return errors.New(InvalidLoopHeader, f.File, node.Head.Source()) }
loop.IteratorName = name fromValue, err := f.evaluateRight(from)
if err != nil { return err }
if !types.Is(fromValue.Type(), types.AnyInt) { return errors.New(&TypeMismatch{Encountered: fromValue.Type().Name(), Expected: types.AnyInt.Name()}, f.File, from.Source()) }
if f.Block().IsIdentified(fromValue) { fromValue = f.copy(fromValue, from.Source(), isIdentifierRead(from)) }
beforeLoop.Identify(name, fromValue) f.jump(loopHead)
// Loop starts, this is the jump target for new iterations. // The upper limit is recalculated on every iteration. // We check that the condition to jump to the loop body is true, // otherwise we jump to the loop exit. f.AddBlock(loopHead)
toValue, err := f.evaluateRight(to)
if err != nil { return err }
if !types.Is(toValue.Type(), fromValue.Type()) { return errors.New(&TypeMismatch{Encountered: toValue.Type().Name(), Expected: fromValue.Type().Name()}, f.File, to.Source()) }
condition := f.Append(&ssa.BinaryOp{ Op: token.Less, Left: fromValue, Right: toValue, })
bodyLabel := f.CreateLabel("loop.body", f.Count.Loop) bodyBlock := ssa.NewBlock(bodyLabel)
f.Append(&ssa.Branch{ Condition: condition, Then: bodyBlock, Else: loopExit, })
loopHead.AddSuccessor(bodyBlock)
// Loop condition is true from now on so we'll // execute the code inside the loop body. f.AddBlock(bodyBlock) f.loopStack.Push(loop) err = f.compileAST(node.Body)
if err != nil { return err }
f.loopStack.Pop() } else { f.jump(loopHead) f.AddBlock(loopHead)
// For infinite loops, there are no conditions to check, // we can simply process the loop body. f.loopStack.Push(loop) err := f.compileAST(node.Body)
if err != nil { return err }
f.loopStack.Pop() }
// Jump back to the loop head. f.loopNext(loop)
// The initial compilation of the loop body does not know // that the code is repeated in a loop. Therefore, we need // to find identifiers that were both defined outside the loop // and modified within the loop. For these identifiers, // we created Phi functions at the top of the loop head. // All that's left to do is to replace all the occurrences // of the old values with their new Phi in the loop blocks. loopBlocks := f.Blocks[loopBlockIndex:]
for _, block := range loopBlocks { if block.Loop == nil { block.Loop = loopHead } }
for _, block := range loopBlocks { for phi := range loopHead.Phis { oldValue := phi.Arguments[0]
if oldValue == ssa.Undefined { continue }
for _, instr := range block.Instructions { if instr == phi { continue }
switch instr := instr.(type) { case *ssa.Jump: jumpBlock := instr.To
if jumpBlock.Loop != nil { continue }
for name, value := range jumpBlock.Identifiers.Before.Raw() { if value == oldValue { jumpBlock.ReplaceIdentifier(name, oldValue, phi) } else if jumpBlock.Index(value) != -1 { value.Replace(oldValue, phi) } } default: instr.Replace(oldValue, phi) } }
for i := 1; i < len(phi.Arguments); i++ { if phi.Arguments[i] == oldValue { phi.Arguments[i] = phi } } } }
if node.Head != nil { loopHead.AddSuccessor(loopExit) }
f.AddBlock(loopExit) return nil}