Porting Linux Applications to LoongArch
Porting Linux applications from x86, ARM, or other architectures to LoongArch is generally straightforward for C/C++ code, but requires attention to architecture-specific code, assembly language, and optimization strategies. This guide covers the complete porting process.
Overview of LoongArch Architecture
LoongArch is a RISC-style instruction set architecture with the following characteristics:
- 32/64-bit architecture: Supports both 32-bit (LA32) and 64-bit (LA64) modes
- Load-store design: Only load and store instructions access memory
- Fixed instruction length: Most instructions are 32-bit (some are 16-bit in compact mode)
- 32 general-purpose registers: 64-bit wide in LA64 mode
- Little-endian byte order: Same as x86 and ARM
- Memory model: Weakly ordered with memory barrier instructions
These characteristics make LoongArch similar to other modern RISC architectures, facilitating porting from ARM and MIPS.
Step 1: Build System Configuration
Update your build system to recognize LoongArch:
Autotools Projects
``bash
Update config.guess and config.sub to latest versions
that recognize loongarch64-unknown-linux-gnu
wget -O config.guess 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD'
wget -O config.sub 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD'
Configure for cross-compilation
./configure --host=loongarch64-unknown-linux-gnu \
CC=loongarch64-linux-gnu-gcc \
CXX=loongarch64-linux-gnu-g++
`
CMake Projects
`cmake
Create toolchain file toolchain-loongarch.cmake
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR loongarch64)
set(CMAKE_C_COMPILER loongarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER loongarch64-linux-gnu-g++)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
`
Then configure:
`bash
cmake -DCMAKE_TOOLCHAIN_FILE=toolchain-loongarch.cmake..
`
Meson Projects
`bash
Create cross file loongarch.txt
cat > loongarch.txt << 'EOF'
[binaries]
c = 'loongarch64-linux-gnu-gcc'
cpp = 'loongarch64-linux-gnu-g++'
ar = 'loongarch64-linux-gnu-ar'
strip = 'loongarch64-linux-gnu-strip'
[host_machine]
system = 'linux'
cpu_family = 'loongarch64'
cpu = 'loongarch64'
endian = 'little'
EOF
meson setup build --cross-file=loongarch.txt
`
Step 2: Handle Architecture Detection
Update architecture detection code:
`c
// Old code might check for specific architectures
#if defined(__x86_64__) || defined(__i386__)
// x86 specific code
#elif defined(__arm__) || defined(__aarch64__)
// ARM specific code
#endif
// Add LoongArch detection
#if defined(__loongarch__)
#if defined(__loongarch64)
// LoongArch 64-bit code
#else
// LoongArch 32-bit code
#endif
#endif
`
Common predefined macros:
- __loongarch__
- Defined on all LoongArch targets - __loongarch64
- Defined on 64-bit LoongArch - __loongarch32
- Defined on 32-bit LoongArch - __loongarch_double_float
- Defined when double-precision FPU present
Step 3: Port Assembly Code
Assembly code requires the most effort to port. Options include:
Option A: Rewrite in C
For small assembly routines, consider rewriting in C. Modern compilers generate efficient code, and C is more portable and maintainable.
Option B: Use Inline Assembly
For performance-critical code, use GCC inline assembly:
`c
// Example: Read CPU cycle counter
static inline uint64_t read_cycles(void) {
uint64_t val;
__asm__ __volatile__ ("rdtime.d %0, $zero" : "=r"(val));
return val;
}
`
Option C: Port Assembly Files
For larger assembly modules, port the. S files to LoongArch syntax:
x86 to LoongArch mapping (common instructions):
| x86 | LoongArch | Description |
| mov | move | Register move |
| add | add.d | Add (64-bit) |
| sub | sub.d | Subtract (64-bit) |
| mul | mul.d | Multiply (64-bit) |
| and | and | Bitwise AND |
| or | or | Bitwise OR |
| call | bl | Branch and link |
| ret | jirl | Jump indirect and link |
| push/pop | st.d/ld.d | Stack operations |
Example porting:
`asm
x86 version
push %rbp
mov %rsp, %rbp
mov %rdi, -8(%rbp)
add $1, -8(%rbp)
pop %rbp
ret
LoongArch version
addi.d $sp, $sp, -16
st.d $fp, $sp, 0
addi.d $fp, $sp, 16
st.d $a0, $fp, -8
ld.d $t0, $fp, -8
addi.d $t0, $t0, 1
st.d $t0, $fp, -8
ld.d $fp, $sp, 0
addi.d $sp, $sp, 16
jirl $zero, $ra, 0
`
Step 4: Handle Endianness
LoongArch uses little-endian byte order, same as x86 and ARM (little-endian mode). If your code already supports x86 or ARM little-endian, no changes are needed.
For code that supports multiple endianness:
`c
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
// Little-endian code (includes LoongArch)
#else
// Big-endian code
#endif
`
Step 5: Memory Barriers
LoongArch uses a weakly-ordered memory model. Use appropriate memory barriers:
`c
// Compiler barrier
__asm__ __volatile__ ("" ::: "memory");
// Full memory barrier
__asm__ __volatile__ ("dbar 0" ::: "memory");
`
For kernel code, use standard Linux memory barrier macros which are architecture-independent.
Step 6: SIMD/Vector Code
LoongArch has LSX (Loongson SIMD eXtension) and LASX (Loongson Advanced SIMD eXtension) for vector operations:
`c
// Check for LSX support
#include <sys/auxv.h>
unsigned long hwcap = getauxval(AT_HWCAP);
if (hwcap & HWCAP_LOONGARCH_LSX) {
// LSX is available
}
`
For portable code, consider using compiler intrinsics or libraries like OpenCV that abstract SIMD operations.
Step 7: Testing and Validation
After porting, thoroughly test your application:
Use debugging tools:
`bash
Run with strace to check system calls
strace -f./myapp
Check for library dependencies
ldd./myapp
Profile performance
perf record./myapp
perf report
`
Common Porting Issues
Issue 1: Unaligned Memory Access
LoongArch handles unaligned access differently than x86. Fix unaligned accesses:
`c
// Instead of direct pointer cast
uint32_t val = (uint32_t)ptr; // May fault on unaligned
// Use memcpy for safe unaligned access
uint32_t val;
memcpy(&val, ptr, sizeof(val));
`
Issue 2: Signal Handling
Signal context structure differs. Use ucontext_t for portable signal handling:
`c
#include <ucontext.h>
void handler(int sig, siginfo_t info, void context) {
ucontext_t uc = (ucontext_t)context;
// Access registers through uc->uc_mcontext
}
`
Issue 3: JIT Compilation
For JIT compilers, add LoongArch code generation backend. Reference existing backends (x86, ARM) for implementation guidance.
Performance Optimization
After successful porting, optimize for LoongArch:
then -fprofile-useGetting Help
If you encounter issues during porting:
- Check Loongson documentation and wiki
- Search existing ported software for reference implementations
- Contact our FAE team for complex porting challenges
- Contribute fixes back to upstream projects
Most applications port with minimal changes. The key is systematic testing and addressing architecture-specific code sections.
💡 FAE Insights
Technical Logic
The porting process follows a natural progression: First, get the code building by fixing build system and architecture detection. This often reveals 90% of the issues. Second, address runtime problems - these are usually alignment issues, signal handling, or memory model differences. Third, optimize for the new architecture - this is where you might add LoongArch-specific SIMD or tune compiler flags. Each phase builds on the previous, and you should have a working (if not optimal) application after phase 2. Don't try to optimize before you have working code - premature optimization complicates debugging.
📋 Customer Cases
Industrial Software Vendor
Software
Challenge
Customer needed to port their SCADA software from x86 to LoongArch to support government customers requiring domestic processors. The codebase was large (500K+ lines) with some x86 assembly for performance-critical communication routines.
Solution
Ported build system to CMake with LoongArch toolchain file. Rewrote assembly routines in C with compiler intrinsics. Fixed alignment issues in protocol buffer handling. Optimized CRC calculation using LSX SIMD instructions.
Customer Feedback
"Porting completed in 6 weeks. Performance improved 15% over original x86 version due to better CRC optimization. Software now deployed at 20+ government facilities."
Frequently Asked Questions
1. How long does it typically take to port an application to LoongArch?
Porting time depends on application complexity and architecture-specific code amount. Pure C/C++ applications with no assembly typically port in days - just rebuild with LoongArch toolchain. Applications with some architecture-specific code (signal handling, thread management) may take 1-2 weeks. Complex applications with significant assembly or JIT compilation may take 1-2 months. The key factors are: amount of assembly code, use of architecture-specific intrinsics, and complexity of build system. Most customers are surprised how quickly their applications port - often 80% of code requires no changes.
2. Do I need to modify my build system for LoongArch?
Most modern build systems require minimal changes for LoongArch. Autotools projects need updated config.guess/config.sub files. CMake needs a toolchain file specifying the cross-compiler. Meson needs a cross-file with similar information. The main work is usually adding LoongArch to architecture detection logic. Build systems that already support multiple architectures (x86, ARM, MIPS) typically need only minor additions. If your build system is well-structured with proper abstraction of platform differences, porting is straightforward.
3. What performance difference should I expect after porting?
Performance after porting varies by application type. Integer-heavy applications typically perform similarly to x86 equivalents. Floating-point performance depends on optimization - may need tuning. Memory bandwidth is competitive with DDR4-3200 support. Applications using SIMD need porting to LSX/LASX for optimal performance. I/O bound applications see minimal difference. Overall, expect 80-120% of x86 performance for most applications after optimization. Some applications may run faster due to better compiler optimizations or architectural advantages. Benchmark your specific workload for accurate assessment.
4. Can I run x86 binaries on Loongson through emulation?
Loongson provides LAT (Loongson Architecture Translator) for running x86 Linux binaries through binary translation. LAT translates x86 instructions to LoongArch at runtime with typically 20-50% performance overhead. Not all x86 applications work perfectly - some may have compatibility issues. Graphics-intensive applications may not work well due to driver differences. For production use, native porting is strongly recommended over emulation. LAT is useful for: quick testing before porting, running legacy proprietary software, and bridging gaps during migration. For best performance and reliability, invest in native porting.
5. What are common mistakes when porting software?
Common mistakes include insufficient planning and requirements analysis, not following reference designs closely enough, inadequate testing at each development phase, overlooking thermal and power design requirements, and not engaging technical support early. Other issues include insufficient validation of software compatibility, inadequate documentation of design decisions, and not planning for long-term software maintenance. We recommend following our development guides, using reference designs as starting points, and engaging our FAE team for design review.