如何解决Cortex-M 编译器生成不正确的 FOR 循环
在 Cortex-M 4 和 Cortex-M 0 上测试和复制。
我发现 GCC 编译器存在问题。当一个函数被声明为 int 类型(非空),并且包含一个 for 循环,但没有 return 语句时,for 循环不会中断;反汇编编译后的代码,有返回函数和无返回函数是有区别的。
编译此代码时,不会抛出错误消息。在第一次编译时,会抛出缺少 return 语句的警告,但之后警告将不会重新出现,直到您重新启动 IDE。如此严重的问题可能无法编译,或者至少会使 Arduino 崩溃,但它永远不会跳出 for 循环。
我主要是想找到合适的渠道来报告这个问题,因为我不确定 GNU ARM Embedded Toolchain launchpad 或 GNU Bugzilla 是否会继续维护。如果有人知道哪个(或两个)网站仍在维护,或者我可以与项目中的某个人直接联系,我可以与他们分享,请分享。
以下是对该行为的更详尽描述。
Arduino 代码
============
这是对最小可重现示例的尝试。我在较大的项目中曾在两个不同的场合遇到过这个问题,这导致程序以极其意外且难以调试的方式运行(但总是通过在函数定义中添加 return 语句来解决)。
/*
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+
*
*
*/
反汇编代码
==================
这里的括号中有一个奇怪的行为。我不是低级代码的专家,但似乎 AFunctionnoreturn 在这里递归调用自己。如果不是,它仍然没有中断条件,并且它没有像 cmp r4,r5
中的 AFunctionWithReturn 这样的比较调用。
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,#48] ; (42f4 <_Z19AFunctionWithReturni+0x38>)
{
42c2: 4605 mov r5,r0
Serial.print("CountWR");
42c4: 480b ldr r0,#44] ; (42f4 <_Z19AFunctionWithReturni+0x38>)
42c6: f000 fafa bl 48be <_ZN5Print5printEPKc>
Serial.println(count);
42ca: 480a ldr r0,#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,lr}
Serial.print("CountNR");
42fa: 4909 ldr r1,#36] ; (4320 <_Z17AFunctionnoreturni+0x28>)
Serial.println(count);
for(int i=0;i<count;i++) {
Serial.println(i);
42fc: 4d09 ldr r5,#36] ; (4324 <_Z17AFunctionnoreturni+0x2c>)
{
42fe: 4604 mov r4,r0
Serial.print("CountNR");
4300: 4808 ldr r0,#32] ; (4324 <_Z17AFunctionnoreturni+0x2c>)
4302: f000 fadc bl 48be <_ZN5Print5printEPKc>
Serial.println(count);
4306: 4621 mov r1,r4
4308: 4806 ldr r0,#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);
}
解决方法
也许能说的最有帮助的事情是:“你为什么要错过 return 语句?你希望达到什么目的?”
各种语言标准(Arduino 有点像 C++,但有一些有趣的预处理)告诉您如果编写有效代码会发生什么。如果您编写无效代码,他们并不总是告诉您会发生什么。在这种情况下,编译器非常有帮助地指出了为什么您的代码是错误的,但在那之后它可以完全自由地做任何事情。无论它做什么,这都不是编译器中的错误,而是代码中的错误。这有时称为“垃圾进-垃圾出”。
为了解释为什么你得到了你所做的特定结果,可以这样想:编译器知道在一个有效的程序执行中,如果没有返回语句,永远不会运行到函数的末尾,所以如果没有在循环之后的 return 语句,可以安全地假设它永远不会离开循环。做出这个假设有助于优化有效代码以更快地运行。如果这个假设改变了无效程序的行为,那么编译器作者通常不关心。他们通常只对有效程序的作用感兴趣。
(关于启动板页面,如果您点击页面顶部的大链接,您将看到有关网站已移至何处的消息)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。