Что мешает вычислению этой функции constexpr во время компиляции?

Я работаю над классом для представления набора аппаратных выводов микроконтроллера (STM32). Выбранные контакты могут быть прерывистыми на порте, но предполагается, что они заказаны. Например, если этот объект создан для представления контактов PA2, PA3 и PA6, я хочу иметь возможность сделать такое назначение, которое устанавливает PA2 и PA6 и сбрасывает PA3.

В настоящее время я еще не реализовал ctor для прерывистых контактов. Текущий позволяет отображать только непрерывные выводы, такие как PA2, P3 и PA4. Однако логика отображения сжатых битов (например, в приведенном выше примере) для фактических аппаратных битов реализовано для прерывистого случая.

Я думал, что такое задание, как может быть рассчитан в основном во время компиляции, и только загрузка фактического аппаратного регистра (для STM32, который обрабатывает атомарный набор и сброс аппаратных выводов) происходит во время выполнения с использованием предварительно рассчитанного значения. К сожалению, это не то, что происходит, и значение для загрузки также рассчитывается во время выполнения.

Вот несколько упрощенная и недоработанная версия кода, который я тестирую. Код выбора порта (GPIOA, GPIOB и др.) Опущен.

      #include <cstdint>

volatile uint32_t BSRR {0}; // Assume it's a HW register for atomic pin access.

class PortSegment {
public:

    constexpr PortSegment(uint8_t start, uint8_t end)
    : selection{calculateSelection(start, end)} {}

    uint16_t operator=(uint16_t setVal) const;
//  operator uint16_t() const; // to be implemented later

private:

    static constexpr uint16_t calculateSelection(uint8_t start, uint8_t end);
    static constexpr uint16_t mapBits(uint16_t val, uint16_t selection);

    uint16_t selection; // Table of used bits in the port

};

// Used in ctor
constexpr uint16_t PortSegment::calculateSelection(uint8_t start, uint8_t end)
{
    uint16_t result {0};
    for (unsigned i = start; i <= end; ++i) result |= (1u << i);
    return result;
}

// static function
constexpr uint16_t PortSegment::mapBits(uint16_t val, uint16_t selection)
{
    uint16_t result {0};
    for (unsigned i = 0; i < 16; ++i) {
        if (selection & 1u)  {
            if (val & (1u << i)) {
                result |= (1u << i);
            }
        }
        else {
            val <<= 1;
        }
        selection >>= 1;
    }
    return result;
}

inline uint16_t PortSegment::operator=(uint16_t setVal) const
{
    uint32_t mapped {mapBits(setVal, selection)};
    BSRR = ((~mapped << 16) | mapped)
            & ((static_cast<uint32_t>(selection) << 16) | selection);
    return setVal;
}

int main()
{
    constexpr PortSegment segment {2,5}; // Use port pins 2,3,4,5
    segment = 0b1010u;
}

В переменная-член представляет контакты, используемые в порту. Например, означает использовать PA2, PA3, PA4, PA5. Проблема в том, что функция не вычисляется во время компиляции. Я также пытался сделать его нестатической функцией-членом, но ничего не изменилось. По моей логике, когда объект класс создан, все уже известно во время компиляции, и значение, которое будет загружено в также могли быть известны. Но, похоже, я чего-то упускаю.

Еще одна странная вещь, которую я обнаружил, если я изменю в функции (что не имеет смысла для алгоритма), можно рассчитать время компиляции.

Вот код в Godbolt.

1 ответ

Вы установили оптимизацию на уровень 1 в Godbolt! Пытаться -O3 вместо -O1.

Другие вопросы по тегам