Tested and reproduced on Cortex-M 4 and Cortex-M 0.
I have discovered an issue with the GCC compiler. When a function is declared as type int (non-void), and contains a for loop, but does not have a return statement, the for loop will not break; after disassembling the compiled code, there is a difference between functions with a return, and without a return.
When this code is compiled, it does not throw an error message. On the first compile, a warning of missing return statements is thrown, but after that the warning will not reappear until you restart the IDE. An issue of this magnitude should probably fail to compile, or at least crash the Arduino, but it just never breaks out of the for loop.
I am mainly looking to find the proper channels to report this, since I am not sure if GNU ARM Embedded Toolchain launchpad or GNU Bugzilla are maintained anymore. If anyone knows which site (or both) are still maintained, or if there's a direct contact to someone in the project who I can share this with, please share.
Below is a more thorough description of the behavior.
Arduino Code
============
This is an attempt at a minimum reproducible example. I have run into this issue on two separate occasions in larger projects, which cause the program to behave in extremely unexpected and hard to debug ways (but always fixed by adding a return statement in the function definition).
/*
gcc compiler error demonstration for Adafruit GrandCentral
gcc version: gcc version 9.2.1 20191025 (release) [ARM/arm-9-branch revision 277599] (GNU Tools for Arm Embedded Processors 9-2019-q4-major)
Arduino IDE: all warinings on
Arduino IDE version 1.8.13
Adafruit SAMD version 1.8.11
based on Blink
modified to call two functions which are identical except one does not have a return
statement even though it is of return type int.
In the list file, myList.GrandCentral.lst ,AFunctionWithReturn shows both the comparison of
i with Count and the conditional comparison i>7 with break assembly instructions
The AFunctionNoReturn does not show any assembly instructions for the end of
loop comparision or the conditional comparison i>7 with break
Found 4/8/21 Robert Calay and Tristan Calay
Turns an LED on for one second, then off for one second, repeatedly.
Most Arduinos have an on-board LED you can control. On the UNO, MEGA and ZERO
it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN is set to
the correct LED pin independent of which board is used.
If you want to know what pin the on-board LED is connected to on your Arduino
model, check the Technical Specs of your board at:
https://www.arduino.cc/en/Main/Products
modified 8 May 2014
by Scott Fitzgerald
modified 2 Sep 2016
by Arturo Guadalupi
modified 8 Sep 2016
by Colby Newman
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/Blink
*/
#define MAIN
//#include "Serial3.h" We are re-directing serial port output to SERCOM 5 on the Grand Central M4.
int AFunctionWithReturn(int count)
{
Serial.print("CountWR");
Serial.println(count);
for(int i=0;i<count;i++) {
Serial.println(i);
if (i>7)
break;
}
return(1);
}
int AFunctionNoReturn(int count)
{
Serial.print("CountNR");
Serial.println(count);
for(int i=0;i<count;i++) {
Serial.println(i);
if (i>7)
break;
}
//Note: No return statement here.
}
// the setup function runs once when you press reset or power the board
void setup() {
Serial.begin(115200);
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
AFunctionWithReturn(10); //This loops 8 times
AFunctionNoReturn(10); //This loops forever, never reaching loop()
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
/*
OUTPUT ON ADAFRUIT GRANDCENTRAL SERIAL PORT
CountWR10
0
1
2
3
4
5
6
7
8
CountNR10
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
....
DOES NOT STOP CONTINUES 2000000+
*
*
*/
Disassembled Code
=================
There is a strange behavior in the brackets here. I'm no expert on the low level code, but it seems like AFunctionNoReturn calls itself recursively here. If not, it still has no break condition, and it does not have a compare call like AFunctionWithReturn in cmp r4, r5
.
int AFunctionWithReturn(int count)
{
42bc: b570 push {r4, r5, r6, lr}
Serial.print("CountWR");
42be: 490c ldr r1, [pc, #48] ; (42f0 <_Z19AFunctionWithReturni+0x34>)
Serial.println(count);
for(int i=0;i<count;i++) {
Serial.println(i);
42c0: 4e0c ldr r6, [pc, #48] ; (42f4 <_Z19AFunctionWithReturni+0x38>)
{
42c2: 4605 mov r5, r0
Serial.print("CountWR");
42c4: 480b ldr r0, [pc, #44] ; (42f4 <_Z19AFunctionWithReturni+0x38>)
42c6: f000 fafa bl 48be <_ZN5Print5printEPKc>
Serial.println(count);
42ca: 480a ldr r0, [pc, #40] ; (42f4 <_Z19AFunctionWithReturni+0x38>)
42cc: 220a movs r2, #10
42ce: 4629 mov r1, r5
42d0: f000 fb43 bl 495a <_ZN5Print7printlnEii>
for(int i=0;i<count;i++) {
42d4: 2400 movs r4, #0
42d6: 42ac cmp r4, r5
42d8: da08 bge.n 42ec <_Z19AFunctionWithReturni+0x30>
Serial.println(i);
42da: 220a movs r2, #10
42dc: 4621 mov r1, r4
42de: 4630 mov r0, r6
42e0: f000 fb3b bl 495a <_ZN5Print7printlnEii>
if (i>7)
42e4: 2c08 cmp r4, #8
42e6: d001 beq.n 42ec <_Z19AFunctionWithReturni+0x30>
for(int i=0;i<count;i++) {
42e8: 3401 adds r4, #1
42ea: e7f4 b.n 42d6 <_Z19AFunctionWithReturni+0x1a>
break;
}
return(1);
}
int AFunctionNoReturn(int count)
{
42f8: b538 push {r3, r4, r5, lr}
Serial.print("CountNR");
42fa: 4909 ldr r1, [pc, #36] ; (4320 <_Z17AFunctionNoReturni+0x28>)
Serial.println(count);
for(int i=0;i<count;i++) {
Serial.println(i);
42fc: 4d09 ldr r5, [pc, #36] ; (4324 <_Z17AFunctionNoReturni+0x2c>)
{
42fe: 4604 mov r4, r0
Serial.print("CountNR");
4300: 4808 ldr r0, [pc, #32] ; (4324 <_Z17AFunctionNoReturni+0x2c>)
4302: f000 fadc bl 48be <_ZN5Print5printEPKc>
Serial.println(count);
4306: 4621 mov r1, r4
4308: 4806 ldr r0, [pc, #24] ; (4324 <_Z17AFunctionNoReturni+0x2c>)
430a: 220a movs r2, #10
430c: f000 fb25 bl 495a <_ZN5Print7printlnEii>
for(int i=0;i<count;i++) {
4310: 2400 movs r4, #0
Serial.println(i);
4312: 4621 mov r1, r4
4314: 220a movs r2, #10
4316: 4628 mov r0, r5
4318: f000 fb1f bl 495a <_ZN5Print7printlnEii>
for(int i=0;i<count;i++) {
431c: 3401 adds r4, #1
431e: e7f8 b.n 4312 <_Z17AFunctionNoReturni+0x1a>
4320: 00006538 .word 0x00006538
4324: 2000011c .word 0x2000011c
00004328 <loop>:
AFunctionWithReturn(10);
AFunctionNoReturn(10);
}