Code Generation Pipeline
Relevant source files
This document details the compile-time transformation pipeline that converts user-defined per-CPU variable declarations into architecture-specific, optimized access code. The pipeline is implemented as a procedural macro system that analyzes user code, detects target platform characteristics, and generates efficient per-CPU data access methods.
For information about the runtime memory layout and initialization, see Memory Layout and Initialization. For details about the specific assembly code generated for each architecture, see Architecture-Specific Code Generation.
Pipeline Overview
The code generation pipeline transforms a simple user declaration into a comprehensive per-CPU data access system through multiple stages of analysis and code generation.
High-Level Transformation Flow
flowchart TD subgraph subGraph2["Output Components"] OUTPUT["Generated Components"] STORAGE["__PERCPU_VAR in .percpu section"] WRAPPER["VAR_WRAPPER struct"] INTERFACE["VAR: VAR_WRAPPER static"] end subgraph subGraph1["Generation Phase"] GENERATE["Code Generation"] STORAGE_GEN["__PERCPU_VAR generation"] WRAPPER_GEN["VAR_WRAPPER generation"] METHOD_GEN["Method generation"] ARCH_GEN["Architecture-specific code"] end subgraph subGraph0["Analysis Phase"] ANALYZE["Type & Feature Analysis"] TYPE_CHECK["is_primitive_int check"] FEATURE_CHECK["Feature flag detection"] SYMBOL_GEN["Symbol name generation"] end INPUT["#[def_percpu] static VAR: T = init;"] PARSE["syn::parse_macro_input"] ANALYZE --> FEATURE_CHECK ANALYZE --> GENERATE ANALYZE --> SYMBOL_GEN ANALYZE --> TYPE_CHECK GENERATE --> ARCH_GEN GENERATE --> METHOD_GEN GENERATE --> OUTPUT GENERATE --> STORAGE_GEN GENERATE --> WRAPPER_GEN INPUT --> PARSE OUTPUT --> INTERFACE OUTPUT --> STORAGE OUTPUT --> WRAPPER PARSE --> ANALYZE
Sources: percpu_macros/src/lib.rs(L72 - L252)
Input Parsing and Analysis
The pipeline begins with the def_percpu
procedural macro, which uses the syn
crate to parse the user's static variable declaration into an Abstract Syntax Tree (AST).
Parsing Stage
flowchart TD subgraph subGraph0["Extracted Components"] EXTRACT["Component Extraction"] ATTRS["attrs: &[Attribute]"] VIS["vis: &Visibility"] NAME["ident: &Ident"] TYPE["ty: &Type"] INIT["expr: &Expr"] end USER["#[def_percpu]static COUNTER: u64 = 0;"] AST["ItemStatic AST"] AST --> EXTRACT EXTRACT --> ATTRS EXTRACT --> INIT EXTRACT --> NAME EXTRACT --> TYPE EXTRACT --> VIS USER --> AST
The parsing logic extracts key components from the declaration:
Component | Purpose | Example |
---|---|---|
attrs | Preserve original attributes | #[no_mangle] |
vis | Maintain visibility | pub |
ident | Variable name | COUNTER |
ty | Type information | u64 |
expr | Initialization expression | 0 |
Sources: percpu_macros/src/lib.rs(L80 - L86)
Type Analysis and Symbol Generation
The pipeline performs type analysis to determine code generation strategy and generates internal symbol names:
flowchart TD subgraph subGraph1["Generated Symbols"] SYMBOL_GEN["Symbol Name Generation"] INNER["_PERCPU{name}"] WRAPPER["{name}_WRAPPER"] PUBLIC["{name}"] end subgraph subGraph0["Primitive Type Detection"] PRIMITIVE_CHECK["is_primitive_int check"] BOOL["bool"] U8["u8"] U16["u16"] U32["u32"] U64["u64"] USIZE["usize"] end TYPE_ANALYSIS["Type Analysis"] PRIMITIVE_CHECK --> BOOL PRIMITIVE_CHECK --> U16 PRIMITIVE_CHECK --> U32 PRIMITIVE_CHECK --> U64 PRIMITIVE_CHECK --> U8 PRIMITIVE_CHECK --> USIZE SYMBOL_GEN --> INNER SYMBOL_GEN --> PUBLIC SYMBOL_GEN --> WRAPPER TYPE_ANALYSIS --> PRIMITIVE_CHECK TYPE_ANALYSIS --> SYMBOL_GEN
The type analysis at percpu_macros/src/lib.rs(L91 - L92) determines whether to generate optimized assembly access methods for primitive integer types.
Sources: percpu_macros/src/lib.rs(L88 - L92)
Code Generation Stages
The pipeline generates three primary components for each per-CPU variable, with different methods and optimizations based on type and feature analysis.
Component Generation Structure
flowchart TD subgraph subGraph2["Interface Component"] INTERFACE["VAR static variable"] PUBLIC_API["Public API access point"] ZERO_SIZED["Zero-sized type"] end subgraph subGraph1["Wrapper Component"] WRAPPER["VAR_WRAPPER struct"] BASIC_METHODS["Basic access methods"] ARCH_METHODS["Architecture-specific methods"] PRIMITIVE_METHODS["Primitive type methods"] end subgraph subGraph0["Storage Component"] STORAGE["__PERCPU_VAR"] SECTION[".percpu section placement"] ATTRS_PRESERVE["Preserve original attributes"] INIT_PRESERVE["Preserve initialization"] end GENERATION["Code Generation"] GENERATION --> INTERFACE GENERATION --> STORAGE GENERATION --> WRAPPER INTERFACE --> PUBLIC_API INTERFACE --> ZERO_SIZED STORAGE --> ATTRS_PRESERVE STORAGE --> INIT_PRESERVE STORAGE --> SECTION WRAPPER --> ARCH_METHODS WRAPPER --> BASIC_METHODS WRAPPER --> PRIMITIVE_METHODS
Sources: percpu_macros/src/lib.rs(L149 - L251)
Method Generation Logic
The wrapper struct receives different sets of methods based on type analysis and feature configuration:
Method Category | Condition | Generated Methods |
---|---|---|
Basic Access | Always | offset(),current_ptr(),current_ref_raw(),current_ref_mut_raw(),with_current(),remote_ptr(),remote_ref_raw(),remote_ref_mut_raw() |
Primitive Optimized | is_primitive_int == true | read_current_raw(),write_current_raw(),read_current(),write_current() |
Preemption Safety | feature = "preempt" | AutomaticNoPreemptGuardin safe methods |
Sources: percpu_macros/src/lib.rs(L100 - L145) percpu_macros/src/lib.rs(L161 - L249)
Architecture-Specific Code Generation
The pipeline delegates architecture-specific code generation to specialized functions in the arch
module, which produce optimized assembly code for each supported platform.
Architecture Detection and Code Selection
flowchart TD subgraph subGraph2["Register Access Patterns"] X86_GS["x86_64: gs:[offset __PERCPU_SELF_PTR]"] ARM_TPIDR["aarch64: mrs TPIDR_EL1/EL2"] RISCV_GP["riscv: mv gp"] LOONG_R21["loongarch64: move $r21"] end subgraph subGraph1["Target Architecture Detection"] X86_OFFSET["x86_64: mov offset VAR"] ARM_OFFSET["aarch64: movz #:abs_g0_nc:VAR"] RISCV_OFFSET["riscv: lui %hi(VAR), addi %lo(VAR)"] LOONG_OFFSET["loongarch64: lu12i.w %abs_hi20(VAR)"] end subgraph subGraph0["Code Generation Functions"] ARCH_GEN["Architecture Code Generation"] OFFSET["gen_offset()"] CURRENT_PTR["gen_current_ptr()"] READ_RAW["gen_read_current_raw()"] WRITE_RAW["gen_write_current_raw()"] end ARCH_GEN --> CURRENT_PTR ARCH_GEN --> OFFSET ARCH_GEN --> READ_RAW ARCH_GEN --> WRITE_RAW CURRENT_PTR --> ARM_TPIDR CURRENT_PTR --> LOONG_R21 CURRENT_PTR --> RISCV_GP CURRENT_PTR --> X86_GS OFFSET --> ARM_OFFSET OFFSET --> LOONG_OFFSET OFFSET --> RISCV_OFFSET OFFSET --> X86_OFFSET
Sources: percpu_macros/src/arch.rs(L16 - L50) percpu_macros/src/arch.rs(L54 - L88)
Assembly Code Generation Patterns
The architecture-specific functions generate different assembly instruction sequences based on the target platform and operation type:
Architecture | Offset Calculation | Current Pointer Access | Direct Read/Write |
---|---|---|---|
x86_64 | mov {reg}, offset {symbol} | mov {reg}, gs:[offset __PERCPU_SELF_PTR] | mov gs:[offset {symbol}], {val} |
AArch64 | movz {reg}, #:abs_g0_nc:{symbol} | mrs {reg}, TPIDR_EL1 | Not implemented |
RISC-V | lui {reg}, %hi({symbol}) | mv {reg}, gp | lui + add + load/store |
LoongArch | lu12i.w {reg}, %abs_hi20({symbol}) | move {reg}, $r21 | lu12i.w + ori + load/store |
Sources: percpu_macros/src/arch.rs(L94 - L181) percpu_macros/src/arch.rs(L187 - L263)
Feature-Based Code Variations
The pipeline adapts code generation based on feature flags, creating different implementations for various use cases and platform configurations.
Feature Flag Processing
flowchart TD subgraph subGraph2["Generated Code Variations"] GLOBAL_VAR["Global variable access"] SAFE_METHODS["Preemption-safe methods"] HYP_MODE["Hypervisor mode support"] end subgraph subGraph1["Code Generation Impact"] NAIVE_IMPL["naive.rs implementation"] GUARD_GEN["NoPreemptGuard generation"] TPIDR_EL2["TPIDR_EL2 register selection"] end subgraph subGraph0["Core Features"] FEATURES["Feature Configuration"] SP_NAIVE["sp-naive"] PREEMPT["preempt"] ARM_EL2["arm-el2"] end ARM_EL2 --> TPIDR_EL2 FEATURES --> ARM_EL2 FEATURES --> PREEMPT FEATURES --> SP_NAIVE GUARD_GEN --> SAFE_METHODS NAIVE_IMPL --> GLOBAL_VAR PREEMPT --> GUARD_GEN SP_NAIVE --> NAIVE_IMPL TPIDR_EL2 --> HYP_MODE
The feature-based variations are configured at percpu_macros/Cargo.toml(L15 - L25) and affect code generation in several ways:
Feature | Impact | Code Changes |
---|---|---|
sp-naive | Single-processor fallback | Usesnaive.rsinstead ofarch.rs |
preempt | Preemption safety | GeneratesNoPreemptGuardin safe methods |
arm-el2 | Hypervisor mode | UsesTPIDR_EL2instead ofTPIDR_EL1 |
Sources: percpu_macros/src/lib.rs(L59 - L60) percpu_macros/src/lib.rs(L94 - L98) percpu_macros/src/arch.rs(L55 - L61)
Final Code Generation and Output
The pipeline concludes by assembling all generated components into the final token stream using the quote!
macro.
Output Structure Assembly
flowchart TD subgraph subGraph1["Quote Assembly"] ASSEMBLY["Token Stream Assembly"] QUOTE_MACRO["quote! { ... }"] TOKEN_STREAM["proc_macro2::TokenStream"] PROC_MACRO["TokenStream::into()"] end subgraph subGraph0["Component Assembly"] COMPONENTS["Generated Components"] STORAGE_TOKENS["Storage variable tokens"] WRAPPER_TOKENS["Wrapper struct tokens"] IMPL_TOKENS["Implementation tokens"] INTERFACE_TOKENS["Interface tokens"] end OUTPUT["Final Output"] ASSEMBLY --> OUTPUT ASSEMBLY --> QUOTE_MACRO COMPONENTS --> ASSEMBLY COMPONENTS --> IMPL_TOKENS COMPONENTS --> INTERFACE_TOKENS COMPONENTS --> STORAGE_TOKENS COMPONENTS --> WRAPPER_TOKENS QUOTE_MACRO --> TOKEN_STREAM TOKEN_STREAM --> PROC_MACRO
The final assembly process at percpu_macros/src/lib.rs(L149 - L251) combines:
- Storage Declaration:
static mut __PERCPU_{name}: {type} = {init};
with.percpu
section attribute - Wrapper Struct: Zero-sized struct with generated methods for access
- Implementation Block: All the generated methods for the wrapper
- Public Interface:
static {name}: {name}_WRAPPER = {name}_WRAPPER {};
The complete transformation ensures that a simple #[def_percpu] static VAR: T = init;
declaration becomes a comprehensive per-CPU data access system with architecture-optimized assembly code, type-safe interfaces, and optional preemption safety.