Lesson 41
Inline Assembly
Inline Assembly allows assembly instructions to be embedded directly inside a C program. GCC Extended Inline Assembly supports input operands, output operands, and clobber lists that help the compiler integrate assembly safely with surrounding C code. It is used in operating system kernels, embedded systems, device drivers, and performance-critical routines where standard C cannot access processor-specific features directly.
C Track
Save progress as you learn.
44 lessons
Lesson content
Inline Assembly in C Programming: A Complete Beginner to Advanced Guide
What is Inline Assembly in C Programming?
Although C is considered a low-level programming language, there are situations where developers need even more direct control over the processor. Tasks such as accessing CPU-specific instructions, optimizing performance-critical code, interacting with hardware, or implementing operating system kernels often require writing assembly language.
Inline Assembly (also called Inline ASM) allows you to write assembly instructions directly inside a C program. Instead of creating a separate assembly source file, you can embed assembly code within C functions.
Think of C as driving a car with a steering wheel and pedals, while assembly language is like controlling each engine component manually. Inline Assembly lets you temporarily switch to manual control whenever you need maximum precision.
Why Does Inline Assembly Exist?
Without Inline Assembly, some processor instructions cannot be accessed directly, hardware-specific operations become difficult, and certain performance optimizations are impossible. With Inline Assembly, you can access CPU registers directly, execute processor-specific instructions, optimize performance-critical code, implement low-level system software, and communicate directly with hardware.
Real-World Use Cases
Inline Assembly is commonly used in:
- Operating system kernels
- Embedded systems
- Device drivers
- Bootloaders
- Cryptography
- High-performance computing
- CPU instruction testing
- Interrupt handling
- Context switching
- Hardware control
Prerequisites
Before learning Inline Assembly in C programming, you should understand:
- Basic C programming
- Variables and data types
- Functions
- Pointers
- Memory layout
- CPU registers
- Basic assembly language concepts
- GCC compiler usage
Core Concepts
What is Inline Assembly?
Inline Assembly allows assembly instructions to be embedded directly inside C code. Both forms below are accepted by GCC:
asm("assembly instructions");
__asm__("assembly instructions");GCC Extended Inline Assembly Syntax
asm (
"assembly instructions"
: output_operands
: input_operands
: clobbered_registers
);| Component | Description |
|---|---|
| Assembly Template | Assembly instructions as a string |
| Output Operands | Variables modified by assembly |
| Input Operands | Variables passed to assembly |
| Clobber List | Registers or memory modified by assembly |
Basic Assembly Instructions
| Instruction | Purpose |
|---|---|
| `mov` | Move data |
| `add` | Add values |
| `sub` | Subtract values |
| `mul` | Multiply |
| `div` | Divide |
| `inc` | Increment |
| `dec` | Decrement |
| `cmp` | Compare |
| `jmp` | Jump |
| `nop` | No operation |
Operand Constraints
Constraints tell GCC where operands may be placed.
| Constraint | Meaning |
|---|---|
| `"r"` | General-purpose register |
| `"m"` | Memory location |
| `"i"` | Immediate constant |
| `"g"` | Register or memory |
| `"0"` | Same location as first output operand |
asm("add %1, %0"
: "+r"(a)
: "r"(b));Clobber List
The clobber list informs GCC about registers or resources modified by the assembly code. Common clobbers include "memory", "cc" (condition flags), and register names such as "eax".
asm(
"nop"
:
:
: "memory"
);Volatile Assembly
Use volatile when the compiler must not optimize away the assembly block. This is useful for hardware interaction and timing-sensitive operations.
asm volatile("nop");Code Examples
Example 1: Beginner – Executing a NOP Instruction
#include <stdio.h> // Standard input/output library
int main() // Program entry point
{
asm volatile("nop"); // Execute a No Operation instruction
printf("NOP executed.\n"); // Display message
return 0; // Exit successfully
}Example 2: Intermediate – Adding Two Numbers
#include <stdio.h> // Standard I/O library
int main() // Program entry point
{
int a = 10; // First operand
int b = 20; // Second operand
asm(
"add %1, %0" // Add second operand to first
: "+r"(a) // Output/Input operand
: "r"(b) // Input operand
: "cc" // Condition codes modified
);
printf("%d\n", a); // Display result
return 0; // Exit program
}Example 3: Intermediate – Swapping Two Values
#include <stdio.h> // Standard I/O library
int main() // Program entry point
{
int x = 5; // First value
int y = 10; // Second value
asm(
"xchg %0, %1" // Exchange values
: "+r"(x), "+r"(y) // Output/Input operands
: // No additional inputs
: // No clobbers
);
printf("x = %d\n", x); // Display new x
printf("y = %d\n", y); // Display new y
return 0; // Exit program
}Example 4: Advanced – Reading the Time Stamp Counter (x86)
#include <stdio.h> // Standard I/O library
#include <stdint.h> // Fixed-width integer types
int main() // Program entry point
{
uint32_t low; // Lower 32 bits
uint32_t high; // Upper 32 bits
asm volatile(
"rdtsc" // Read Time Stamp Counter
: "=a"(low), "=d"(high) // Outputs from EAX and EDX
);
unsigned long long ticks =
((unsigned long long)high << 32) | low; // Combine values
printf("%llu\n", ticks); // Display CPU ticks
return 0; // Exit program
}Example 5: Advanced – Using CPUID (x86)
#include <stdio.h> // Standard I/O library
int main() // Program entry point
{
unsigned int eax = 0; // Input value
unsigned int ebx; // Output register
unsigned int ecx; // Output register
unsigned int edx; // Output register
asm volatile(
"cpuid" // Execute CPUID instruction
: "=a"(eax), "=b"(ebx),
"=c"(ecx), "=d"(edx) // Output registers
: "a"(eax) // Input register
);
printf("CPU Vendor Registers:\n"); // Display heading
printf("EBX = %u\n", ebx); // Print EBX
printf("ECX = %u\n", ecx); // Print ECX
printf("EDX = %u\n", edx); // Print EDX
return 0; // Exit program
}Common Mistakes and Pitfalls
| Wrong | Correct |
|---|---|
| Forgetting operand constraints | Specify proper input and output constraints |
| Omitting clobbered registers | List all modified registers and flags |
| Writing architecture-specific code without documentation | Clearly document target architecture |
| Using Inline Assembly for ordinary arithmetic | Prefer normal C unless assembly is required |
| Ignoring compiler optimizations | Use volatile only when necessary |
Wrong:
asm("add eax, ebx");Correct:
asm(
"add %1, %0"
: "+r"(a)
: "r"(b)
: "cc"
);Best Practices
- Use Inline Assembly only when standard C cannot accomplish the task.
- Prefer compiler intrinsics when available as they are usually more portable and easier to optimize.
- Clearly document architecture-specific instructions.
- Keep assembly blocks as small and focused as possible.
- Specify accurate input, output, and clobber lists.
- Use volatile only when preventing optimization is necessary.
- Test code with different optimization levels.
- Verify compatibility across compiler versions and CPU architectures.
- Review the generated assembly with gcc -S to understand compiler behaviour.
- Keep portability in mind, especially for projects targeting multiple platforms.
When NOT to Use This
Inline Assembly is not the best choice when:
- Standard C provides an efficient solution.
- Compiler intrinsics expose the required CPU instruction.
- Portability across different CPU architectures is important.
- Readability and maintainability are higher priorities than low-level optimization.
- The performance benefit is insignificant compared to the added complexity.
Summary / Key Takeaways
- Inline Assembly embeds assembly instructions directly within C code.
- GCC Extended Inline Assembly supports input operands, output operands, and clobber lists.
- Operand constraints help the compiler allocate registers and memory correctly.
- volatile prevents the compiler from removing essential assembly instructions.
- Inline Assembly is useful for hardware interaction, processor-specific instructions, and performance-critical routines.
- It should be used sparingly because it reduces portability and increases maintenance complexity.
- Prefer compiler intrinsics or standard C whenever they provide equivalent functionality.
FAQ About Inline Assembly in C
1. What is Inline Assembly in C programming?
Inline Assembly allows assembly language instructions to be written directly inside a C program, enabling access to processor-specific features and low-level operations.
2. Why should I use Inline Assembly?
It is useful when you need CPU-specific instructions, direct hardware access, or highly optimized routines that cannot be expressed efficiently in standard C.
3. What is the purpose of operand constraints?
Operand constraints tell the compiler where operands may be stored such as registers or memory and how they are used, allowing it to integrate the assembly block safely with surrounding C code.
4. What does asm volatile mean?
volatile tells the compiler that the assembly block has important side effects and must not be removed or reordered during optimization.
5. Is Inline Assembly portable?
No. Inline Assembly is generally compiler- and architecture-specific. Code written for GCC on x86 processors may not compile or work correctly on ARM processors or with a different compiler.