编译器对屏蔽类没有常量折叠

Posted

技术标签:

【中文标题】编译器对屏蔽类没有常量折叠【英文标题】:Compiler Does No Constant Folding for Masking Class 【发布时间】:2018-05-12 20:58:36 【问题描述】:

对于嵌入式项目,我使用辅助类作为标志和掩码。由于未知原因,掩码类的代码未按预期正确折叠。

掩码的最小实现如下所示:

template<typename Enum, typename MaskValue>
class EnumMask

public:
    constexpr inline EnumMask() noexcept : _mask(0) 
    constexpr inline EnumMask(Enum enumValue) noexcept : _mask(maskFromEnum(enumValue)) 
    constexpr inline EnumMask(const std::initializer_list<Enum> enumValues) noexcept : _mask(maskFromEnum(enumValues.begin(), enumValues.end())) 
    constexpr inline operator MaskValue() const noexcept  return _mask; 
private:
    constexpr static inline MaskValue maskFromEnum(const Enum enumValue) noexcept 
        return (static_cast<MaskValue>(1)<<static_cast<uint8_t>(enumValue));
    
    constexpr static inline MaskValue maskFromEnum(
        typename std::initializer_list<Enum>::const_iterator it,
        typename std::initializer_list<Enum>::const_iterator end) noexcept
    
        return (it == end ? static_cast<MaskValue>(0) : (maskFromEnum(*it)|maskFromEnum(it+1, end)));
    
private:
    const MaskValue _mask;
;

类的使用如下例所示:

class Driver

public:
    enum Pin : uint8_t 
        GPA0 = 0x00,
        GPA1 = 0x01,
        GPA2 = 0x02,
    ;
    typedef EnumMask<Pin, uint16_t> PinMask;
    void setPinDirection(const uint16_t mask, bool direction);
    inline void setPinDirection(const PinMask &mask, bool direction) 
        setPinDirection(static_cast<uint16_t>(mask), direction);
    
;

void main()

    Driver d;
    d.setPinDirection(Driver::GPA0, Driver::GPA1, true);

代码是使用带有选项-Os 的GCC 4.8.3 编译的。我希望,编译器会将此代码解析为单个值,但它实际上会创建一个函数来根据这些值计算掩码。

我的代码中是否有特殊原因阻止了正确的 const 折叠?

【问题讨论】:

GCC 4.x 是一个古老的编译器。 GCC 最新版本目前是 8.x。 @NeilButterworth 我知道这一点。对于嵌入式平台,遗憾的是,您必须尽最大努力。 我一直不明白为什么嵌入式平台在这方面被认为如此特别。在所有平台上,您必须尽最大努力,但关键是要确保您确实在使用最好的。 只有编译器编写者才有机会回答。同时,尝试各种宏和其他内联函数的组合。 @o11c 感谢您的提示,我实际上找到了一种让 gcc 7.2.1 与该项目一起工作的方法。它支持C++14,所以这实际上应该可以解决问题。 【参考方案1】:

解释其实很简单:

std::initializer_listbegin()end() 成员仅在 C++14 中为 constexpr

见reference

【讨论】:

在 C++20 模式下使用最新的 gcc 编译没有帮助。问题是 -Os 使编译器对内联/展开非常害羞,因为这可能会增加代码大小。但是,也需要计算最终值。 -O1 在这种情况下会给出更好的结果。

以上是关于编译器对屏蔽类没有常量折叠的主要内容,如果未能解决你的问题,请参考以下文章

如何在我的中间代码上执行常量折叠和常量传播?

类加载机制

高质量C++读书笔记(复习用)

const和defin区别

C++知识点整理

初探JVM