MIPS Inline Assembly Language Examples
The MIPS instruction set is designed to be used with optimizing compilers. The MIPS compiler (CLMIPS.EXE) for Visual C++ generates high-quality MIPS assembly-language code. Therefore, use __asm statements only when necessary. While the examples shown here illustrate the use of __asm statements, they are not inherently any more efficient than the code typically produced directly by the Visual C++ compiler.
The MIPS edition of the compiler allows you to use the __asm statement to write macros that you can call from within C or C++ source code. The syntax of the __asm statement makes such macros possible because of its flexible support for arguments. As illustrated in the following example, you do not need to fix argument values, but can specify them at the time of macro expansion. However, call a macro such as this only from within C or C++ functions.
In the following example, a sample function named my_mult takes two unsigned 32-bit integer arguments, a and b, and multiplies them together. The product is stored at the location specified by the third argument, c, which is a pointer to a 32-bit argument. The function also determines what the overflow portion of the multiplication is, if any, and returns the overflow portion through its return value.
extern "C" { // Use extern "c" for C++ file only
void __asm(char*, ...);
};
#pragma intrinsic(__asm) // this is actually optional
long my_mult(long a, long b, long* c) {
long overflow;
__asm("mult $4,$5");
__asm("mflo $4");
__asm("sw $4, 0($6)"); // store product
__asm("mfhi $11");
__asm("sra $12,$4, 31");
__asm("sne $12,$12,$11");
__asm("sw $12, 0(%0)", &overflow); // store overflow
return(overflow);
}
Because the preceding example does not use conditional compilation, the code is not portable to all MIPS platforms. To make it portable, #ifdef blocks would have to be used.
The preceding example function places its arguments a, b, and c in the registers $4, $5, and $6, respectively. These are the first three argument registers; they can also be referred to as a0, a1, and a2.
The local variable overflow is defined in C or C++ code. Inline assembly code can easily recognize variables defined in C or C++ statements because of the __asm argument-handling feature. The expression &overflow is referred to as the argument %0 in the string portion of the following statement:
asm("sw $12, 0(%0)",&overflow); // store
The following example shows how to us the __asm statement with multiple instructions separated by semicolons (;) and explicitly coded new-lines (\n).
extern "C" { // Use extern "c" for C++ file only
void __asm(char*, ...);
};
long my_mult(long a, long b, long* c) {
long overflow;
__asm("mult $4,$5;"
"mflo $4;"
"sw $4, 0($6);" // store product
"mfhi $11;"
"sra $12,$4, 31;"
"sne $12,$12,$11;"
__asm("sw $12, 0(%0)", &overflow); // store overflow
return(overflow);
}
Notice that the use of "%0" has potential conflicts with the my_mult function argument. Both the value of argument variable "a" and argument of __asm statement, the address of variable overflow, are passed in register $4. To avoid corrupting the value of argument "a" by the argument of the __asm statement, finish using the argument in $4 first, before using an __asm statement that contains arguments following its assembly string.
The inline assembler occasionally generates nop (no-operation) instructions to improve performance. Without the nop instructions, in some cases the processor would have to interlock the pipeline until all dependencies were satisfied. Insertion of the nop instructions changes the timing of instructions so that the pipeline hazard is avoided. The assembler automatically determines these situations for you.
Some assembly-language instructions are actually macros that result in the generation of more than one instruction. The sne instruction, which is used here, is an example of such a macro.
The last __asm statement, which takes the expression &overflow as an argument, generates an additional instruction that loads this expression into an argument register.
.text
.ent my_mult
.globl my_mult
# Begin code for function: my_mult
my_mult:
.set noreorder
00000 fff8 27bd addiu sp,sp,0xFFF8
00004 .frame sp, 8, RA
00004 .mask 0x00000000, 0x00000000
00004 .fmask 0x00000000, 0x00000000
00004 .prologue 0
$M951:
00004 1025 00c0 or v0,a2,zero
00008 0010 afa2 sw v0,c$(sp)
0000c 1025 00a0 or v0,a1,zero
00010 000c afa2 sw v0,b$(sp)
00014 1025 0080 or v0,a0,zero
00018 0008 afa2 sw v0,a$(sp)
0001c 0018 0085 mult a0,a1 // $4 * $5; these are argument registers
00020 2012 0000 mflo a0 // $4 = lower 32 bits of result
00024 1025 0080 or v0,a0,zero // Store $r4 into *c
00028 0000 acc2 sw v0,0(a2) // Store $r4 into *c
0002c 5810 0000 mfhi t3 // $11 = upper 32 bits of result
00030 67c3 0004 sra t4,a0,0x1F // Shift sign-bit into all lower bits
00034 1023 018b subu v0,t4,t3 // sne (Set if Not-Equal)
00038 0001 2c42 sltiu v0,v0,1 // sne; overflow if the upper 32 bits
0003c 0001 2403 addiu v1,zero,1 // sne; are not the same as bit-31.
00040 6023 0062 subu t4,v1,v0 // sne
00044 0000 27a4 addiu a0,sp,overflow$ // overflow = t4
00048 0000 ac8c sw t4,0(a0) // overflow = t4
0004c 0000 8fa2 lw v0,overflow$(sp) // return overflow;
00050 0004 afa2 sw v0,$T950(sp) // return overflow;
00054 0004 8fa2 lw v0,$T950(sp) // return overflow;
00058 0008 27bd addiu sp,sp,8
0005c 0008 03e0 jr ra
00060 0000 0000 nop
$M952:
.end
# End code for function: my_mult
See Also
MIPS Device Inline Assembly Language | Intrinsic Functions and MIPS Inline Assembly | MIPS Assembly Language Resources | The _asm Keyword in MIPS Inline Assembly
Last updated on Thursday, April 08, 2004
© 1992-2003 Microsoft Corporation. All rights reserved.