Overview
2c (“to C”) is Porffor’s own WebAssembly-to-C compiler. It translates the WebAssembly bytecode generated by Porffor into optimized C code, which can then be compiled into native binaries using standard C compilers. Unlike traditional Wasm-to-C tools like wasm2c, 2c uses Porffor’s internal information to generate specific, efficient C code with minimal boilerplate.Why 2c?
Benefits:- Native Performance: Compiled binaries run at near-native C speeds
- No Runtime: Zero runtime overhead, no JIT compilation
- Standalone: Single binary with no external dependencies
- Cross-Platform: Compile on one platform, run on another
- Small Footprint: Minimal boilerplate code
- Not sandboxed (unlike WebAssembly)
- Experimental and may have bugs
- Larger binary size than pure Wasm
- Compile time is longer
Compiling to Native Binaries
Compile JavaScript directly to a native executable:Platform-Specific Output
Choosing a C Compiler
Specify which C compiler to use:Optimization Levels
Control C compiler optimization:Binaries are stripped by default to reduce size. Debug symbols are removed.
Compiling to C Source
Generate C code without compiling to binary:- Inspecting generated C code
- Custom compilation workflows
- Understanding how 2c works
- Debugging issues
Example Output
How 2c Works
Compilation Pipeline
Type System
2c translates WebAssembly types to C types:| Wasm Type | C Type | Description |
|---|---|---|
i32 | i32 (int32_t) | 32-bit integer |
u32 | u32 (uint32_t) | Unsigned 32-bit integer |
i64 | i64 (int64_t) | 64-bit integer |
u64 | u64 (uint64_t) | Unsigned 64-bit integer |
f32 | f32 (float) | 32-bit float |
f64 | f64 (double) | 64-bit float |
i8 | u8 (uint8_t) | 8-bit integer |
i16 | u16 (uint16_t) | 16-bit integer |
Memory Model
2c allocates WebAssembly linear memory using C’s heap:Memory Safety Option
Usememcpy instead of pointer casts (safer but potentially slower):
--2cMemcpy:
Function Translation
2c translates WebAssembly functions to C functions: WebAssembly:Multi-Value Returns
Porffor functions typically return(value, type) pairs:
Platform-Specific Code
2c can generate platform-specific code for Windows vs. Unix:Data Sections
2c initializes WebAssembly data sections inmain():
Includes and Dependencies
2c automatically includes necessary headers:Performance Considerations
Optimization Tips
-
Use Ofast: Default for maximum performance
-
Choose the Right Compiler:
- Clang: Generally fastest, best optimization
- GCC: Good alternative, widely available
- Zig: Great for cross-compilation
-
Profile Before Optimizing: Run with profiling first
- Minimize Memory Allocations: Reuse objects in hot loops
- Use Typed Arrays: Better performance than regular arrays
Performance Comparison
Typical performance (relative to Node.js):- Wasm (interpreted): 2-5x faster
- Wasm (JIT): Similar speed (after warmup)
- Native (2c + Ofast): 3-10x faster
Performance varies greatly depending on the workload. I/O-heavy code may see less benefit than CPU-heavy code.
Cross-Compilation
Compile for different platforms using Zig:Debugging Native Binaries
Compile with Debug Info
Use GDB/LLDB
Common Issues
Segmentation Fault:- Usually memory access issues
- Check pointer arithmetic in inline Wasm
- Verify array bounds
- Check type conversions
- Verify memory layout assumptions
- Compare against Wasm output
- Check generated C code for invalid constructs
- Try different optimization levels
- Report bugs to Porffor
Limitations
- No Sandboxing: Unlike Wasm, native code has full system access
- Platform-Specific: Binaries are platform-specific
- Experimental: May have bugs or generate incorrect code
- Limited Debugging: Harder to debug than JavaScript
- Compile Time: Slower than Wasm-only compilation
Security Considerations
If you need sandboxing, use WebAssembly output instead:Source Code
The 2c compiler is implemented incompiler/2c.js. Key components:
- Type translation: Wasm types → C types
- Instruction translation: Wasm opcodes → C code
- Memory model: Linear memory allocation
- Function generation: Wasm functions → C functions
- Data initialization: Wasm data sections → C arrays
Examples
Hello World
Fibonacci (Performance)
File Processing
Best Practices
- Test in Wasm First: Validate correctness before compiling to native
- Use Optimization: Always use
-cO=Ofastor-cO=O3for production - Profile: Use
porf profileto find bottlenecks - Benchmark: Compare native vs. Wasm vs. Node.js
- Check Generated C: Review
porf coutput for issues - Keep It Simple: Complex inline Wasm may not translate well
Future Improvements
Planned enhancements for 2c:- Better optimization passes
- SIMD support translation
- Smaller binary sizes
- Faster compilation
- Better debugging support
- More platform-specific optimizations
Getting Help
If you encounter issues with 2c:- Check generated C code:
porf c script.js output.c - Try different compilers:
--compiler=gccvs--compiler=clang - Test with Wasm first:
porf wasm script.js test.wasm - Report bugs on GitHub
- Ask in Discord
Related Documentation
- Inline WebAssembly - Low-level Wasm in JS
- Custom Built-ins - Optimized built-in functions
- Optimization Strategies - Performance optimization tips