This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] 编译器/MSP430F149:GCC 9.2.0.50编译错误、带测试用例

Guru**** 2534260 points
Other Parts Discussed in Thread: MSP430F149

请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/946728/compiler-msp430f149-gcc-9-2-0-50-miscompilation-with-test-case

器件型号:MSP430F149

工具/软件:TI C/C++编译器

您好、对于 gcc 9.2.0.50、以下代码编译错误。 之所以观察到这一点、是因为它生成了一个无限循环。

下面的测试用例是我的应用程序的精简版本(它实际上不对向量进行计数;这只是演示错误编译的最小测试用例)

实际上、写入 GPIO 并不是必需的-我将 objdump 输出与源代码相结合非常有用

我可以强制编译器通过禁用某些优化(如下所示)来发出正确的代码、这可能会提供错误发生的线索。 或者可能不是-我不是编译器专家!

如果有人对此进行了探讨、我会非常感激、因为我不会经常在这些论坛上闲逛

/*----------------------------------------------
 *使用以下代码进行编译:
 *   msp430-gcc -OS -mmcu=MSP430F149 -IC:/ti/msp430_gcc/include test.c
 *
 *编译器会将函数 vectors_used 错误地转换为
 *无限循环
 *
 *自由洒满易挥发性限定符将会导致正确(但是
 *次优)要生成的代码。  添加"-fno-tree-dominator-opts"
 *-fno-tree-vrp"添加到编译器开关也会导致正确
 要生成的*代码
 *************************************************************************************************** *
#include
#include
typedef unsigned int U16;

U16 __attribute__((已用)) vectors_used( void )

   const U16 *地址;
   使用的 U16 = 0;

   对于(addr=(const U16 *) 0xffe0;addr;addr++){
       if (* addr!= 0xFFFF){
           P5OUT = 1;
           已用++;
           P5OUT = 0;
       }
   }

   使用的退货;


int main( void )

   对于(;;){
       P4OUT = vectors_used ();
   }
   返回0;


  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我认为问题是使用纯"addr"作为循环条件。 我尝试了几个变体。

    int vectors_used (void)
    {
    int *addr;
    使用的 INT = 0;
    
    对于(addr=(void *) 0xfffe;addr!=(void *) 0xce0;addr-)
    if (*addr !=0xFFFF )
    use++;
    
    已使用返回;
    } 

    工作正常。 另一方面、这会生成"jump $+0"的代码:

    int vectors_used2 (void)
    {
    int *addr;
    使用的 INT = 0;
    
    Addr=(void *) 0xffe0;
    while (addr)
    {
    if (*addr !=0xFFFF )
    use++;
    addr++;
    }
    
    已使用返回;
    }
    

    空的 main 也会导致它"跳转$+0"。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    >我认为问题是使用纯"addr"作为循环条件

    很显然,我尝试了"addr != 0",这与普通"addr"没有什么区别。 我觉得地址可能有点奇怪、因为有些设备的寻址大于16位(我使用的是'f149、这是一种旧学校的16位寻址)。 但这毫无意义,因为您仍然会在*some *地址得到一个回绕。

    几乎就像编译器没有为 addr++生成控制取消频率一样(令人惊讶的是、lkml 上有几个线程在谈论这个主题)。 但这不是整个情况、因为写入 addr 不是作为指针、而是作为常规 U16工作:

    //此版本生成正确的代码
    U16 __attribute__((已使用)) vectors_used( void ){
    
    U16地址;
    使用的 U16 = 0;
    
    对于(addr=0xffe0;addr;addr+=2){
    if (*(U16 *) addr!= 0xFFFF){
    P5OUT = 1;
    已用++;
    P5OUT = 0;
    }
    }
    
    已使用返回;
    } 

    因此、它显然与控制依赖项和指针有关、但它知道什么

    我问我的原始代码是否有效。 我在 C 语言方面有相当丰富的经验、并且长时间盯着简短的测试案例、试图找出所写代码的错误。 我出现空白-根本看不到任何明显的东西

    我不知道。 正如我所写的、我不是编译器专家。 但是我最近继承了一个10+yo 13kLOC 项目,因为原因我要尝试使用 gcc。 修复了这个错误(这很明显是因为它提供了一个闭锁)之后、我将在生产测试序列下更进一步地点击另一个闭锁。 我的代码库中可能存在什么*其他*未检测到的错误编译(我尚未在完整代码库中尝试过"-fno-tree-dominator-opts -fno-tree-vrp"技巧,但我确实看到它将代码大小增加了5%多一点, 这可能对我的特定情况来说很好、但如果不是这样的话会更好)

    S.

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    [引用 user="Steve Landy">因此、它显然与控制依赖项和指针有关、但知道什么当指针"缠绕"时、它看起来是一个问题。 例如,以下命令使用  MSP430-elf-gcc-9.2.0 -OS 生成无限循环

    U16 __attribute__((已用)) vectors_used( void ){
    
    const U16 * addr;
    使用的 U16 = 0;
    
    对于(addr=(const U16 *) 0xffe0;addr!=(const U16 *)(0xfffe + 2);addr++){
    if (* addr!= 0xFFFF){
    P5OUT = 1;
    已用++;
    P5OUT = 0;
    }
    }
    
    已使用返回;
    } 

    而将终止循环的地址更改为以下不会生成无限循环:

    U16 __attribute__((已用)) vectors_used( void ){
    
    const U16 * addr;
    使用的 U16 = 0;
    
    对于(addr=(const U16 *) 0xffe0;addr!=(const U16 *)(0xfffe + 0);addr++){
    if (* addr!= 0xFFFF){
    P5OUT = 1;
    已用++;
    P5OUT = 0;
    }
    }
    
    已使用返回;
    } 

    [报价用户="Steve Landy">我问我的原始代码是否有效。 我在 C 语言中有很好的经验、并且长时间盯着简短的测试用例、试图找出所写代码的错误。通过快速搜索、如果指针换行、看起来是未定义的行为-请参阅 指针溢出检查

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    它比生成无限循环更糟糕。 在测试中、我确实将整个函数简化为"跳转$"。 根本没有其他代码、甚至不返回。

    如果发出警告、我不会介意这一点。

    更改内容、使"addr"的类型为"int"、也可以使其正常工作。 (使用强制转换使存储器参考工作。)

    int vectors_used (void)
    {
    int addr;
    使用的 INT = 0;
    
    对于(addr= 0xe0;addr;addr++)
    if (*((int *) addr)!= 0xFFFF)
    used++;
    
    已使用返回;
    }
    

    但是、我认为最好的解决方案是通过以下方式避免所有这些问题:

    int vectors_used (void)
    {
    int *addr;
    INT I、被使用= 0;
    
    对于(addr =(void *) 0xffe0、i = 0;i < 16;i++)
    if (* addr++!=0xFFFF)
    use++;
    
    已使用返回;
    }
    

    查看生成的代码时、我发现变量"i"已消失、编译器使用指针作为循环变量。 即使在增量后发出冗余比较为零、也能使端接值为零。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    表达式0xfffe + 2的类型为 unsigned int。 由于'f419有一个16位 int、因此它是三进制0、而不进行任何花哨的优化。 因此、我并不感到惊讶、它会提供无限循环、但是:

    [引用用户="Chester Gillon"]

    从快速搜索中、如果指针换行、则看起来是未定义的行为-请参阅 指针溢出检查

    [/报价]

    首先、感谢这种联系-这是一个非常有趣的项目、可能会变成我的兔子洞! 很遗憾、它引用了标准、但不是特定的部分、所以我将我的副本挖开了。 我认为相关的位是关于加法运算符的位置:

    [引用用户="ISO-9989第6.3.6节"]

     运算符的目的。  指向 非数组 对象的指针 的行为 与相同
    指向  长度 为1的数组的第一个元素的指针、对象 的类型 作为其元素
    类型。

    (笑声)

    如果 指针 操作数 和结果 都指向  同一数组的元素
    物体。  或 数组 对象的最后一个元素之后、 评估  不应产生  
    溢出; 否则 、行为 未定义。  

    [/报价]

    因此、我想我在我写 addr=(const U16 *) 0xffe0时就迷路了

    我明白我特别要求编译器针对大小进行优化、我接受我编写的代码依赖于未定义的行为、但该死的... 从实施质量的角度来看、这并不是很友好。 我同意缺乏警告是相当令人震惊的。

    S.

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Steve、

    我已经听过你几天了,所以我假设你的问题已经得到了解答。
    如果情况并非如此,请单击“这无法解决我的问题”按钮,并使用更多信息回复此主题。
    如果此主题锁定、请单击"提出相关问题"按钮、然后在新主题中描述您的问题的当前状态以及您可能需要帮助我们帮助解决您的问题的任何其他详细信息。


  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    好的

    我所听到的是"是的、gcc 编译错误。 但您的代码会调用未定义的行为、因此很难"

    真正的问题是、它会无提示地使它复杂化

    所以除非一个 gcc 人愿意升高并拥有这个-并且根据过去的形式我不会屏住呼吸-那么我认为正确的解决办法就是不要使用 gcc

    S.

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    不要太多的阅读内容。 TI 员工非常热衷于关闭主题帖、我怀疑工作绩效评级与之相关。

    字节运算问题已经存在了很长时间。 我将 binutils 当前版本的代码(tc-msp430.c 和 opcode/msp430.h)与 mspgcc 天后的代码进行了比较。 也不会检查字节操作是否有意义。 说明表似乎没有包含任何信息、因此这种检查必须简单、以便应用于:

    2816. 判例"b":
    2817 /*字节操作。 */2818.
    bin |= byte_operation;
    2819 byte_op = true;
    2820 Check = true;
    2821 中断; 

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    这是 GCC 的通用选择、不会以这种方式发出警告和进行优化。 如果将"U16"更改为 uintptr_t、则可以在主机系统上观察到相同的行为。

    GCC 警告许多未定义的行为、使用-Wall 和-Wextra、我不确定这种情况为什么不是它警告的内容之一。 它应该针对有符号指针溢出发出警告、但不针对无符号指针溢出发出警告。

    您可以在 GCC bugzilla 上发布帖子(使其成为 x86或 x86_64系统的通用帖子)、并且您可能会了解 GCC 为何不专门针对此发出警告、或者可能只是在雷达下方发布、有人会添加警告。

    我还会将其放在 MSP430-GCC 待办事项中。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    还值得注意的是、有关这些指针溢出优化的投诉已经存在了一段时间。 您可能会发现本文评论中的讨论很有意思: https://lwn.net/Articles/278137/

    鉴于此问题已存在一段时间的争用点、我怀疑从未添加过警告、因为它会产生过多的噪声。 运行时清除器可能会捕获-fcleepe=undefined、但是、MSP430不支持 GCC 运行时清除器。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    这篇文章很有趣、感谢您的链接! (其中有一些切面但具有启发性的位、例如"C 不是那么低、整数类型会环绕")

    我想、如果 gcc 没有发出警告、则问题变为"什么 SCA 工具会将其标记为可疑?"。 或者、我想、将测试委托给我们的客户... 这就是它们的用途、对吧? (笑声)但是现在我想我们的标题是“非主题”

    (简史:这已投入生产超过十年、甚至9年以上都未涉及到。 由于硬件过时、它才会恢复使用。 最初使用 Quadavox 构建、但并未完成大量工作、以便与 CrossWorks 一起沿线路的某个位置运行。 现在、我要拿起这些器件...)

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    [引用用户="Steve Landy">我想如果 gcc 不发出警告、问题就会变成"什么 SCA 工具会将其标记为可疑?"。 [/报价]

    恐怕我对此没有任何建议。

    GCC 具有内置的静态分析器(-fanalyzer)、该分析器处于初始阶段、因此检测不到太多。 也许可以将这一范围扩大到对此提出警告。

    或者、当 GCC 发现这种优化机会时、可能会添加警告、并删除它认为永远不会执行的代码。

    考虑到在 GCC 的代码内部表示中执行的优化是模块化的、则可能无法进行后续操作、在执行此优化时、代码可能无法追溯到您在代码中使用的特定未定义行为。 我的意思是、如果早期的优化将有效代码强制转换为看起来与调用未定义行为的代码相同的格式、则 GCC 无法对此发出警告、因为它会生成大量噪声和/或无效警告。

    我现在只是猜测、在给出准确的答案之前、我需要适当地查看此优化的机制。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Steve、

    感谢 Jozef 对此提供的支持。  您似乎能够继续进行编码。

    我看到 Jozef 正在挖掘更多信息、但在平均时间内、我将关闭此主题。

    请单击"这未解决我的问题"按钮、并使用更多信息回复此主题。
    如果此主题锁定、请单击"提出相关问题"按钮、然后在新主题中描述您的问题的当前状态以及您可能需要帮助我们帮助解决您的问题的任何其他详细信息。