What's faster on ARM? MUL or (SHIFT + SUB)? - c

Which is faster on ARM?
*p++ = (*p >> 7) * 255;
or
*p++ = ((*p >> 7) << 8) - 1
Essentially what I'm doing here is taking an 8-bit word and setting it to 255 if >= 128, and 0 otherwise.

If p is char below statement is just an assignment to 255.
*p++ = ((*p >> 7) << 8) - 1
If p is int, then of course it is a different story.
You can use GCC Explorer to see how the assembly output looks like. Below is appearently what you get from Linaro's arm-linux-gnueabi-g++ 4.6.3 with -O2 -march=armv7-a flags;
void test(char *p) {
*p++ = (*p >> 7) * 255;
}
void test2(char *p) {
*p++ = ((*p >> 7) << 8) - 1 ;
}
void test2_i(int *p) {
*p++ = ((*p >> 7) << 8) - 1 ;
}
void test3(char *p) {
*p++ = *p >= 128 ? ~0 : 0;
}
void test4(char *p) {
*p++ = *p & 0x80 ? ~0 : 0;
}
creates
test(char*):
ldrb r3, [r0, #0] # zero_extendqisi2
sbfx r3, r3, #7, #1
strb r3, [r0, #0]
bx lr
test2(char*):
movs r3, #255
strb r3, [r0, #0]
bx lr
test2_i(int*):
ldr r3, [r0, #0]
asrs r3, r3, #7
lsls r3, r3, #8
subs r3, r3, #1
str r3, [r0, #0]
bx lr
test3(char*):
ldrsb r3, [r0, #0]
cmp r3, #0
ite lt
movlt r3, #255
movge r3, #0
strb r3, [r0, #0]
bx lr
test4(char*):
ldrsb r3, [r0, #0]
cmp r3, #0
ite lt
movlt r3, #255
movge r3, #0
strb r3, [r0, #0]
bx lr
If you are not nitpicking best is always to check assembly of the generated code over such details. People tend to overestimate compilers, I agree most of the time they do great but I guess it is anyone's right to question generated code.
You should also be careful interpreting instructions, since they won't always match into cycle accurate listing due to core's architectural featuers like having out-of-order, super scalar deep pipelines. So it might not be always shortest sequence of instructions win.

Well, to answer the question in your title, on ARM, a SHIFT+SUB can be done in a single instruction with 1 cycle latenency, while a MUL usually has multiple cycle latency. So the shift will usually be faster.
To answer the implied question of what C code to write for this, generally you are best off with the simplest code that expresses your intent:
*p++ = *p >= 128 ? ~0 : 0; // set byte to all ones iff >= 128
or
*p++ = *p & 0x80 ? ~0 : 0; // set byte to all ones based on the MSB
this will generally get converted by the compiler into the fastest way of doing it, whether that is a shift and whatever, or a conditional move.

Despite the fact that your compiler can optimize the line quite well (and reading the assembly will tell you what is really done), you can refer from this page to know exactly how much cycles a MUL can take.

Related

Data dependency detection bug with arm-none-eabi-gcc and -Ofast

I am writing a program for an Arm Cortex M7 based microcontroller, where I am facing an issue with (I guess) compiler optimization.
I compile using arm-none-eabi-gcc v10.2.1 with -mcpu=cortex-m7 -std=gnu11 -Ofast -mthumb.
I extracted the relevant parts, they are as below. I copy some data from a global variable into a local one, and from there the data is written to a hardware
CRC engine to compute the CRC over the data. Before writing it to the engine, the byte order is to be reversed.
#include <stddef.h>
#include <stdint.h>
#define MEMORY_BARRIER asm volatile("" : : : "memory")
#define __REV(x) __builtin_bswap32(x) // Reverse byte order
// Dummy struct, similiar as it is used in our program
typedef struct {
float n1, n2, n3, dn1, dn2, dn3;
} test_struct_t;
// CRC Engine Registers
typedef struct {
volatile uint32_t DR; // CRC Data register Address offset: 0x00
} CRC_TypeDef;
#define CRC ((CRC_TypeDef*) (0x40000000UL))
// Write data to CRC engine
static inline void CRC_write(uint32_t* data, size_t size)
{
for(int index = 0; index < (size / 4); index++) {
CRC->DR = __REV(*(data + index));
}
}
// Global variable holding some data, similiar as it is used in our program
volatile test_struct_t global_var = { 0 };
int main()
{
test_struct_t local_tmp;
MEMORY_BARRIER;
// Copy the current data into a local variable
local_tmp = global_var;
// Now, write the data to the CRC engine to compute CRC over the data
CRC_write((uint32_t*) &local_tmp, sizeof(local_tmp));
MEMORY_BARRIER;
return 0;
}
This compiles to:
main:
push {r4, r5, lr}
sub sp, sp, #28
ldr r4, .L4
mov ip, sp
ldr r3, [sp] ; load first float into r3
mov lr, #1073741824 ; load address of CRC engine DR register into lr
rev r5, r3 ; reverse byte order of first float and store in r5
ldmia r4!, {r0, r1, r2, r3} ; load first 4 floats into r0-r3
stmia ip!, {r0, r1, r2, r3} ; copy first 4 floats into local variable
ldm r4, {r0, r1} ; load float 5 and 6 into r0 and r1
stm ip, {r0, r1} ; copy float 5 and 6 into local variable
str r5, [lr] ; write reversed bytes from r5 to CRC engine
ldr r3, [sp, #4]
rev r3, r3
str r3, [lr]
ldr r3, [sp, #8]
rev r3, r3
str r3, [lr]
ldr r3, [sp, #12]
rev r3, r3
str r3, [lr]
ldr r3, [sp, #16]
rev r3, r3
str r3, [lr]
ldr r3, [sp, #20]
rev r3, r3
str r3, [lr]
movs r0, #0
add sp, sp, #28
pop {r4, r5, pc}
.L4:
.word .LANCHOR0
global_var:
The problem is now that the first float is loaded and its byte order is reversed (rev r5, r3) before the data has been copied from the global variable (happens with the ldmia/sdmia).
This is clearly not intended, leads to a wrong CRC, and I wonder why the compiler does not recognize this data dependency. Am I doing anything wrong or is there a bug in GCC?
Code in compiler explorer: https://godbolt.org/z/ErWThc5dx

Trying to animate a sprite using a simple timer and bitwise & checks

I'm attempting to make a simple Game Boy Advance game that has a sprite with four frames of animation.
I'm new to C programming (I'm an assembly hobbyist, ARM assembly makes more sense to me but I gave up because of its limited ability to read and move data into its registers) and I'm running into difficulty understanding how to do things.
The code below was supposed to increment the variable i each frame, and cycle through one of four possible graphics for the player sprite. (that's what the 0x20, 0x28, 0x20, and 0x30 represent). But when the game runs it only displays the sprite as if I had just set the sprite attribute to 0x30 and never changes.
Basically my if statements have no effect and I don't really understand why.
C code:
ObjectAttributes *spriteAttribs = &oam_object_backbuffer[0];
spriteAttribs->attr0 = 0x2000;
spriteAttribs->attr1 = 0x4000;
spriteAttribs->attr2 = 0x20;
int i;
i = 0;
while(1)
{
i = i++;
vsync();
if (i & 3 == 0) //this doesn't work, no idea why
{
spriteAttribs->attr2 = 0x20;
}
else if (i & 3 == 1)
{
spriteAttribs->attr2 = 0x28;
}
else if (i & 3 == 2)
{
spriteAttribs->attr2 = 0x20;
}
else
{
spriteAttribs->attr2 = 0x30;
}
MEM_OAM[0] = oam_object_backbuffer[0];
// this is what displays the sprite to the screen
key_poll() // not relevant to this question
} //loop forever
This produces the following ARM code:
L21:
sub r1, fp, #20
ldr r3, [r1, #0]
mov r2, r3
str r2, [fp, #-20]
add r3, r3, #1
str r3, [r1, #0]
bl vsync
ldr r2, [fp, #-1068]
mov r3, #48
strh r3, [r2, #4] # movhi
mov r2, #117440512 # 0x07000000, address of Game Boy Advance's OAM
mvn r3, #1040 # By the way is there any way to tell GCC to output these values in
# hexadecimal?
sub r3, r3, #7
sub r0, fp, #12
add r3, r0, r3
ldmia r3, {r0-r1}
stmia r2, {r0-r1}
bl key_poll
b .L21

How to do the if or in assembly?

I am trying to translate the following code to assembly!
int i = 1
int a = 3
int b;
if(i == 1 || a == 3)
b = 95;
else
b = 0;
I am confused about the part where I have to use or in the if statement. Do you guys have any suggestions?
ldr r0, [r13, #0] //i = 1
ldr r1, [r13, #4] //a = 3
mov r2, #1 //put 1 in r2
mov r3, #3 //put 3 in r3
cmp r0, r2 //compare i and 1
orr r1, r3 //or a and 3
bgt else //if false branch to else
ldr r4 #0 // put 0 in r4
str r4, [r13, #8] //store it at location 208 with r13
b endif //branch to else if if true
ldr r5 #95 //put 95 on r5
str r5 [r13, #12] //store 95 on location 212 with r13
So far I have this!
Honestly looks wrong! So you can roast me I am here to learn so please teach me! :)
I don't recognize the assembly language. But the pseudo-code would be:
compare i with 1
if true, jump to if
compare a with 3
if true, jump to if
else:
store 0 in b
jump to endif
if:
store 95 in b
endif:
This also implements the short-circuiting of ||, since a == 3 is only tested if i == 1 fails.
Writing assembly code by hand will quickly get out of hand and become goto spaghetti and a modern compiler does a better job optimizing, with that said sometimes you want to write a few lines of assembler for some other reason.
I don't think you must load the constants into registers first and the cost of assigning a value to a register is low compared to conditional branching.
My approach here would be
Store the value of one of the braces (95) in one register.
Compare a with 1 and 3 and branch if equal
Overwrite the register with 0 if branch was not taken
Store the contents of the register in area of variable b
begin:
mov r5, #95 // The value
ldr r0, [r13, #0] //i = 1
ldr r1, [r13, #4] //a = 3
cmp r0, #1 //compare i and 1
beq else
cmp r1,#3 // compare a and 3
beq else //if false branch to else
mov r5,#0 // Clear r5
else:
str r5, [r13, #8] //store it at location 208 with r13
Edit: I Found this cheat sheet It looks like there are conditional variants of the mov instruction. Then the code could be written like this: Without any jumps at all.
begin:
mov r5, #95 // The value
mov r6,#0 // Clear r6
ldr r0, [r13, #0] // i = 1
ldr r1, [r13, #4] // a = 3
cmp r0, #1 // compare i and 1
moveq r5,r6 // conditional move
cmp r1,#3 // compare a and 3
moveq r5,r6
str r5, [r13, #8] // store it at location 208 with r13
If i equals 1 then branch to code that assigns 95 to b. Otherwise, if a is 3, branch to that same code. Otherwise, assign 0 to b and branch to just after the other assignment.
I find the comments in another answer interesting and disturbing at the same time. And more interesting that that answer did not simply ask a compiler.
int fun ( int i, int a )
{
int b;
if(i == 1 || a == 3)
b = 95;
else
b = 0;
return b;
}
00000000 <fun>:
0: e3510003 cmp r1, #3
4: 13500001 cmpne r0, #1
8: 03a0005f moveq r0, #95 ; 0x5f
c: 13a00000 movne r0, #0
10: e12fff1e bx lr
so that means
ldr r0, [sp, #0] //i
ldr r1, [sp, #4] //a
cmp r0, #1 //compare i with 1, interested in equal or not
cmpne r1, #3 //if not equal then test a with 3, interested in equal or not
moveq r5, #95 //if either of the two were equal set b = 95
movne r5, #0 //if neither of the two were equal set b = 0
which is this machine code
0: e59d0000 ldr r0, [sp]
4: e59d1004 ldr r1, [sp, #4]
8: e3500001 cmp r0, #1
c: 13510003 cmpne r1, #3
10: 03a0505f moveq r5, #95 ; 0x5f
14: 13a05000 movne r5, #0
As shown in the ARM documentation, start with the ARM Architectural Reference Manual for ARMv5 to get your feet wet with the basic 32 bit ARM instructions (and base (all thumb variants) thumb instructions). Notice in that documentation that the first nibble describes the condition code and all instructions can be conditionally executed (to avoid branches for if-then-else type things).
0: e3a0505f mov r5, #95 ; 0x5f
4: 03a0505f moveq r5, #95 ; 0x5f
8: 13a0505f movne r5, #95 ; 0x5f
c: e3500001 cmp r0, #1
10: 03500001 cmpeq r0, #1
14: 13500001 cmpne r0, #1
18: c3500001 cmpgt r0, #1
1c: b3500001 cmplt r0, #1
See how the first 4 bits change but the other 28 do not? A feature you see in ARM instruction sets specifically and not necessarily in others. Some others have similar features though.
Not heard of a32 instruction set, so it is not clear which of the handful or more of the arm instruction sets you are using. The above works on armv4t through armv7-a. But tell a modern compiler to build for armv7-a it is likely going to build thumb first then arm only if you can force it. See the ARM Architectural Reference Manual for armv7-ar (it also shows all the way back to armv4t each instruction indicating which architectures are supported).
This is arm code as well that runs on some arm processors:
0: 2903 cmp r1, #3 compare a with 3
2: bf18 it ne these two
4: 2801 cmpne r0, #1 do an if not equal then compare i with 1
6: bf0c ite eq these three do a
8: 205f moveq r0, #95 ; 0x5f if either are equal b = 95
a: 2000 movne r0, #0 else b = 0
c: 4770 bx lr
e: bf00 nop
(just to show that it matters very much which specific instruction set a question is asking about and for ARM which of the ARM instruction sets)
You are basically wanting to do a
if i == 1 set the z flag
else if a == 3 set the z flag
if the z flag is set (from either of the above) b = 95
else b = 0
There are many basic ways to do this and Simson's answer is a clean straightforward approach that saves a branch or two.
mov r5,#95
ldr r0, [sp, #0] //i
ldr r1, [sp, #4] //a
// if i == 1
cmp r0,#1
bne skip
// or if a == 3
cmp r1,#3
bne skip
// else
mov r5,#0 //neither were equal
skip:
str r5, [r13, #12]
I was focused on that answer, but looking at yours did you mean to place the result in two different places based on the result?
ldr r4 #0 // put 0 in r4
str r4, [r13, #8] //store it at location 208 with r13
ldr r5 #95 //put 95 on r5
str r5 [r13, #12] //store 95 on location 212 with r13
That breaks Simson's answer. And mine above.
Most folks would start with this, easy to read and follow, brute force straight from the high level code.
ldr r0, [sp, #0] //i
ldr r1, [sp, #4] //a
// if i == 1
check_i:
cmp r0,#1
bne check_a
b one_equal //folks will forget to do this one
check_a:
// or if a == 3
cmp r1,#3
beq one_equal
bne neither_equal //or just fall through
// else
neither_equal:
mov r4,#0
str r4, [r13, #8]
b the_end //many folks forget this branch
one_equal:
mov r5,#95
str r5, [r13, #12]
the_end:
Or something like it which can then be shortened slightly into this, some folks would start with something like this:
ldr r0, [sp, #0] //i
ldr r1, [sp, #4] //a
// if i == 1
cmp r0,#1
beq one_equal
// or if a == 3
cmp r1,#3
beq one_equal
// else
neither_equal:
mov r4,#0
str r4, [r13, #8]
b the_end //many folks forget this one
one_equal:
mov r5,#95
str r5, [r13, #12]
the_end:
Here is where you start to go off the rails
cmp r0, r2 //this is a valid starting point
orr r1, r3 //orr is a logical or, not an if this "or" that
// so we are confused by what you are doing here
bgt else //you are wanting to know if it is equal or not, not if greater
// than
It does not get any better after that
If you really meant the result in two different places then:
Still get the variables into registers from the stack
ldr r0, [sp, #0] //i
ldr r1, [sp, #4] //a
This still does an if this is equal or that is equal
cmp r0, #1 //is i == 1?
cmpne r1, #3 //if not then is a == 3?
You end up here with z set if either one is equal or z clear if neither are equal
moveq r4,#95 //one or the other is equal
streq r4,[r13, #8] //one or the other is equal
movne r5,#0 //neither are equal
strne r5,[r13, #12] //neither are equal
Final result:
ldr r0, [sp, #0] //i
ldr r1, [sp, #4] //a
cmp r0, #1 //is i == 1?
cmpne r1, #3 //if not then is a == 3
moveq r4,#95 //one or the other is equal
streq r4,[r13, #8] //one or the other is equal
movne r5,#0 //neither are equal
strne r5,[r13, #12] //neither are equal
It assembles fine, so the syntax is good
0: e59d0000 ldr r0, [sp]
4: e59d1004 ldr r1, [sp, #4]
8: e3500001 cmp r0, #1
c: 13510003 cmpne r1, #3
10: 03a0405f moveq r4, #95 ; 0x5f
14: 058d4008 streq r4, [sp, #8]
18: 13a05000 movne r5, #0
1c: 158d500c strne r5, [sp, #12]
I have edited this so many times I hope I did not leave any mistakes...I will get beat up for it if I did I am sure...Before doing any assembly language you need the proper documentation. In this case you want one of the ARM Architectural Reference Manuals, likely the oldest one which is directly derived from the printed versions before they distributed pdfs. The armv5 manual.
In general you will see a compiler will do the opposite and jump over
if(x==1)
{
y = 5;
}
cmp r0,#1
bne skip //C code is equal so branch if not
mov r1,#5
skip:
If you had if ((i==1)&&(a==3)) you would also want to look at the opposite, skip over if (i!=1) skip over if (a!=3) having the two paths skip to a common label.
But in the case of an this OR that you kind of want to have two paths land in the same place by branching to a common label and then have it fall through to the else code if neither are true. By doing the as written comparison if i == 1 branch to label, of a == 3 branch to label.

Optimization level breaks the C code order

I have the following 5 lines of code and want the this lines are executed in exact this order with O2 or O3:
PORT->Group[GPIO_PORTB].OUTCLR.reg = (volatile uint32_t) 1 << 9;
TC3->COUNT16.COUNT.reg = (volatile uint16_t) 0;
TC3->COUNT16.CC[0].reg = (volatile uint16_t) vusb_driver->in_data->bitlength;
SERCOM0->SPI.DATA.reg = (volatile uint32_t) 0x54;
DMAC->Channel[USB_SEND_SD_DMA_CH].CHCTRLA.reg = (volatile uint8_t) DMAC_CHCTRLA_ENABLE;
If I optimize with O2 or O3 the code breaks at line 264 because this line must be executed before line 265:
261: PORT->Group[GPIO_PORTB].OUTCLR.reg = (volatile uint32_t) 1 << 9;
200001EE ldr r1, [pc, #84]
263: TC3->COUNT16.CC[0].reg = (volatile uint16_t) vusb_driver->in_data->bitlength;
200001F0 ldr r5, [pc, #84]
264: SERCOM0->SPI.DATA.reg = (volatile uint32_t) 0x54;
200001F2 ldr r4, [pc, #88]
265: DMAC->Channel[USB_SEND_SD_DMA_CH].CHCTRLA.reg = (volatile uint8_t) DMAC_CHCTRLA_ENABLE;
200001F4 ldr r0, [pc, #88]
261: PORT->Group[GPIO_PORTB].OUTCLR.reg = (volatile uint32_t) 1 << 9;
200001F6 mov.w r6, #512
200001FA str.w r6, [r1, #148]
262: TC3->COUNT16.COUNT.reg = (volatile uint16_t) 0;
200001FE strh r2, [r3, #20]
263: TC3->COUNT16.CC[0].reg = (volatile uint16_t) vusb_driver->in_data->bitlength;
20000200 ldr r2, [r5]
20000202 ldr r2, [r2, #20]
20000204 ldrh.w r2, [r2, #72]
20000208 strh r2, [r3, #28]
264: SERCOM0->SPI.DATA.reg = (volatile uint32_t) 0x54;
2000020A movs r5, #84
265: DMAC->Channel[USB_SEND_SD_DMA_CH].CHCTRLA.reg = (volatile uint8_t) DMAC_CHCTRLA_ENABLE;
2000020C movs r2, #2
264: SERCOM0->SPI.DATA.reg = (volatile uint32_t) 0x54;
2000020E str r5, [r4, #40]
Your use of volatile is incorrect, you should define the destination objects as volatile to ensure they are written to exactly in the order of the program.
The compiler is - in the regular case - allowed to reorder instructions if the effect of the execution is the same (this is the "as-if rule"). So you must do one of the following:
Indicate to it that the instructions will have a different effect (e.g. by making the relevant .reg variables volatile; or through aliasing of pointers etc.)
Use some sort of compiler-specific directives to control its behavior.
Not compile, i.e. generate your machine code in a different manner.
Specifically, if you choose the first option, you must explain - to the compiler and perhaps to yourself, why is it that
line [264] must be executed before line 265
In what sense "must" it be executed before 265? Who would notice? It's likely that a concrete answer to this question is something you could use to force the desired order of execution.

ARM Cortex M7 unaligned access and memcpy

I am compiling this code for a Cortex M7 using GCC:
// copy manually
void write_test_plain(uint8_t * ptr, uint32_t value)
{
*ptr++ = (u8)(value);
*ptr++ = (u8)(value >> 8);
*ptr++ = (u8)(value >> 16);
*ptr++ = (u8)(value >> 24);
}
// copy using memcpy
void write_test_memcpy(uint8_t * ptr, uint32_t value)
{
void *px = (void*)&value;
memcpy(ptr, px, 4);
}
int main(void)
{
extern uint8_t data[];
extern uint32_t value;
// i added some offsets to data to
// make sure the compiler cannot
// assume it's aligned in memory
write_test_plain(data + 2, value);
__asm volatile("": : :"memory"); // just to split inlined calls
write_test_memcpy(data + 5, value);
... do something with data ...
}
And I get the following Thumb2 assembly with -O2:
// write_test_plain(data + 2, value);
800031c: 2478 movs r4, #120 ; 0x78
800031e: 2056 movs r0, #86 ; 0x56
8000320: 2134 movs r1, #52 ; 0x34
8000322: 2212 movs r2, #18 ; 0x12
8000324: 759c strb r4, [r3, #22]
8000326: 75d8 strb r0, [r3, #23]
8000328: 7619 strb r1, [r3, #24]
800032a: 765a strb r2, [r3, #25]
// write_test_memcpy(data + 5, value);
800032c: 4ac4 ldr r2, [pc, #784] ; (8000640 <main+0x3a0>)
800032e: 923b str r2, [sp, #236] ; 0xec
8000330: 983b ldr r0, [sp, #236] ; 0xec
8000332: f8c3 0019 str.w r0, [r3, #25]
Can someone explain how the memcpy version works? This looks like inlined 32-bit store to the destination address, but isn't this a problem since data + 5 is most certainly not aligned to a 4-byte boundary?
Is this perhaps some optimization which happens due to some undefined behavior in my source?
For Cortex-M processors unaligned loads and stores of bytes, half-words, and words are usually allowed and most compilers use this when generating code unless they are instructed not to. If you want to prevent gcc from assuming the unaligned accesses are OK, you can use the -mno-unaligned-access compiler flag.
If you specify this flag gcc will no longer inline the call to memcpy and write_test_memcpy looks like
write_test_memcpy(unsigned char*, unsigned long):
push {lr}
sub sp, sp, #12
movs r2, #4
add r3, sp, #8
str r1, [r3, #-4]!
mov r1, r3
bl memcpy
add sp, sp, #12
ldr pc, [sp], #4
Cortex-M 7 , M4, M3 M33, M23 does support unaligned access
M0, M+ doesn't support unaligned access
however you can disable the support of unaligned access in cortexm7 by setting bit UNALIGN_TRP in configuration and control register and any unaligned access will generate usage fault.
From compiler perspective, default setting is that generated assembly code does unaligned access unless you disable this by using the compile flag -mno-unaligned-access

Resources