GCC отказывается выпускать длинные вызовы для оператора new/delete на PowerPC - программирование
Подтвердить что ты не робот

GCC отказывается выпускать длинные вызовы для оператора new/delete на PowerPC

У ветвей PowerPC есть только 24 бита для смещения цели, поэтому, если текстовый раздел становится слишком большим, ветки на одном конце не смогут достичь целей на другом. Там более длинная последовательность инструкций, которые могут достигать цели дальше (смещение составляет 32 бита вместо 24), но GCC не использует его по умолчанию, если вы не передадите ему опцию -mlongcall. Однако даже при включенной опции GCC по-прежнему генерирует короткие вызовы для определенных функций, а именно operator new и operator delete

Например, данный код:

extern void foo();

int main(int argc, char** argv) {
    foo();
    new char;
}

Обычный запуск GCC будет генерировать сборку:

bl _Z3foov // void foo()
bl _Znwj   // operator new(unsigned int)

Запуск GCC с помощью опции -mlongcall генерирует:

lis r9, [email protected]
addi r9, r9, [email protected]
mtctr r9
bctrl
bl _Znwj

Первые четыре команды являются длинным вызовом foo(), как и ожидалось, но вызов operator new не изменяется. Вызовы для случайных функций libc и libstdС++ преобразуются в длинные вызовы, как ожидалось. Почему вызовы operator new и operator delete все еще заканчиваются как инструкции bl? Есть ли способ заставить GCC сделать их длинными вызовами? Я использую GCC 4.7.2 на 64-битной машине PowerPC Fedora (хотя я строю 32-разрядную версию)

4b9b3361

Ответ 1

Кажется, что g++ toolchain имеет какую-то ошибку в том, как она называет восемь "заменяемых" функций стандартной библиотеки С++ в вашей архитектуре, если эти функции фактически не заменяются кодом пользователя.

Переносимая замена для всех восьми:

#include <memory>
#include <cstdlib>

// May never return a null pointer.
void* operator new(std::size_t size) {
    void* p = std::malloc(size, 1);
    while (!p) {
        std::new_handler handler = std::get_new_handler();
        if (handler) {
            handler();
        } else {
            throw std::bad_alloc();
        }
        // A handler is only allowed to return if it did something to make more
        // memory available, so try again.
        p = std::malloc(size, 1);
    }
    return p;
}

void operator delete(void* p) noexcept {
    if (p) std::free(p);
}

void* operator new(std::size_t size, const std::nothrow_t&) noexcept {
    void* p = nullptr;
    try {
        p = operator new(size);
    } catch(...) {}
    return p;
}

void operator delete(void* p, const std::nothrow_t&) noexcept {
    operator delete(p);
}

// May never return a null pointer.
void* operator new[](std::size_t size) {
    return operator new(size);
}

void operator delete[](void* p) noexcept {
    operator delete(p);
}

void* operator new[](std::size_t size, const std::nothrow_t& nt) noexcept {
    return operator new(size, nt);
}

void operator delete[](void* p, const std::nothrow_t& nt) noexcept {
    operator delete(p, nt);
}

Ответ 2

Если мы можем определить эту функцию в #pragma long_calls или объявить атрибут long-call внутри этой функции, мы можем заставить GCC также сделать их длинными вызовами. Checkout GCC-options.