我要评分
获取效率
正确性
完整性
易理解

Poring Rust Inline Assembly Instructions

Rust supports inline assembly. When instructions in the inline assembly statement are not in the AArch64 architecture, the instructions need to be ported. The procedure is similar to that of the inline assembly porting in C and C++, but the compilation format is different. Rust attaches great importance to memory security. The code of the inline assembly feature module must be run in the scope of keyword unsafe, and #![feature(asm)] or #![feature(llvm_asm)] must be used for description. Currently, Rust inline assembly has two forms: asm and llvm_asm.

Symptom

When the source code contains x86 inline assembly, "error: unrecognized instruction mnemonic" or "error: invalid asm template modifier for this register class" is displayed during compilation.

asm Inline Assembly

The usage of asm inline assembly in Rust is more convenient and easier to understand. It is similar to that in C and C++, but the representation is different. The following example describes the usage of asm inline assembly in Rust:
asm!("rev {dst:w}, {src:w}", 
    dst = lateout(reg)val, 
    src = in(reg)val
);
  • rev: reverse byte order assembly instruction.
  • {dst:w}/{src:w}: placeholder of the rev instruction operand. :w indicates that the 32-bit W register is used, and :x indicates that the 64-bit X register is used.
  • dst = lateout(reg)val: dst is a user-defined mnemonic symbol, lateout indicates output (multiplexing the same register), reg indicates register constraints, and val indicates the actual Rust variable.
  • src = in(reg)val: src is a user-defined mnemonic symbol, in indicates input, reg indicates register constraints, and val indicates the actual Rust variable.

Example:

The following example describes how to port the bswap inline assembly instruction in x86. The instruction reverses the byte order of 32-bit unsigned numbers.

#![feature(asm)]
//bswap inline assembly implementation in x86
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn bswap(mut val : u32) -> u32
{
    unsafe {
        asm!("bswap {:e}", inout(reg)val);
    }
    return val;
}
//bswap inline assembly implementation in Kunpeng
#[cfg(any(target_arch = "aarch64"))]
fn bswap(mut val : u32) -> u32
{
    unsafe {
        asm!("rev {dst:w}, {src:w}", dst = lateout(reg)val, src = in(reg)val);
    }
    return val;
}
fn main()
{
    let val = bswap(32);
    println!("The result {}", val);
}

llvm_asm Inline Assembly

Rust can also use the llvm_asm! macro for inline assembly. It is similar to the C and C++ inline assemblies. For details about the rules, see http://llvm.org/docs/LangRef.html#inline-assembler-expressions. The general format is as follows:

llvm_asm!(assembly  template    
    :   output operands 
    :   input operands  
    :   clobbers    
    :   options 
);
  • assembly template: assembly instruction.
  • output operands: list of output operands. Use commas (,) to separate multiple operands.
  • input operands: list of input operands. Use commas (,) to separate multiple operands.
  • clobbers: (optional) notifies the resource list such as the modified register.
  • options: This parameter is specific to the Rust language. Use commas (,) to separate multiple options. Currently, the following options are supported:
    • volatile: equivalent to __volatile__ in GCC/Clang compilers, which instructs the compiler not to optimize the inline assembly.
    • alignstack: instructs the compiler to perform alignment in a specific manner.
    • intel: uses the Intel syntax instead of the default AT&T syntax.

Example:

The following example describes how to port the bswap inline assembly instruction in x86. The instruction reverses the byte order of 32-bit unsigned numbers.

#![feature(llvm_asm)]
//bswap inline assembly implementation in x86
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn bswap(mut val : u32) -> u32
{
    unsafe {
        llvm_asm!("bswap $0" : "=r" (val) : "0" (val));
    }
    return val;
}
//bswap inline assembly implementation in Kunpeng
#[cfg(any(target_arch = "aarch64"))]
fn bswap(mut val : u32) -> u32
{
    unsafe {
       llvm_asm!( "rev ${0:w}, ${1:w}" : "=r" (val) : "r" (val));
       // :w in {dst:w} indicates that the W register is used, and :x indicates that the X register is used.
    }
    return val;
}
fn main()
{
    let val = bswap(32);
    println!("The result {}", val);
}
  • $num represents operands in the inline assembly. For example, $0 and $1 correspond to the placeholders of the output and input operands respectively. The preceding example also includes register constraints, such as ${0:w} and ${1:w}. In {dst:w}, :w indicates that the 32-bit W register is used, and :x indicates that the 64-bit X register is used.
  • When compiling a file that contains inline assembly statements, run the rustup install nightly command to install the Rust nightly version in advance. During compilation, add the +nightly option to the command, for example, rustc +nightly main.rs.