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, _Z3foov@ha
addi r9, r9, _Z3foov@l
mtctr r9
bctrl
bl _Znwj

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


person Michael Mrozek    schedule 11.03.2013    source источник
comment
Какая версия GCC? Вы отправили отчет об ошибке на gcc.gnu.org/bugzilla? Вы пробовали с последней версией GCC (4.7.2 и скоро 4.8)?   -  person Basile Starynkevitch    schedule 12.03.2013
comment
Ущипните меня, здесь уже поздно, и, может быть, я уже сплю. Или я просто действительно нашел хороший вопрос?   -  person    schedule 12.03.2013
comment
Просто интересно, будет ли то же самое, если вы определите замену void* operator new(std::size_t);?   -  person aschepler    schedule 12.03.2013
comment
@BasileStarynkevitch 4.7.2. Я не знаю, что это ошибка; Я предполагаю, что у GCC есть веская причина, я просто не знаю, что это такое   -  person Michael Mrozek    schedule 12.03.2013
comment
@aschepler Это исправляет (превращается в долгий вызов). Есть ли в этом недостаток? Он все еще компилируется, даже если я на самом деле не реализую эту функцию; видимо все же подхватывает стандартный   -  person Michael Mrozek    schedule 12.03.2013
comment
Хм. Это одна из восьми функций, которые должны быть определены стандартной библиотекой C++, но также могут быть заменены определяемой пользователем функцией. Это объясняет, почему компоновщик действует для него по-другому, но помимо этого это должно быть просто ошибкой.   -  person aschepler    schedule 12.03.2013
comment
new char не является прямым вызовом operator new(1), что произойдет, если вы вызовете его напрямую?   -  person MSalters    schedule 12.03.2013
comment
@MSalters Получается то же самое (объектный файл идентичен)   -  person Michael Mrozek    schedule 12.03.2013
comment
@Ben, если текстовый раздел станет слишком большим, ветви на одном конце не смогут достичь целей на другом   -  person Michael Mrozek    schedule 12.03.2013
comment
@MichaelMrozek, 24 бита = 16 МБ - это большой раздел, но не невозможный. Но генерируются ли в этом случае короткие вызовы (предположительно, SEGV)? Есть ли у OP такие большие текстовые разделы, которые он не может разделить? Я подозреваю, что это будет генерировать длинные вызовы, но OP не конкретизирует этот момент - отсюда и вопрос.   -  person Ben    schedule 12.03.2013
comment
@Ben На самом деле это 32 МБ (оно подписано, поэтому 8 МБ, но смещения PPC умножаются на 4, чтобы соответствовать размеру инструкции, поэтому 32 МБ), но мне удалось это сделать; длинная история. Также возможно иметь relocs, которые пересекаются, как PLTREL24, так что это может произойти даже с меньшими текстовыми разделами. Объектные файлы имеют короткие вызовы; компоновщику не удается разрешить relocs в инструкциях bl, потому что он видит, что значение, которое ему нужно будет вставить в поле смещения, слишком велико, поэтому он просто выдает ошибку   -  person Michael Mrozek    schedule 12.03.2013
comment
@aschepler Хотите назвать свою идею ответом? Это работает для меня, хотя это немного взломано   -  person Michael Mrozek    schedule 12.03.2013
comment
@MichaelMrozek: для меня это пахнет ошибкой. Я бы сообщил об этом. Ваш пост в значительной степени идеален для отчета об ошибке.   -  person nneonneo    schedule 12.03.2013


Ответы (2)


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

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

#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);
}
person aschepler    schedule 12.03.2013

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

person sebi    schedule 28.11.2014