DEV Community

Annurdien Rasyid
Annurdien Rasyid

Posted on

From Swift to Machine Code

Table of Contents

  1. Introduction
  2. What is a Compiler?
  3. The Swift Compiler Architecture
  4. Frontend: Source Code to SIL
  5. Mid-End: SIL Optimizations
  6. Back-End: LLVM Code Generation
  7. Linker: Creating the Final Executable
  8. Code Examples from the Swift Compiler
  9. Key Takeaways
  10. Conclusion

Introduction

Here's a fun thought: every time you write some Swift code and slam that "Run" button in Xcode, you're kicking off one of the most ridiculously sophisticated magic tricks in modern software. You're triggering this massive, complex piece of engineering. The Swift compiler grabs your (hopefully) beautiful, human-readable code and transforms it into super-efficient, safe, optimized machine code that your phone or Mac just... runs.

(This was one of those psyching-myself-out moments for me—seriously, it feels like it should be way more complicated than it is! It's like giving a master chef a recipe for a 5-course meal written in crayon. The chef (our compiler) has to:

  1. Figure out what you meant to write (Parsing).
  2. Make sure it's a valid, safe recipe that won't burn the kitchen down (Semantic Analysis).
  3. Make it way more efficient than your crayon scrawl (Optimization).
  4. Finally, translate it into the actual physical movements of their hands and tools (Machine Code).)

Swift first popped into existence back in 2010, thanks to Chris Lattner and his crew at Apple. Their grand plan? To build a shiny new language to replace Objective-C. They wanted something that could do everything Objective-C did, but with way, way better safety, performance, and a developer experience that didn't make you want to tear your hair out. Because who doesn't love a good "out with the old, in with the new" story, right?

So how does this wild transformation actually work? What sorcery happens between you typing print("Hello, World!") and seeing those glorious words appear on your screen? I'm going to walk you through this whole pipeline, step by step. Let's follow the rabbit down the hole.

      (\_/)
      (o.o)
     (")(")o
Enter fullscreen mode Exit fullscreen mode

What is a Compiler?

Before we dive into Swift's peculiarities, let's get on the same page about what this compiler beast even is. If you ask a computer science professor, they'll clear their throat and give you a dusty definition like this:

A compiler is software that translates computer code written in one programming language into another language.

That sounds... kinda simple. And boring. And it massively undersells the drama! A compiler isn't just a basic Google Translate for code.

    +-----------------+      +-----------------+
    | "Hello" (Swift) | ===> | "Hola" (Machine)|  <- This is WRONG
    +-----------------+      +-----------------+
Enter fullscreen mode Exit fullscreen mode

It's not just translating 'Hello' to 'Hola.' It's more like translating, "I'd, y'know, maybe like to get some food... if that's cool, I guess?" into "GO TO RESTAURANT. ORDER TACOS. PAY. EAT." It takes your sometimes-vague intent and turns it into a set of brutally fast, efficient, and unambiguous instructions.

The Swift compiler shoves your code through four main phases, or zones: Frontend, Mid-End, Back-End, and Linker. Each phase grabs the code, does its one job, and passes it along to the next.

The Swift Compiler Architecture

The Swift compiler follows the traditional playbook, but it adds some of its own special sauce.

Why all the stages? You don't try to bake a cake, frost it, and eat it all in one motion. You parse the ingredients (Frontend), mix and bake (Mid-End), decorate (Back-End), and put it on a plate (Linker). Each step builds on the last.

waves hands ~~~ compiler magic ~~~

+-------------------+
|    Frontend       |
| (The Grammar Cop) |
+-------------------+
         |
         v
+---------------------+
|   Mid-End (SIL)     |
| (The Super-Trainer) |
+---------------------+
         |
         v
+---------------------+
|  Back-End (LLVM).   |
| (The Factory Floor) |
+---------------------+
         |
         v
+--------------------------+
|         Linker           |
| (The General Contractor) |
+--------------------------+
Enter fullscreen mode Exit fullscreen mode

What makes Swift so special is its secret weapon: Swift Intermediate Language (SIL). Think of it as a high-level, private language the compiler uses to talk to itself. This SIL thingy allows Swift to perform all kinds of galaxy-brain optimizations that other compilers just, like, can't achieve. (Seriously, what are those other compilers even doing? Twiddling their thumbs?)

Frontend: Source Code to SIL

The frontend is the bouncer and grammar cop at the club. Its job is to take your messy, all-too-human .swift files, make sure they're following the rules, and prepare them for the optimization party. It transforms that text into the fancy SIL we just talked about.

       .--.
      |o_o |
      |:_/ |
     //   \ \
    (|     | )
    /'\_   _/`\
    \___)=(___/
Enter fullscreen mode Exit fullscreen mode

Parsing & Lexing

First, the compiler just... reads your code. A "lexer" scans your file and breaks it into "tokens" (like func, myFunctionName, (, ), {, }). Then a "parser" takes that stream of tokens and tries to build a giant tree structure called an Abstract Syntax Tree (AST).

This is literally a grammar check. Like your 8th-grade English teacher. It sees the func keyword and thinks, "OK, my rulebook says that after func comes an identifier (the name), then (, then some arguments, then ), then maybe -> and a return type, and definitely { and }." If you miss a curly brace, the parser throws up its hands and gives you that error message.

Want to See the AST for Yourself?

This AST thing isn't some mythical, abstract concept. It's real data. And you can actually go look at it!

A fantastic tool called the Swift AST Explorer lets you do just this. You paste your Swift code on the left, and on the right, it instantly shows you the exact, glorious, complicated Abstract Syntax Tree that the compiler builds from it. It's the perfect way to see this first step of the compiler in action. Go paste in a struct or a func and watch the tree get built. It's so cool!

Example: An Abstract Syntax Tree (AST)

Let's say you write this super simple line of Swift:

var x = 1 + 2
Enter fullscreen mode Exit fullscreen mode

The parser chews on this and spits out a data structure (the AST) that, if we drew it as text (like you'd see in the explorer tool!), would look something like this:

(top_level_code
  (variable_declaration 'x'
    (binary_expression '+'
      (integer_literal 1)
      (integer_literal 2)
    )
  )
)
Enter fullscreen mode Exit fullscreen mode

See? It's a tree! top_level_code is the root. It has one branch, a variable_declaration. That declaration has a name (x) and an initial value. The value is a binary_expression (the + operation). And that expression has two branches of its own: the integer_literal 1 and the integer_literal 2.

The compiler now understands your code's structure and intent, not just as a pile of letters.

Semantic Analysis

This is the logic check. Your grammar might be perfect, but you wrote, "The green idea slept furiously." Your teacher is now very confused.

This is the compiler stage that says, "Whoa, hoss. You're trying to add a String to an Int ("hello" + 5). That's semantic nonsense! What does that even mean? I can't find a + function that takes a String and an Int. ERROR!" This is where type checking lives, making sure all your types line up and your code actually makes sense.

Clang Importer

This part is the universal translator on your Star Trek team. It's the bridge that connects Swift to all that "legacy" (read: ancient but necessary) C and Objective-C code out there. It peeks into C/Obj-C "header files" and magically makes them available in Swift as if they were native Swift APIs. It handles all the weird cultural (syntactical) differences, like mapping NSString to String.

SIL Generation

And poof! The grand finale for the frontend. This stage takes that big, beautiful, type-checked AST (which is now guaranteed to be grammatically and logically correct) and converts it into raw Swift Intermediate Language (SIL). This is our first real look at the compiler's internal secret language. It's the super-detailed, high-level set of instructions for the next department (the Mid-End).

Swift Source (.swift) → AST → Type-checked AST → Raw SIL
Enter fullscreen mode Exit fullscreen mode

Example: What SIL Actually Looks Like

Let's use our var x = 1 + 2 example again. The Raw SIL (before any optimization) is super verbose, but it looks something like this. Don't panic, I'll explain it.

// This is our main function
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):

  // 1. Make some space on the stack for our variable 'x'
  %2 = alloc_stack $Int, var, name "x"

  // 2. Load the *type* Int
  %3 = metatype $@thin Int.Type

  // 3. Load the *literal value* 1
  %4 = integer_literal $Builtin.Int64, 1
  %5 = struct $Int (%4 : $Builtin.Int64)

  // 4. Load the *literal value* 2
  %6 = integer_literal $Builtin.Int64, 2
  %7 = struct $Int (%6 : $Builtin.Int64)

  // 5. Find the '+' function for Ints
  %8 = function_ref @$sSi1poiyS2i_SitFZ : $@convention(method) (Int, Int, @thin Int.Type) -> Int

  // 6. CALL the '+' function with 1 and 2
  %9 = apply %8(%5, %7, %3) : $@convention(method) (Int, Int, @thin Int.Type) -> Int

  // 7. STORE the result (which is in %9) into our variable 'x' (which is at %2)
  store %9 to %2 : $*Int

  // ... (lots of cleanup code)...
}
Enter fullscreen mode Exit fullscreen mode

See? It's not magic, it's just a lot of very specific, tiny steps! It has to "allocate" memory for x, load the numbers 1 and 2, find the + function, call the + function, and finally store the result. This verbosity is good—it gives the optimizer tons of tiny pieces to move around and improve.

Mid-End: SIL Optimizations

Okay, we have Raw SIL. Now the Mid-End steps in. This is the personal trainer. Its job is to take that "raw" SIL and make it better, faster, stronger.

This is where Swift gets its superpowers. Because SIL still "understands" high-level Swift concepts (like Array or String, not just raw memory addresses), it can make smarter changes.

       .---.
      /  .-.  \
     |  |   |  |
      \  `-'  /
       `-----'
       /     \
      |       |
      |       |
       \     /
        `---'
Enter fullscreen mode Exit fullscreen mode

SIL Guaranteed Transformations

First, some mandatory checkups. This is the "eat your vegetables" phase. It's the compiler being your spotter at the gym. "Whoa there, you're about to use that myVariable before you put any value in it! That's a crash waiting to happen. Let me stop you right there." This is what guarantees Swift's memory safety and catches things like uninitialized variables. This produces "canonical" SIL.

SIL Optimizations

Now the real fun begins. After we have "canonical" SIL, the compiler runs a ton of optimization passes. These are high-level, Swift-specific transformations. Stuff like:

  • ARC Optimization: This is the compiler cleaning up after you. It sees where you create an object and figures out exactly where you're done with it, inserting the 'throw this in the trash' (release) code automatically. Then it optimizes that, like "Hmm, you're releasing it on line 10 and retaining it again on line 11? Let's just... not do either of those. Saved two instructions."
  • Generic Specialization: This is a custom-tailoring shop. It sees your generic, one-size-fits-all Array<T> is being used in one place as Array<Int> and another as Array<String>. So it builds two brand-new, custom-fit versions: one that only works on Ints and one that only works on Strings. These bespoke versions are way faster.
  • Devirtualization: Replaces a fancy, dynamic "hey, somebody needs to run this function" call (dynamic dispatch) with a simple, direct "call this specific function right here" (static dispatch). Much, much faster.
  • Dead Code Elimination: Finds code that never, ever gets run (maybe it's inside an if false) and just yeets it into the sun.
  • Inlining: The compiler sees your code is about to make a "phone call" (function call) to a tiny function that just does a + b. It says, "This is dumb. Why go through all the overhead of a call? I'll just paste the a + b code right here." It hangs up the phone and just does the work.

IR Generation

Once the SIL is as lean and mean as it can possibly be, it's time to translate it again. This step (lib/IRGen) lowers the "Optimized SIL" into LLVM Intermediate Representation (IR). We're now officially leaving the cozy, Swift-specific world and entering the big, scary, platform-agnostic land of LLVM.

Raw SIL → Canonical SIL → Optimized SIL → LLVM IR
Enter fullscreen mode Exit fullscreen mode

Example: What LLVM IR Looks Like

So, our optimizer looked at that bloated SIL and said, "Are you kidding me? You're allocating a variable just to store the result of 1 + 2? I know what 1 + 2 is! It's 3! And you're not even using x! I'm throwing all this junk out."

In a real (but still simple) optimized scenario where you print(1 + 2), the LLVM IR would look totally different. The optimizer would pre-calculate 1 + 2 (a process called "constant folding"). The IR would be translated from SIL and look more like this generic, low-level assembly:

; Define a constant string for "3\n"
@.str = private unnamed_addr constant [3 x i8] c"3\n\00"

; The main function
define i32 @main(i32 %0, i8** %1) {
  ; 1. Get a pointer to our constant string "3\n"
  %3 = getelementptr inbounds [3 x i8], [3 x i8]* @.str, i64 0, i64 0

  ; 2. Call the 'puts' function (a C function to print a string)
  %4 = call i32 @puts(i8* %3)

  ; 3. Return 0
  ret i32 0
}

; Declaration for the external 'puts' function
declare i32 @puts(i8*)
Enter fullscreen mode Exit fullscreen mode

Wait, what? Where did our + go? The optimizer killed it! It saw 1 + 2, did the math at compile time, and just replaced the whole operation with the result, 3. Then it just calls a basic puts function (or a Swift equivalent) to print the string "3".

That's optimization. All that complicated SIL vanished because it wasn't needed.

Back-End: LLVM Code Generation

The Back-End is the factory floor. This is where we forget all about "Swift". The code is now in LLVM IR, a generic compiler language. LLVM is a masterpiece of engineering used by many languages (like Rust, Clang, C++, etc.). Swift is now just one more customer of the giant LLVM factory.

It takes that generic LLVM IR and does the final translation into actual, real-deal machine code for your target architecture (e.g., the ARM chip in your iPhone or the M-series chip in your Mac).

        ._________.
        | .-[ ]-. |
        | |  '  | |
        | |_____| |
        |_________|
       /           \
      /             \
     |_______________|
Enter fullscreen mode Exit fullscreen mode

Example: The Final Assembly Output

This is it! The moment of truth! The LLVM Back-End takes that generic LLVM IR (the puts("3\n") one) and translates it into the specific assembly language for your CPU. If you're compiling for an Apple Silicon Mac (ARM64 architecture), it will look something like this:

; This is ARM64 assembly (for an Apple M-series chip)
_main:
    stp     x29, x30, [sp, -16]! ; Set up the stack (prologue)
    mov     x29, sp

    ; 1. Get the address of our string "3\n"
    adrp    x0, _main.str@PAGE
    add     x0, x0, _main.str@PAGEOFF

    ; 2. Call the 'puts' function (address is in register x0)
    bl      _puts

    ; 3. Set the return value to 0
    mov     w0, #0

    ldp     x29, x30, [sp], 16 ; Clean up the stack (epilogue)
    ret                        ; Return

; The data section, where our string lives
_main.str:
    .asciz  "3\n"
Enter fullscreen mode Exit fullscreen mode

This is the real deal! This is the human-readable version of the binary machine code. No more high-level concepts like 'string' or 'variable.' This is all just: 'load this memory address into CPU register x0,' 'jump to the _puts function,' and 'put a 0 in the return register.'

The CPU just chugs through these instructions one by one. And poof! '3' appears on your screen.

But wait, there's more! The LLVM back-end does another whole round of optimizations on the assembly itself:

  • Instruction Selection: Picks the best and most efficient machine instructions for the job.
  • Register Allocation: This is a huge one. Your CPU has a tiny number of super-fast storage buckets (like 32 registers). This stage is a master-level game of Sudoku, figuring out how to juggle all your hundreds of variables to use only those few registers for maximum speed.
  • Instruction Scheduling: Re-orders the machine instructions to make sure the CPU's internal pipelines are always full and chugging along efficiently.
  • Peephole Optimizations: Takes one last look and makes tiny, local improvements (e.g., "you're storing a value just to load it right back? I'll just keep it in the register.").

Finally, the code generator spits out object files (.o). This is it! This is machine code! ...Almost.

Linker: Creating the Final Executable

You thought we were done? Ha! Psych!

You've got your .o file (or maybe a bunch of them), but it's just one piece of the puzzle. It's like one part of an IKEA furniture set. Your code (.o file) is the BILLY bookcase. But the instructions say you need a 'hex key' (the print function) and 'screws' (system libraries). Your .o file just has a bunch of "IOUs" for code it needs.

The Linker is the final boss. It's the general contractor who assembles the final, runnable program.

      /----\     /----\
     /      \   /      \
    |        |-|        |
     \      /   \      /
      \----/     \----/
Enter fullscreen mode Exit fullscreen mode

The linker does a few crucial things:

  • Symbol Resolution: It goes through all your .o files. It sees your code "calling" _puts (a symbol) and finds the actual code for _puts in the operating system's C library. It connects the "call" to the "destination."
  • Library Linking: It grabs all the system libraries (like libc, Foundation, SwiftCore) and user libraries you said you needed and pulls in just the pieces you actually use.
  • Relocation: It adjusts all the memory addresses in the code so it can be loaded and run properly by the operating system.
  • Dead Code Stripping: After pulling in those big libraries, it finds all the code you didn't end up using and tosses it out to make your final executable smaller.
    ╱|、
  (˚ˎ 。7  
   |、˜〵          
  じしˍ,)ノ

Take a short break here.....
Enter fullscreen mode Exit fullscreen mode

Code Examples from the Swift Compiler

Don't just take my word for it. This stuff is all open source! Let's peek under the hood at the actual source code from the Swift compiler repository. This is the belly of the beast.

      .--.
     /    \
    |      | \
     \    /  /
      '--'  /
           /
          /
Enter fullscreen mode Exit fullscreen mode

Parsing: Building the AST

This is the code that reads your text and builds the grammar tree.

// From lib/Parse/Parser.h in swiftlang/swift
class Parser {
public:
  ParserResult<Expr> parseExpr(Diag<> ID);
  ParserResult<Stmt> parseStmt();
  ParserResult<Decl> parseDecl(ParseDeclOptions Flags, bool IsAtStartOfLineOrPreviousHadSemi);

  // Parse a function declaration
  ParserResult<FuncDecl> parseFuncDecl();
};
Enter fullscreen mode Exit fullscreen mode

What this really means:
Look at those function names! parseExpr is the code that gets called when the parser expects an expression (like 1 + 1 or foo()). parseStmt handles a statement (like var x = 5). parseDecl handles a declaration (like class MyClass {} or func myFunc()).

Real-life example: When you type func, the main parser loop says "Aha! The func keyword! I'll call parseFuncDecl()." That function then takes over, saying "OK, I just saw func. According to my rulebook, the next thing must be an identifier (the function name). Let me call my parseIdentifier helper..." If it finds a 5 instead, it throws a "parser error"—your familiar "Expected an identifier" message.

Type Checking: Semantic Analysis

This is the code that solves the puzzle of your types.

// From lib/Sema/TypeCheckConstraints.cpp in swiftlang/swift
class ConstraintSystem {
public:
  Solution applySolution(const Solution &solution);
  void solve();

  // Type checking for function calls
  bool solveForFunctionCall(ApplyExpr *apply);
};
Enter fullscreen mode Exit fullscreen mode

What this really means:
This is so cool. When you write let x = "hello" + 5, the compiler creates a ConstraintSystem—a big logic puzzle. It says, "I have an ApplyExpr (a function call, because + is just a function!). The left side is String. The right side is Int. I need to solve() for a function + that takes (String, Int). It looks through all its 'constraint' rules, finds no such function, and solve() fails. solveForFunctionCall is the detective trying to find a suspect (+ function) that matches the evidence (String and Int).

SIL Generation: High-Level IR

This is the scribe that translates the AST (the idea) into SIL (the recipe).

// From lib/SILGen/SILGen.cpp in swiftlang/swift
class SILGenModule {
public:
  SILFunction *getOrCreateFunction(SILLocation loc, SILDeclRef constant,
                                   ForDefinition_t forDefinition);

  // Generate SIL for expressions
  void emitExprInto(RValue &&rv, SILLocation loc, SGFContext C);
};
Enter fullscreen mode Exit fullscreen mode

What this really means:
This is the translator. SILGenModule is in charge of converting the entire AST into SIL. When it sees your myFunc in the AST, it calls getOrCreateFunction to build the new, empty SIL function. Then, it walks all the code inside your function. When it sees the expression 1 + 1, it calls emitExprInto to spit out the actual SIL instructions for loading 1, loading another 1, and calling the add function for integers.

SIL Optimizations: Mid-Level Transformations

This is the clean-up crew, the personal trainer, the efficiency expert.

// From lib/SILOptimizer/SILCombiner/SILCombiner.h in swiftlang/swift
class SILCombiner : public SILInstructionVisitor<SILCombiner> {
public:
  void visitApplyInst(ApplyInst *AI);
  void visitAllocStackInst(AllocStackInst *ASI);

  // Combine redundant instructions
  bool tryCombine(InstructionBase *I);
};
Enter fullscreen mode Exit fullscreen mode

What this really means:
This is the clean-up crew. It's a Visitor that 'visits' every single instruction in the SIL. Look at visitApplyInst—this code runs for every function call (ApplyInst) in your program. Inside this function is a giant list of patterns. It might say, "Are you calling a function that just returns a value... and then immediately calling another function with that value? I can combine these into one operation!" tryCombine is its master function, full of tricks to make the code faster.

LLVM IR Generation: Lowering to LLVM

This is the final translation from the Swift world to the generic factory floor.

// From lib/IRGen/IRGen.cpp in swiftlang/swift
class IRGenerator {
public:
  llvm::Module *getModule() const { return Module; }

  // Generate LLVM IR for SIL functions
  void emitSILFunction(SILFunction &f);
};
Enter fullscreen mode Exit fullscreen mode

What this really means:
IRGenerator is the final translator. Its big job is emitSILFunction. This function takes a whole function written in SIL (Swift-specific) and painstakingly converts it, instruction by instruction, into LLVM IR (generic). It's the bridge out of the Swift-specific world. getModule() is just a helper to get the 'file' (llvm::Module) that it's writing all this new LLVM IR into.

Linking: Final Assembly

This isn't the final OS linker, but a compiler linker that helps with optimization!

// From lib/SILOptimizer/UtilityPasses/Link.cpp in swiftlang/swift
class SILLinker : public SILModuleTransform {
public:
  void run() override {
    SILModule &M = *getModule();
    for (auto &Fn : M)
      if (M.linkFunction(&Fn, LinkMode))
        invalidateAnalysis(&Fn, SILAnalysis::InvalidationKind::Everything);
  }
};
Enter fullscreen mode Exit fullscreen mode

What this really means:
This is a super-clever part of the optimization phase. A SILModule is your entire app's worth of SIL. This pass, SILLinker, runs through it and finds all the functions (Fn). M.linkFunction is the key: it's the code that says, "Oh, you're calling myOtherFunction? Let me go find myOtherFunction's SIL code (maybe from another file) and pull it in so the optimizer can see both functions at the same time." This is what allows Whole Module Optimization and lets the compiler do crazy-smart things like inlining functions across files.

Key Takeaways

     .--.
    /  () \
    \    /
     `--'
       |
       |
      / \
     |   |
     `---'
Enter fullscreen mode Exit fullscreen mode

It's not a single magic box. It's an assembly line:

  • Multi-phase Pipeline: A grammar teacher (Frontend), a hyper-obsessed personal trainer (Mid-End/SIL), a generic factory floor (Back-End/LLVM), and a general contractor (Linker).
  • SIL is Unique: That Swift Intermediate Language (SIL) is the real secret weapon. It lets Swift do all kinds of high-level, safety-conscious optimizations (like for generics and ARC) that other languages just can't.
  • Open Source: This is all just... out there. You can go read the entire swiftlang/swift repository right now. Go on. Psych yourself out.
  • Optimization Focus: They are obsessed with optimization. Every single stage, from SIL to LLVM, is designed to clean, tweak, polish, and speed up your code.
  • Safety First: The compiler is your overprotective parent. It uses semantic analysis (TypeCheckConstraints) and guaranteed SIL transformations to enforce Swift's safety rules so you don't (metaphorically) run with scissors.

Conclusion

So, yeah. The Swift compiler is one hell of a piece of software. It takes your perfectly reasonable human-written code and chews it up, transforms it, optimizes it, and spits it out the other end as blazing-fast, executable machine code. It does all this through four key phases, each ensuring your final program is safe, correct, and as fast as it can be.

Get your head around this pipeline, and you'll not only write better Swift code, but you'll be a wizard at debugging when things go weird. The secret, as we've seen, is that SIL thing. It's the compiler's private notebook, letting it perform high-level optimizations other compilers can only dream of.

And it's all open source! You can go wade into the swiftlang/swift repository if you're feeling brave. For a slightly less terrifying deep dive, check the official Swift compiler documentation.

From your brain to a running binary, the compiler is the unsung hero doing all the heavy lifting. Next time you hit "Run" in Xcode and wait for your print statement to appear, just take a second to appreciate the journey your code is on. It's doing all this work while you just sit there. Computers are so cool!

( •_•)
( •_•)>⌐■-■
(⌐■_■)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)