I would like to realize if condition in armv8 NEON inline assembly code.
In armv7 this was possible through checking overflow bit like this:
VMRS r4, FPSCR
BIC r4, r4, #(1<<27)
VMSR FPSCR, r4
vtst.16 d30, d30, d30
vqadd.u16 d30, d30, d30
vmrs r4, FPSCR
tst r4, #(1<<27)
bne label1
But I am not able to achieve this in armv8 equivalent code. It seems that SQADD doesnt affect overflow bit in FPSR or I cannot check it like this. Is it possible or is there better approach how to skip long part of code?
Thank you
The same information is available in Aarch64. You just need to replace:
VMSR r4, FPSCR
VMRS FPSCR, r4
by:
MRS w4, FPSR
MSR FPSR, w4
Related
For this simple piece of code
std::atomic_int i;
void foo() {
i.store(1);
i.store(2);
}
gcc generates the following assembly for ARM:
movw r3, #:lower16:.LANCHOR0
movt r3, #:upper16:.LANCHOR0
dmb ish
mov r1, #1
mov r2, #2
str r1, [r3]
dmb ish
dmb ish ; why is this not eliminated?
str r2, [r3]
dmb ish
bx lr
You may notice that there is a repeated fence generated in the middle, which seems to be superfluous. Is it an issue of gcc's optimizer being not able to catch and eliminate extra fences or am I missing something?
BTW, clang seems to handle adjacent fences.
Yes, it does not, and I have been debating it for a while with various people. For external observer like myself effect is that it treats atomic as volatile, while standard doesn't require it to. I was not able to find a requirement for this in the standard.
However, it might also be a simple case of missing optimization.
For the following function...
uint16_t swap(const uint16_t value)
{
return value << 8 | value >> 8;
}
...why does ARM gcc 6.3.0 with -O2 yield the following assembly?
swap(unsigned short):
lsr r3, r0, #8
orr r0, r3, r0, lsl #8
lsl r0, r0, #16 # shift left
lsr r0, r0, #16 # shift right
bx lr
It appears the compiler is using two shifts to mask off the unwanted bytes, instead of using a logical AND. Could the compiler instead use and r0, r0, #4294901760?
Older ARM assembly cannot create constants easily. Instead, they are loaded into literal pools and then read in via a memory load. This and you suggest can only take I believe an 8-bit literal with shift. Your 0xFFFF0000 requires 16-bits to do as 1 instructions.
So, we can load from memory and do an and (slow),
Take 2 instructions to create the value and 1 to and (longer),
or just shift twice cheaply and call it good.
The compiler chose the shifts and honestly, it is plenty fast.
Now for a reality check:
Worrying about a single shift, unless this is a 100% for sure bottleneck is a waste of time. Even if the compiler was sub-optimal, you will almost never feel it. Worry about "hot" loops in code instead for micro-ops like this. Looking at this from curiosity is awesome. Worrying about this exact code for performance in your app, not so much.
Edit:
It has been noted by others here that newer versions of the ARM specifications allow this sort of thing to be done more efficiently. This shows that it is important, when talking at this level, to specify the Chip or at least the exact ARM spec we are dealing with. I was assuming ancient ARM from the lack of "newer" instructions given from your output. If we are tracking compiler bugs, then this assumption may not hold and knowing the specification is even more important. For a swap like this, there are indeed simpler instructions to handle this in later versions.
Edit 2
One thing that could be done to possibly make this faster is to make it inline'd. In that case, the compiler could interleave these operations with other work. Depending on the CPU, this could double the throughput here as many ARM CPUs have 2 integer instruction pipelines. Spread out the instructions enough so that there are no hazards, and away it goes. This has to be weighed against I-Cache usage, but in a case where it mattered, you could see something better.
There is a missed-optimization here, but and isn't the missing piece. Generating a 16-bit constant isn't cheap. For a loop, yes it would be a win to generate a constant outside the loop and use just and inside the loop. (TODO: call swap in a loop over an array and see what kind of code we get.)
For an out-of-order CPU, it could also be worth using multiple instructions off the critical path to build a constant, then you only have one AND on the critical path instead of two shifts. But that's probably rare, and not what gcc chooses.
AFAICT (from looking at compiler output for simple functions), the ARM calling convention guarantees there's no high garbage in input registers, and doesn't allow leaving high garbage in return values. i.e. on input, it can assume that the upper 16 bits of r0 are all zero, but must leave them zero on return. The value << 8 left shift is thus a problem, but the value >> 8 isn't (it doesn't have to worry about shifting garbage down into the low 16).
(Note that x86 calling conventions aren't like this: return values are allowed to have high garbage. (Maybe because the caller can simply use the 16-bit or 8-bit partial register). So are input values, except as an undocumented part of the x86-64 System V ABI: clang depends on input values being sign/zero extended to 32-bit. GCC provides this when calling, but doesn't assume as a callee.)
ARMv6 has a rev16 instruction which byte-swaps the two 16-bit halves of a register. If the upper 16 bits are already zeroed, they don't need to be re-zeroed, so gcc -march=armv6 should compile the function to just rev16. But in fact it emits a uxth to extract and zero-extend the low half-word. (i.e. exactly the same thing as and with 0x0000FFFF, but without needing a large constant). I believe this is pure missed optimization; presumably gcc's rotate idiom, or its internal definition for using rev16 that way, doesn't include enough info to let it realize the top half stays zeroed.
swap: ## gcc6.3 -O3 -march=armv6 -marm
rev16 r0, r0
uxth r0, r0 # not needed
bx lr
For ARM pre v6, a shorter sequence is possible. GCC only finds it if we hand-hold it towards the asm we want:
// better on pre-v6, worse on ARMv6 (defeats rev16 optimization)
uint16_t swap_prev6(const uint16_t value)
{
uint32_t high = value;
high <<= 24; // knock off the high bits
high >>= 16; // and place the low8 where we want it
uint8_t low = value >> 8;
return high | low;
//return value << 8 | value >> 8;
}
swap_prev6: # gcc6.3 -O3 -marm. (Or armv7 -mthumb for thumb2)
lsl r3, r0, #24
lsr r3, r3, #16
orr r0, r3, r0, lsr #8
bx lr
But this defeats the gcc's rotate-idiom recognition, so it compiles to this same code even with -march=armv6 when the simple version compiles to rev16 / uxth.
All source + asm on the Godbolt compiler explorer
ARM is a RISC machine (Advanced RISC Machine), and thus, all instrutcions are encoded in the same size, capping at 32bit.
Immediate values in instructions are assigned to a certain number of bits, and AND instruction simply doesn't have enought bits assigned to immediate values to express any 16bit value.
That's the reason for the compiler resorting to two shift instructions instead.
However, if your target CPU is ARMv6 (ARM11) or higher, the compiler takes leverage from the new REV16 instruction, and then masks the lower 16bit by UXTH instruction which is unnecessary and stupid, but there is simply no conventional way to persuade the compiler not to do this.
If you think that you would be served well by GCC intrinsic __builtin_bswap16, you are dead wrong.
uint16_t swap(const uint16_t value)
{
return __builtin_bswap16(value);
}
The function above generates exactly the same machine code that your original C code did.
Even using inline assembly doesn't help either
uint16_t swap(const uint16_t value)
{
uint16_t result;
__asm__ __volatile__ ("rev16 %[out], %[in]" : [out] "=r" (result) : [in] "r" (value));
return result;
}
Again, exactly the same. You cannot get rid of the pesky UXTH as long as you use GCC; It simply cannot read from the context that the upper 16bits are all zeros to start with and thus, UXTH is unnecessary.
Write the whole function in assembly; That's the only option.
This is the optimal solution, the AND would require at least two more instructions possibly having to stop and wait for a load to happen of the value to mask. So worse in a couple of ways.
00000000 <swap>:
0: e1a03420 lsr r3, r0, #8
4: e1830400 orr r0, r3, r0, lsl #8
8: e1a00800 lsl r0, r0, #16
c: e1a00820 lsr r0, r0, #16
10: e12fff1e bx lr
00000000 <swap>:
0: ba40 rev16 r0, r0
2: b280 uxth r0, r0
4: 4770 bx lr
The latter is armv7 but at the same time it is because they added instructions to support this kind of work.
Fixed length RISC instructions have by definition a problem with constants. MIPS chose one way, ARM chose another. Constants are a problem on CISC as well just a different problem. Not difficult to create something that takes advantage of ARMS barrel shifter and shows a disadvantage of MIPS solution and vice versa.
The solution actually has a bit of elegance to it.
Part of this as well is the overall design of the target.
unsigned short fun ( unsigned short x )
{
return(x+1);
}
0000000000000010 <fun>:
10: 8d 47 01 lea 0x1(%rdi),%eax
13: c3 retq
gcc chooses not to return the 16 bit variable you asked for it returns a 32 bit, it doesnt properly/correctly implement the function I asked for with my code. But that is okay if when the user of the data gets that result or uses it the mask happens there or with this architecture ax is used instead of eax. for example.
unsigned short fun ( unsigned short x )
{
return(x+1);
}
unsigned int fun2 ( unsigned short x )
{
return(fun(x));
}
0000000000000010 <fun>:
10: 8d 47 01 lea 0x1(%rdi),%eax
13: c3 retq
0000000000000020 <fun2>:
20: 8d 47 01 lea 0x1(%rdi),%eax
23: 0f b7 c0 movzwl %ax,%eax
26: c3 retq
A compiler design choice (likely based on architecture) not an implementation bug.
Note that for a sufficiently sized project, it is easy to find missed optimization opportunities. No reason to expect an optimizer to be perfect (it isnt and cant be). They just need to be more efficient than a human doing it by hand for that sized project on average.
This is why it is commonly said that for performance tuning you dont pre-optimize or just jump to asm immediately you use the high level language and the compiler you in some way profile your way through to find the performance problems then hand code those, why hand code them because we know we can at times out perform the compiler, implying the compiler output can be improved upon.
This isnt a missed optimization opportunity, this is instead a very elegant solution for the instruction set. Masking a byte is simpler
unsigned char fun ( unsigned char x )
{
return((x<<4)|(x>>4));
}
00000000 <fun>:
0: e1a03220 lsr r3, r0, #4
4: e1830200 orr r0, r3, r0, lsl #4
8: e20000ff and r0, r0, #255 ; 0xff
c: e12fff1e bx lr
00000000 <fun>:
0: e1a03220 lsr r3, r0, #4
4: e1830200 orr r0, r3, r0, lsl #4
8: e6ef0070 uxtb r0, r0
c: e12fff1e bx lr
the latter being armv7, but with armv7 they recognized and solved these issues you cant expect the programmer to always use natural sized variables, some feel the need to use less optimal sized variables. sometimes you still have to mask to a certain size.
I'm having a bit of trouble understanding the rlwinm PPC Assembly instruction (Rotate Left Word Immediate Then AND with Mask).
I am trying to reverse this part of a function
rlwinm r3, r3, 0, 28, 28
I already know what r3 is. r3 in this case is a 4 byte integer but I am not sure exactly what this instruction rlwinm is doing to it.
By the way, this is on a 32 bit machine.
Your understanding is not quite right. As per the IBM link on this instruction, the form you're seeing is:
rlwinm <target=r3>, <source=r3>, <shift=0>, <begin-mask=28>, <end-mask=28>
Hence no actual shift is involved. And the actual mask used for the AND operation is constructed from the begin and end mask positions, it's not given as an explicit argument(a).
In this case, since both positions are 28, the mask is simply a single bit, as per the linked page (slightly paraphrased):
If the begin-mask value is less than the end-mask value plus one, then the mask bits between and including the starting point and the end point are set to ones. All other bits are set to zeros.
So the instruction you're seeing is nothing more complicated than a single AND operation.
(a) There is a form that allows you to specify the actual mask (assuming it consists of contiguous one-bits) but it's the four-argument version and really just syntactic sugar that the assembler can turn into the five-argument one.
#paxdiablo's answer is a correct, but to add some more context:
The various r* instructions (rlwinm, rlwimi, etc) are designed for extracting bit fields whose size is known at compile time, for example C struct bitfields, or even just splitting a word into bytes (which may be faster with one lw and four rlwinm instructions than several separate lbzus).
lw r4, r3 ; load the word at the address pointed at by r3
rlwinm r5, r4, 8, 24, 31 ; first byte in r5
rlwinm r6, r4, 16, 24, 31 ; second byte in r6
rlwinm r7, r4, 24, 24, 31 ; third byte in r7
rlwinm r8, r4, 0, 24, 31 ; fourth byte in r8, identical to andi r8, r4, 255
The rlwinm instructions can also be used, as in this case, as a special form of andi for contiguous sets of bits. Since instructions in PowerPC are always 32 bits, instructions taking immediate values have only 16 bits to hold those values - so if you want to mask a set of bits that crosses the high/low half-word boundary, say 23 to 8, you need to use multiple operations.
lis r4, r4, 0x00ff ; first set bits 23 to 16 of the mask
ori r4, r4, 0xff00 ; then bits bits 15 to 8
and r3, r3, r4 ; then perform the actual masking
However, with the rlwinm instructions, we can perform that same operation in a single instruction:
rlwinm r3, r3, 0, 8, 23
In your case, the value probably holds flags for something and this instruction is extracting one of them. The next instruction is probably a conditional branch on r3.
ETA: Peter Cordes corrected out some of my mistakes, for which I am grateful, and added that it was probably unnecessary to use rlwinm in this case, and that it may simply be a peculiarity of the compiler that causes this instruction to be generated instead of andi.
I just setup a raspberry pi machine and tried reverse engineering the following piece of code.
#include<stdio.h>
int main() {
printf("this is a test\n");
}
For the most part the following disassembly in gdb seemed to make sense.
0x000083c8 <+0>: push {r11, lr}
0x000083cc <+4>: add r11, sp, #4
0x000083d0 <+8>: ldr r0, [pc, #8] ; 0x83e0 <main+24>
0x000083d4 <+12>: bl 0x82ec <puts>
0x000083d8 <+16>: mov r0, r3
0x000083dc <+20>: pop {r11, pc}
0x000083e0 <+24>: andeq r8, r0, r4, asr r4
However, I fail to understand why the instruction at 0x000083e0 exists. Is that instruction even a part of the main function? Wouldn't the value that was pushed in at 0x000083c8 be popped out into pc, immediately transferring control over to some other location?
Also I tried setting a breakpoint at 0x000083e0 -- I seem to be getting a very strange SEGFAULT. Why would that be?
When this function is called (i.e. when execution begins at instruction 0x000083c8), the link register (LR) should already contain the return address. Fast-forward to 0x000083d8: the puts function's return result is placed in R0 in accordance with the ARM C calling convention (link, link). Then, the return address is popped from the stack into the PC - effectively ending execution of this function. This implies that the instruction at 0x000083e0 is not a part of your program, and your inspection should be limited to instructions 0x000083c8 through 0x000083dc.
So to answer your questions:
Correct.
The "instruction" at 0x000083e0 is essentially junk. You may not even have execution and/or access privileges to this memory depending on the specifics of your ARM core (Does it have an MMU, etc?). Thus, a seg fault is a reasonable outcome when attempting to inspect that location.
EDIT: in agreement with comments below, the contents of 0x000083e0 should be interpreted as data, not instructions.
Four bytes at 0x000083e0 isn't junk. It is part of the PC relative load at
0x000083d0 <+8>: ldr r0, [pc, #8] ; 0x83e0 <main+24>
It is also visible in the comment as ; 0x83e0 <main+24>.
Problem here since you need to pass address of a string to puts, whose address might change during linking step, compiler needs to create suitable code for such further processing. Thus address of string ends up in instruction stream yet outside of any execution context.
I was debugging some c++ code (WinCE 6 on ARM platform),
and i find some behavior strange:
4277220C mov r3, #0x93, 30
42772210 str r3, [sp]
42772214 ldr r3, [pc, #0x69C]
42772218 ldr r2, [pc, #0x694]
4277221C mov r1, #0
42772220 ldr r0, [pc, #0x688]
Line 42772214 ldr r3, [pc, #0x69C] is used to get some constant from .DATA section, at least I think so.
What is strange that according to the code r2 should be filled with memory from address pc=0x42772214 + 0x69C = 0x427728B0, but according to the memory contents it's loaded from 0x427728B8 (8bytes+), it happens for other ldr usages too.
Is it fault of the debugger or my understanding of ldr/pc?
Another issue I don't get - why access to the .data section is relative to the executed code? I find it little bit strange.
And one more issue: i cannot find syntax of the 1st mov command (any one could point me a optype specification for the Thumb (1C2))
Sorry for the laic description, but I'm just familiarizing with the assemblies.
This is correct. When pc is used for reading there is an 8-byte offset in ARM mode and 4-byte offset in Thumb mode.
From the ARM-ARM:
When an instruction reads the PC, the value read depends on which instruction set it comes from:
For an ARM instruction, the value read is the address of the instruction plus 8 bytes. Bits [1:0] of this value are always zero, because ARM instructions are always word-aligned.
For a Thumb instruction, the value read is the address of the instruction plus 4 bytes. Bit [0] of this value is always zero, because Thumb instructions are always halfword-aligned.
This way of reading the PC is primarily used for quick, position-independent addressing of nearby instructions and data, including position-independent branching within a program.
There are 2 reasons for pc-relative addressing.
Position-independent code, which is in your case.
Get some complicated constants nearby which cannot be written in 1 simple instruction, e.g. mov r3, #0x12345678 is impossible to complete in 1 instruction, so the compiler may put this constant in the end of the function and use e.g. ldr r3, [pc, #0x50] to load it instead.
I don't know what mov r3, #0x93, 30 means. Probably it is mov r3, #0x93, rol 30 (which gives 0xC0000024)?