Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building BrainFuck in Swift

Building BrainFuck in Swift

Samuel E. Giddins

August 14, 2015
Tweet

More Decks by Samuel E. Giddins

Other Decks in Technology

Transcript

  1. Building BrainFuck
    in Swift
    Samuel Giddins

    View full-size slide

  2. Samuel Giddins
    Cocoa @ Realm
    CocoaPods, Bundler, Jazzy, etc.

    View full-size slide

  3. Turing
    Complete

    View full-size slide

  4. ++++++++
    [>++++
    [>++>+++>+++>+<<<<-]
    >+>+>->>+[<]<-
    ]
    >>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.

    View full-size slide

  5. Hello World!
    ++++++++
    [>++++
    [>++>+++>+++>+<<<<-]
    >+>+>->>+[<]<-
    ]
    >>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.

    View full-size slide

  6. The 8 Operations

    View full-size slide

  7. | Character | Meaning |
    |:----------|:-----------------------------------------------------------------------------------|
    | > | Move data pointer one cell to the right |
    | < | Move data pointer one cell to the left |
    | + | Increment the value at the data pointer |
    | - | Decrement the value at the data pointer |
    | . | Print out the value at the data pointer (as ASCII) |
    | , | Accept a byte of input, and store it as the value at the data pointer |
    | [ | Jump to the matching `]` unless the value at the data pointer is `0` |
    | ] | Jump backwards to the matching `[` unless the value at the data pointer is not `0` |

    View full-size slide

  8. Virtual Machine
    » Encapsulates state
    » Internally driven
    » Single-use

    View full-size slide

  9. Virtual Machine
    class VM {
    init?(program: String)
    func run()
    }

    View full-size slide

  10. Virtual Machine
    class VM {
    init?(program: String)
    func run()
    var data: UnsafeMutablePointer
    var dataPointer: Int = 0
    }

    View full-size slide

  11. class VM {
    init?(program: String)
    func run()
    var data: UnsafeMutablePointer
    var dataPointer: Int = 0
    let commands: [Command]
    }

    View full-size slide

  12. VM.Command
    » IncrData
    » DecrData
    » Incr
    » Decr
    » Output
    » Input
    » LoopStart
    » LoopEnd

    View full-size slide

  13. The Naïve Approach
    public func run() {
    while instructionPointer != program.endIndex {
    if let command = Command(rawValue: String(program[instructionPointer])) {
    command.execute(self)
    }
    instructionPointer++
    }
    }

    View full-size slide

  14. The Naïve Approach
    Suuuuuuuuuuuuuuuper slow.
    » LoopStart & LoopEnd have
    arbitrary (linear)
    complexity.
    » Extra layers of
    indirection.

    View full-size slide

  15. So how do we make our interpreter that fast?

    View full-size slide

  16. Threading!
    (no, not that kind of threading)

    View full-size slide

  17. Threading!
    » Precompute all of our jumps
    » Validate program before running

    View full-size slide

  18. var index = 0
    var pairs = [Int]()
    for command in commands {
    switch command {
    case .LoopStart(_):
    pairs.append(index)
    case .LoopEnd(let endBox):
    if pairs.count == 0 {
    fatalError("invalid program")
    }
    let start = pairs.removeLast()
    switch commands[start] {
    case .LoopStart(let startBox):
    startBox.val = index
    endBox.val = start
    default:
    fatalError("invalid program")
    }
    default: ()
    }
    index++
    }
    if pairs.count != 0 {
    fatalError("invalid program")
    }

    View full-size slide

  19. » We know that each [ is balanced with a ]
    » Maintain a stack of jump targets [
    » Push every time we see a [
    » Pop every time we see a ]
    » Match up the jump target indices
    » Yes / No validation

    View full-size slide

  20. Next Steps
    » Roll up adjacent commands
    » No need to just increment / decrement
    » No need to just move one cell at a time
    » No need to run Swift code

    View full-size slide

  21. No need to run Swift
    code

    View full-size slide

  22. (All) Interpreters are Slow
    » Interpretation takes time
    » Function call for each command
    » Continuous penalty while program runs
    » Host language overhead
    » Refcounting is a pain

    View full-size slide

  23. So let's not write an interpreter.
    Let's used compiled code.
    Let's compile it Just-In-Time.

    View full-size slide

  24. JIT
    » malloc
    » mprotect
    » address[i] = instr
    » 0xB3 0xF000 0xBA4
    » mprotect
    » func run() {
    typealias Trampoline = @convention(c) Int -> Int
    let fp = executableSpace
    let f = unsafeBitCast(fp, Trampoline.self)
    print(f(data))
    }

    View full-size slide

  25. Swift Gotchas
    » ARC
    » Strings
    » UnsafeMutablePointer
    » MutableBox
    » Trapping arithmatic
    » Bounds checking

    View full-size slide

  26. Open Source
    https://github.com/segiddins/BF

    View full-size slide

  27. ----[---->+<]>++.[--->+<]>+++.[--->+<]>-.-[---->+<]>++.----[->++++<]>+.++++
    .++[->+++<]>.[--->+<]>----.+.-----------.++++++.-.+++++.[->+++++<]>.>++++++
    ++++..>-[--->+<]>--.++++++++++++++.++++++++++++.++++++++.++[->+++<]>.++++++
    +.[++>---<]>--.+++[->++<]>+.-[-->+++<]>.-----..+++++.+++++.+++++.+[---->+<]
    >+++.++++++++.++[-->+++<]>+.-[----->+<]>.++++[->+++<]>.++.++.-----..+++++.+
    ++++.+++++.-[--->+<]>+++.>++++++++++..>-[--->+<]>-.[---->+++++<]>-.-------.
    +++++++++++++.---.++++++++.+[---->+<]>+++.---[->++++<]>-.----.[--->+<]>----
    -.+[----->+<]>.++++++++.+[->+++<]>+.+++++.--[--->+<]>--.---[->++++<]>.-----
    .[--->+<]>-----.>-[--->+<]>---.+[----->+++<]>.----.+++++++++++.+.[->+++++<]
    >-.-[--->++<]>--.+++++++.++++.+.-----------.+++++.-------.-[--->+<]>-.-.--[
    ->++<]>.---------.>++++++++++..

    View full-size slide