Bit Operations
- Basic bit operation instructions
The following table lists the mapping between common bit operation functions and instruction types. The derivative instructions of a specific function type are not described in this document. For details, see the Armv8 instruction manual.
Function
Instruction/Instruction Family
And
AND
Or
ORR
Negation
NEG
Exclusive or
EOR
Logic shift left
LSL
Logic shift right
LSR
- Alignment judgment and processing
As we mentioned in section 3, assembly code involves frequent memory operations. Compared with HLL, assembly code, especially the string series, focuses more on memory address alignment. The general principle of memory addressing is that only some types of addresses can be directly accessed. Other addresses can be accessed by adding offsets to the addresses that can be directly accessed. Therefore, for memory operations, directly operating the memory with aligned addresses is better than operating the memory with unaligned addresses.
The implementation of address alignment is not complex. The key is to perform bit operations on the memory addresses. You can use the BIC instruction. However, after an address is aligned, you need to adjust related parameters to ensure that the logic is correct. In this case, the AND instruction needs to be used to calculate and save the number of bytes to be offset for address alignment. In addition, the effect of aligning addresses by using the SUB instruction is the same as that of the BIC instruction, as shown in the following code.
/* Assume the src is the addr to be aligned */ ... ... and tmp, src, 15 bic src, src, 15 (sub src, src, tmp) add count, count, tmp sub dst, dst, tmp ... ...
- Use of magic numbers
In the source code of string functions, some specially designed magic numbers involve advanced bit operations. These magic numbers and corresponding bit operations can also be used to implement some powerful functions. For example, strcpy uses magic numbers to determine whether each byte is a null terminator.
#define REP8_01 0x0101010101010101 #define REP8_7f 0x7f7f7f7f7f7f7f7f ... ... ldp data1, data2, [src], #16 sub tmp1, data1, #REP8_01 orr tmp2, data1, #REP8_7f sub tmp3, data2, #REP8_01 orr tmp4, data2, #REP8_7f bic has_nul1, tmp1, tmp2 bics has_nul2, tmp3, tmp4 ccmp has_nul1, #0, #0, eq /* NZCV = 0000 */
In the assembly code in strcpy.S, the magic numbers REP8_01 and REP8_7f are set, and the bit operation expression ((X - 1) & ~(X | 0x7f)) is used to determine whether each byte is a null terminator (0x00). The expression implementation is not described here. You can assume that a string of ASCII values is used in the expression for calculation. You will find that different calculation results are displayed only when the value is 0x00.
Another magic number appears in the implementation of memchr.S. By defining the magic number 0x40100401 and related bit calculation operations, the function of finding characters with the same ASCII value is implemented.