VK

Compiler PrinciplesCompiler Principles

πŸ“š Ensiklopedia Β· Fondasi KuatEnsiklopedia Β· Fondasi Kuat 🌏 Dual Bahasa (ID / EN) ⚑ VibeKoding Native

Ensiklopedia VibeKoding: Compiler Principles.Ensiklopedia VibeKoding: Compiler Principles.

πŸ’‘ Tips PraktisπŸ’‘ Pro Tip

When you press the "Run" button, how does your code become the result on screen? The computer actually can't "understand" any line of code you write β€” it only recognizes 0s and 1s. The compiler is the "translator" that converts human language into machine language. Understanding compiler principles helps you understand where error messages come from, why some languages are faster than others, and the underlying logic of code optimization.When you press the "Run" button, how does your code become the result on screen? The computer actually can't "understand" any line of code you write β€” it only recognizes 0s and 1s. The compiler is the "translator" that converts human language into machine language. Understanding compiler principles helps you understand where error messages come from, why some languages are faster than others, and the underlying logic of code optimization.

What will you learn from this article?What will you learn from this article?

After completing this chapter, you will gain:After completing this chapter, you will gain:

ChapterContentCore Concepts
Chapter 1What Is a CompilerTranslator analogy, compilation pipeline
Chapter 2Lexical AnalysisTokens, lexical rules
Chapter 3Syntax AnalysisAST, syntax trees, precedence
Chapter 4AST VisualizationInteractive syntax tree, node types
Chapter 5Semantic Analysis and OptimizationType checking, constant folding, dead code elimination
Chapter 6Optimization Techniques in PracticeFunction inlining, loop hoisting, constant propagation
Chapter 7Compiled vs Interpreted vs JITComparison of three execution models

------

0. Big Picture: Compilation Pipeline Overview0. Big Picture: Compilation Pipeline Overview

Imagine you're a translator tasked with translating a Chinese novel into English. You wouldn't translate word by word literally. Instead, you would:Imagine you're a translator tasked with translating a Chinese novel into English. You wouldn't translate word by word literally. Instead, you would:

  1. Identify words β€” Break sentences into individual words (lexical analysis)Identify words β€” Break sentences into individual words (lexical analysis)
  2. Understand syntax β€” Determine if sentence structure is correct (syntax analysis)Understand syntax β€” Determine if sentence structure is correct (syntax analysis)
  3. Understand semantics β€” Ensure the meaning is coherent and contradiction-free (semantic analysis)Understand semantics β€” Ensure the meaning is coherent and contradiction-free (semantic analysis)
  4. Polish and refine β€” Make the translation more natural and fluent (code optimization)Polish and refine β€” Make the translation more natural and fluent (code optimization)
  5. Output the translation β€” Write the final English version (code generation)Output the translation β€” Write the final English version (code generation)
  6. A compiler does exactly the same thing, except it translates programming languages.A compiler does exactly the same thing, except it translates programming languages.

    ------

    1. The Compiler's Six-Stage Pipeline1. The Compiler's Six-Stage Pipeline

    A compiler's work can be divided into six stages, like a factory assembly line where each stage hands off to the next.A compiler's work can be divided into six stages, like a factory assembly line where each stage hands off to the next.

    πŸ’‘ Tips PraktisπŸ’‘ Pro Tip

    1. Lexical Analysis: Break source code into tokens (words) 2. Syntax Analysis: Organize tokens into a syntax tree (AST) 3. Semantic Analysis: Check if types are correct and variables are declared 4. Intermediate Code Generation (IR Generation): Generate platform-independent intermediate representation 5. Code Optimization: Make the intermediate code more efficient 6. Code Generation: Generate machine code for the target platform1. Lexical Analysis: Break source code into tokens (words) 2. Syntax Analysis: Organize tokens into a syntax tree (AST) 3. Semantic Analysis: Check if types are correct and variables are declared 4. Intermediate Code Generation (IR Generation): Generate platform-independent intermediate representation 5. Code Optimization: Make the intermediate code more efficient 6. Code Generation: Generate machine code for the target platform

    StageInputOutputAnalogy
    Lexical AnalysisSource code character streamToken streamBreak sentences into words
    Syntax AnalysisToken streamAST (syntax tree)Analyze sentence structure
    Semantic AnalysisASTTyped ASTCheck if the meaning makes sense
    Intermediate CodeTyped ASTIRWrite a first draft
    Code OptimizationIROptimized IRPolish and trim
    Code GenerationOptimized IRMachine codeOutput the final version

    ------

    2. Lexical Analysis: Tokenization of Source Code2. Lexical Analysis: Tokenization of Source Code

    Lexical analysis is the first step of compilation. The compiler scans each character of the source code from left to right, combining them into meaningful tokens.Lexical analysis is the first step of compilation. The compiler scans each character of the source code from left to right, combining them into meaningful tokens.

    Just as your brain automatically combines letters into words when reading an English sentence, the lexer combines characters into tokens:Just as your brain automatically combines letters into words when reading an English sentence, the lexer combines characters into tokens:

    CODE
    Source code: let x = 10 + 5; Token stream: [let] β†’ Keyword (language reserved word) [x] β†’ Identifier (variable name) [=] β†’ Operator (assignment) [10] β†’ Numeric literal [+] β†’ Operator (addition) [5] β†’ Numeric literal [;] β†’ Separator (statement end)
    
    πŸ’‘ Tips PraktisπŸ’‘ Pro Tip

    - Keywords: Special words reserved by the language, such as let, if, return, function - Identifiers: Names defined by programmers, such as variable names and function names - Literals: Values written directly in code, such as the number 42 and the string "hello" - Operators: Symbols that perform operations, such as +, -, =, === - Separators: Symbols that separate code structures, such as ;, ,, (, )- Keywords: Special words reserved by the language, such as let, if, return, function - Identifiers: Names defined by programmers, such as variable names and function names - Literals: Values written directly in code, such as the number 42 and the string "hello" - Operators: Symbols that perform operations, such as +, -, =, === - Separators: Symbols that separate code structures, such as ;, ,, (, )

    ------

    3. Syntax Analysis: Building the Syntax Tree (AST)3. Syntax Analysis: Building the Syntax Tree (AST)

    Lexical analysis breaks code into tokens, but tokens are just isolated "words." The task of syntax analysis is to organize these tokens into an Abstract Syntax Tree (AST) according to grammar rules β€” it reflects the structure of the code and operator precedence.Lexical analysis breaks code into tokens, but tokens are just isolated "words." The task of syntax analysis is to organize these tokens into an Abstract Syntax Tree (AST) according to grammar rules β€” it reflects the structure of the code and operator precedence.

    CODE
    Expression: 1 + 2 * 3 Syntax tree: Why this way? + Because * has higher / \ precedence than +, 1 * so 2 * 3 groups / \ together first 2 3
    
    πŸ’‘ Tips PraktisπŸ’‘ Pro Tip

    AST is the "core data structure" of a compiler. Subsequent semantic analysis, optimization, and code generation are all based on it. Modern development tools also heavily use AST: - ESLint: Parses code into AST and checks for rule violations - Prettier: Parses into AST and reformats the output - Babel: Parses AST β†’ transforms β†’ generates compatible code - IDE refactoring: Performs safe variable renaming and function extraction based on ASTAST is the "core data structure" of a compiler. Subsequent semantic analysis, optimization, and code generation are all based on it. Modern development tools also heavily use AST: - ESLint: Parses code into AST and checks for rule violations - Prettier: Parses into AST and reformats the output - Babel: Parses AST β†’ transforms β†’ generates compatible code - IDE refactoring: Performs safe variable renaming and function extraction based on AST

    Syntax StructureToken SequenceAST Node
    Variable declarationlet x = 10VariableDeclaration β†’ Identifier + Literal
    Function calladd ( 1 , 2 )CallExpression β†’ Identifier + Arguments
    Conditional statementif ( a > b )IfStatement β†’ BinaryExpression + Block

    ------

    4. AST Visualization: Intuitive Presentation of Code Structure4. AST Visualization: Intuitive Presentation of Code Structure

    Above we described AST structure in text, but "seeing" is more intuitive than "reading." The interactive component below lets you select different expressions and observe their syntax trees in real time.Above we described AST structure in text, but "seeing" is more intuitive than "reading." The interactive component below lets you select different expressions and observe their syntax trees in real time.

    Through visualization, you'll find that the core patterns of AST are actually quite simple:Through visualization, you'll find that the core patterns of AST are actually quite simple:

    Code StructureAST Root NodeChild Nodes
    1 + 2 * 3BinaryExpression (+)Left: NumericLiteral(1), Right: BinaryExpression(*)
    let x = 10VariableDeclarationVariableDeclarator β†’ Identifier(x) + NumericLiteral(10)
    add(a, b)CallExpressionIdentifier(add) + Arguments(a, b)
    πŸ’‘ Tips PraktisπŸ’‘ Pro Tip

    You may not have written a compiler directly, but you use AST-based tools every day: - ESLint / Prettier: Parse code into AST for rule checking or reformatting - Babel / SWC: Parse AST β†’ transform syntax β†’ generate compatible code - IDE refactoring: Safe renaming and function extraction based on AST - Tree-shaking: Analyze import/export in AST to remove unused codeYou may not have written a compiler directly, but you use AST-based tools every day: - ESLint / Prettier: Parse code into AST for rule checking or reformatting - Babel / SWC: Parse AST β†’ transform syntax β†’ generate compatible code - IDE refactoring: Safe renaming and function extraction based on AST - Tree-shaking: Analyze import/export in AST to remove unused code

    ------

    5. Semantic Analysis and Code Optimization5. Semantic Analysis and Code Optimization

    Syntax analysis ensures code is "structurally correct," but structural correctness doesn't mean "semantically correct." Semantic analysis checks whether the meaning of the code is valid, while code optimization makes programs run faster.Syntax analysis ensures code is "structurally correct," but structural correctness doesn't mean "semantically correct." Semantic analysis checks whether the meaning of the code is valid, while code optimization makes programs run faster.

    5.1 Semantic Analysis and Type Checking5.1 Semantic Analysis and Type Checking

    CheckExampleResult
    Type checkingint x = "hello"Type mismatch
    Scope checkingUsing undeclared variable yVariable does not exist
    Type inference1 + 2.0Inferred result is float
    Parameter checkingadd(1, 2, 3) but function only accepts 2 parametersParameter count mismatch
    πŸ’‘ Tips PraktisπŸ’‘ Pro Tip

    - TypeError: Cannot read properties of undefined β€” Type checking - ReferenceError: x is not defined β€” Scope checking - Expected 2 arguments, but got 3 β€” Parameter checking- TypeError: Cannot read properties of undefined β€” Type checking - ReferenceError: x is not defined β€” Scope checking - Expected 2 arguments, but got 3 β€” Parameter checking

    5.2 Code Optimization: Equivalent Transformation of Intermediate Representation5.2 Code Optimization: Equivalent Transformation of Intermediate Representation

    Before generating the final code, the compiler applies various optimizations to the intermediate code. These optimizations are transparent to the programmer but can significantly improve performance.Before generating the final code, the compiler applies various optimizations to the intermediate code. These optimizations are transparent to the programmer but can significantly improve performance.

    Optimization TechniqueBeforeAfterPrinciple
    Constant foldingx = 10 + 5x = 15Compute the result at compile time
    Dead code eliminationif (false) { ... }Removed entirelyCode that will never execute
    Constant propagationx = 15; y = x * 2y = 30Replace with known values directly
    Loop-invariant code motionRepeatedly computing len = arr.length inside a loopMove outside the loopAvoid redundant computation

    ------

    6. Optimization Techniques in Practice: How Compilers Make Code Faster6. Optimization Techniques in Practice: How Compilers Make Code Faster

    Above we mentioned several optimization technique names. Now let's dive deeper into exactly how compilers do this. The interactive component below demonstrates 5 of the most common compiler optimizations. You can intuitively compare the code before and after optimization.Above we mentioned several optimization technique names. Now let's dive deeper into exactly how compilers do this. The interactive component below demonstrates 5 of the most common compiler optimizations. You can intuitively compare the code before and after optimization.

    Modern compilers and JIT engines (such as V8, GCC, LLVM) automatically apply dozens of optimizations. As a developer, you don't need to perform these optimizations manually, but understanding them helps you:Modern compilers and JIT engines (such as V8, GCC, LLVM) automatically apply dozens of optimizations. As a developer, you don't need to perform these optimizations manually, but understanding them helps you:

    • Write code that's easier to optimize: For example, using const instead of let makes it easier for the compiler to apply constant foldingWrite code that's easier to optimize: For example, using const instead of let makes it easier for the compiler to apply constant folding
    • Understand performance differences: Why are small functions faster than large ones? Because the compiler can inline themUnderstand performance differences: Why are small functions faster than large ones? Because the compiler can inline them
    • Avoid "de-optimization": Certain coding patterns prevent compiler optimization, such as eval() and withAvoid "de-optimization": Certain coding patterns prevent compiler optimization, such as eval() and with
    Optimization TechniqueTrigger ConditionPerformance ImpactWhat Developers Can Do
    Constant foldingAll constants in an expressionEliminates runtime computationUse const declarations more
    Dead code eliminationUnreachable code or unused resultsReduces code sizeClean up unused code promptly
    Loop-invariant code motionInvariant computation inside a loopReduces redundant computationManual extraction is also a good habit
    Function inliningSmall functions called frequentlyEliminates call overheadKeep functions small and focused
    Constant propagationVariable values known at compile timeEntire computation chain eliminatedUse constants instead of magic numbers

    ------

    7. Compiled vs Interpreted vs JIT7. Compiled vs Interpreted vs JIT

    After writing code, there are three "translation methods" to make it run. Each has its own strengths and weaknesses, directly determining the performance characteristics and use cases of the language.After writing code, there are three "translation methods" to make it run. Each has its own strengths and weaknesses, directly determining the performance characteristics and use cases of the language.

    DimensionCompiledInterpretedJIT (Just-In-Time)
    ProcessFully compile to machine code first, then executeTranslate and execute line by lineInterpret first, then compile hot code
    Execution speedFastestSlowestMedium (hot codeζŽ₯θΏ‘compiled speed)
    Startup speedSlow (requires compilation)Fast (runs directly)Medium (requires warm-up)
    Cross-platformRequires recompilationNaturally cross-platformCross-platform
    Representative languagesC, Rust, GoPython, RubyJavaScript (V8), Java
    πŸ’‘ Tips PraktisπŸ’‘ Pro Tip

    V8's JIT compiler monitors which code is executed frequently (hot code) and compiles it into highly optimized machine code. So although JavaScript is an "interpreted language," its performance in V8 can approach that of compiled languages. This is also the foundation that enables Node.js to be used on the server side.V8's JIT compiler monitors which code is executed frequently (hot code) and compiles it into highly optimized machine code. So although JavaScript is an "interpreted language," its performance in V8 can approach that of compiled languages. This is also the foundation that enables Node.js to be used on the server side.

    ------

    SummarySummary

    Compiler principles aren't just knowledge for compiler developers. Understanding the compilation process helps you better understand error messages, choose appropriate languages, and write more efficient code.Compiler principles aren't just knowledge for compiler developers. Understanding the compilation process helps you better understand error messages, choose appropriate languages, and write more efficient code.

    Review the key points of this chapter:Review the key points of this chapter:

    1. A compiler is a translator: Converts human-readable code into machine-executable instructionsA compiler is a translator: Converts human-readable code into machine-executable instructions
    2. Six-stage pipeline: Lexical analysis β†’ Syntax analysis β†’ Semantic analysis β†’ Intermediate code β†’ Optimization β†’ Code generationSix-stage pipeline: Lexical analysis β†’ Syntax analysis β†’ Semantic analysis β†’ Intermediate code β†’ Optimization β†’ Code generation
    3. Lexical analysis breaks tokens: Breaks character streams into meaningful units like keywords, identifiers, and operatorsLexical analysis breaks tokens: Breaks character streams into meaningful units like keywords, identifiers, and operators
    4. Syntax analysis builds AST: Organizes tokens into a tree structure according to grammar rules, reflecting operator precedenceSyntax analysis builds AST: Organizes tokens into a tree structure according to grammar rules, reflecting operator precedence
    5. Semantic analysis ensures correctness: Type checking, scope checking β€” most errors you encounter come from hereSemantic analysis ensures correctness: Type checking, scope checking β€” most errors you encounter come from here
    6. Compilers optimize automatically: Techniques like constant folding, dead code elimination, and function inlining make code automatically fasterCompilers optimize automatically: Techniques like constant folding, dead code elimination, and function inlining make code automatically faster
    7. Three execution models: Compiled is fastest, interpreted is most flexible, JIT combines the best of bothThree execution models: Compiled is fastest, interpreted is most flexible, JIT combines the best of both
    8. Further ReadingFurther Reading

      • [AST Explorer](https://astexplorer.net/) - View the AST structure of code online[AST Explorer](https://astexplorer.net/) - View the AST structure of code online
      • [Crafting Interpreters](https://craftinginterpreters.com/) - Build a programming language from scratch (free online book)[Crafting Interpreters](https://craftinginterpreters.com/) - Build a programming language from scratch (free online book)
      • [The Super Tiny Compiler](https://github.com/jamiebuilds/the-super-tiny-compiler) - A super small compiler implemented in JavaScript[The Super Tiny Compiler](https://github.com/jamiebuilds/the-super-tiny-compiler) - A super small compiler implemented in JavaScript
      • [V8 Blog](https://v8.dev/blog) - V8 engine's JIT compilation technology blog[V8 Blog](https://v8.dev/blog) - V8 engine's JIT compilation technology blog
      • [LLVM Official Site](https://llvm.org/) - The most popular compiler infrastructure[LLVM Official Site](https://llvm.org/) - The most popular compiler infrastructure