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 语句,可以安全地假设它永远不会离开循环。做出这个假设有助于优化有效代码以更快地运行。如果这个假设改变了无效程序的行为,那么编译器作者通常不关心。他们通常只对有效程序的作用感兴趣。

(关于启动板页面,如果您点击页面顶部的大链接,您将看到有关网站已移至何处的消息)。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...