Слишком много разделов, ошибка ассемблера, используя boost:: spirit

Я занимаюсь написанием компилятора для подмножества Java, используя boost::spirit, для лексинга и синтаксического анализа. Во время компиляции фазы lexer/parser компилятор потребляет 1.6GB ОЗУ (g++ (GCC) 4.8.1), однако это не проблема, так как на этом компьютере достаточно памяти.

Однако проблема заключается в том, что когда компилятор завершен, а ассемблер начинает работать (GNU assembler (GNU Binutils) 2.23.52.20130604), он сбрасывается с:

as: build/src/ast_generate.o: too many sections (33098)
/tmp/cc0ZyvKK.s: Assembler messages:
/tmp/cc0ZyvKK.s: Fatal error: can't write build/src/ast_generate.o: File too big
as: build/src/ast_generate.o: too many sections (33098)
/tmp/cc0ZyvKK.s: Fatal error: can't close build/src/ast_generate.o: File too big
scons: *** [build/src/ast_generate.o] Error 1

Добавление '-Os' к моим флагам компилятора позволяет ассемблеру обрабатывать вывод компилятора, но, как я вижу, это всего лишь вопрос времени, пока я не попаду в ту же проблему, даже с небольшим флагом оптимизации.

Проверка, оптимизированный по размеру объектный файл (ast_generate.o) с помощью objdump, говорит мне, что я генерирую pe-x86-64, чего я ожидал бы в Windows.

Сгенерированные разделы 2358, однако, являются шоком для меня. В основном, как кажется, секция была сгенерирована для каждой части boost::spirit;

 CONTENTS, ALLOC, LOAD, READONLY, DATA, LINK_ONCE_DISCARD
 ...
 60 .pdata$_ZNK5boost5lexer6detail8end_node9unique_idEv 0000000c  0000000000000000  0000000000000000  00030750  2**2
 61 .text$_ZNK5boost5lexer6detail8end_node11lexer_stateEv 00000010  0000000000000000  0000000000000000  0003075c  2**4
 ...

Итак, мои вопросы:

  • Число в ошибке (too many sections (X)), количество секций, которые должны быть сгенерированы, или это код ошибки?
  • Почему для каждого типа данных создается раздел?
  • Что я могу сделать, чтобы избежать передачи '-Os' моему компилятору. То есть, что я могу сделать, чтобы исправить проблему, а не обойти ее?
  • Разделял бы лексер и фазу разбора на две разные фазы (и единицы компиляции), только связанные с помощью лектора-итератора, помогли мне?

Примечание; Я компилирую с помощью cygwin64.

Ответ 1

Я сделал некоторые хаки здесь и реорганизовал вещи a, чтобы показать не-runtime-полиморфный стиль:

Я надеюсь, что это не увеличит время компиляции:) (Я не собирался разделить грамматику, но она уменьшилась).


Особенности:

  • больше кучи не выделяют узлы AST (даже для деревьев, таких как expression и/или statement); следовательно, более явное клонирование и/или ложные константные члены.
  • Я заменил Maybe.hpp

    #pragma once
    #include <boost/optional.hpp>
    
    template <typename T> using Maybe = boost::optional<T>;
    

    Это быстро и грязно, но все это компилируется

  • Я заменил open type-change своими собственными незначительными усилиями (я не мог заставить его работать, а также с форсированным вариантом, все это встроено):

    namespace visitor_galore // this is my make-shift replacement for typeswitch (I couldn't find it/make it work)
    {
        template<typename T, class...Fs> struct visitor_t;
    
        template<typename T, class F1, class...Fs>
        struct visitor_t<T, F1, Fs...> : F1, visitor_t<T, Fs...>::type {
            typedef visitor_t type;
            visitor_t(F1 head, Fs...tail) : F1(head), visitor_t<T, Fs...>::type(tail...) {}
    
            using F1::operator();
            using visitor_t<T, Fs...>::type::operator();
        };
    
        template<typename T, class F> struct visitor_t<T, F> : F, boost::static_visitor<T> {
            typedef visitor_t type;
            visitor_t(F f) : F(f) {}
            using F::operator();
        };
    
        template<typename T=void, class...Fs>
        typename visitor_t<T, Fs...>::type make_visitor(Fs...x) { return {x...}; }
    }
    
    using visitor_galore::make_visitor;
    

    Чтобы узнать, как это используется, посмотрите на, например, ast_pp.cpp:

    void pretty_print(expression_incdec const& exp)
    {
         boost::apply_visitor(
                make_visitor(
                    [&exp](inc_dec_op_preinc const& op)  { std::cout << "++"; pretty_print(exp.variable); }, 
                    [&exp](inc_dec_op_predec const& op)  { std::cout << "--"; pretty_print(exp.variable); }, 
                    [&exp](inc_dec_op_postinc const& op) { pretty_print(exp.variable); std::cout << "++"; }, 
                    [&exp](inc_dec_op_postdec const& op) { pretty_print(exp.variable); std::cout << "--"; }
                    )
                , exp.operatur);
    }
    

    БОНУС Если вам не все равно, чтобы перечислять все типы в ветвях, например. потому что все они по умолчанию вызывают одну и ту же свободную функцию (или перегрузки), вы можете использовать полиморфного посетителя:

    static const struct pretty_print_visitor_ : boost::static_visitor<>
    {
        template<typename T>
        void operator ()(T const& v) const { pretty_print(v); }
    } pretty_print_visitor;
    

    например. теперь вы можете заменить 24 ветки на expression&:

    boost::apply_visitor(
            make_visitor(
                [](expression_binop const& exp)              { pretty_print(exp); }, 
                [](expression_unop const& exp)               { pretty_print(exp); }, 
                [](expression_integer_constant const& exp)   { pretty_print(exp); }, 
                [](expression_character_constant const& exp) { pretty_print(exp); }, 
                [](expression_string_constant const& exp)    { pretty_print(exp); }, 
                [](expression_boolean_constant const& exp)   { pretty_print(exp); }, 
                [](expression_null const& exp)               { pretty_print(exp); }, 
                [](expression_this const& exp)               { pretty_print(exp); }, 
                [](expression_static_invoke const& exp)      { pretty_print(exp); }, 
                [](expression_non_static_invoke const& exp)  { pretty_print(exp); }, 
                [](expression_simple_invoke const& exp)      { pretty_print(exp); }, 
                [](expression_ambiguous_invoke const& exp)   { pretty_print(exp); }, 
                [](expression_new const& exp)                { pretty_print(exp); }, 
                [](expression_new_array const& exp)          { pretty_print(exp); }, 
                [](expression_lvalue const& exp)             { pretty_print(exp); }, 
                [](expression_assignment const& exp)         { pretty_print(exp); }, 
                [](expression_incdec const& exp)             { pretty_print(exp); }, 
                [](expression_cast const& exp)               { pretty_print(exp); }, 
                [](expression_ambiguous_cast const& exp)     { pretty_print(exp); }, 
                [](expression_instance_of const& exp)        { pretty_print(exp); }, 
                [](expression_parentheses const& exp)        { pretty_print(exp); },
                [](lvalue_non_static_field const& exp)       { pretty_print(exp); },
                [](lvalue_array const& exp)                  { pretty_print(exp); },
                [](lvalue_ambiguous_name const& exp)         { pretty_print(exp); }
           )
            , exp);
    

    простым

    boost::apply_visitor(pretty_print_visitor, exp);
    
  • Обратите внимание на несколько случаев, когда я добавил комментарии // TODO или // FIXME (примечание с concat, которое не совсем хотело компилировать для меня больше).

  • Обратите внимание, что классы Ast значительно упростились (особенно более тривиально правильные относительно распределения памяти)

  • Обратите внимание, что сам Parser сократился из-за уменьшенной потребности в семантических действиях и адаптированных функциях Phoenix.

  • Заметьте, что я сейчас забыл о информации о LexerPosition (которая раньше была "скрыта" в базовых классах, теперь ушла). Существует пример учебника для компилятора, в котором показано, как использовать qi::on_error(qi::success, ...) для очень элегантно прикреплять информацию о местоположении источника к выбранным узлам Ast (не навязчиво).

  • Вместо различных предикатов в ast_helpers я бы предположил, что может быть множество полезных предикатов на основе признаков (например, is_lvalue или is_true_const). Я решил "сохранить" помощников более или менее как есть (что может быть совершенно неправильно, я ничего не тестировал).

  • Я повсеместно пытался заменить передачу параметра по значению на передачу const& (сравнить, например, ast_pp.hpp) но я знаю, что я оставил некоторые пятна, потому что задача была достаточно большой, как это было.

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ GIANT. Я, вероятно, по-разному разбил парсер. Я не пытался ничего разбирать с этим. Редакции предоставляются как есть и без каких-либо претензий на полезность. Я решил аналогичные проблемы разными способами (после a traits::tranform_attribute<> специализации, когда было довольно большое семантическое действие с at_c<> и некоторыми другими подходами):

Цель состояла в том, чтобы показать вам, что я имел в виду, когда я упомянул, может быть,

  • значительно снижает динамический полиморфизм,
  • избежание семантических действий
  • обдумывая конструкции boost, где возможно, чтобы получить более "автоматическую" интеграцию с духом
  • показывая различные идеи, вы можете выбрать своих фаворитов из:/

Ответ 2

Попробуйте

  • разделение его на разные единицы перевода
  • отключение отладочной информации (часто это происходит с большими размерами файлов, поскольку информация об отладке испускается как другие данные объекта)
  • отключение rtti (последнее средство)

Ответ 3

Повороты оптимизации на (-O1 flag) решили проблему для меня.

Ответ 4

Попробуйте добавить -Wa,-mbig-obj к вашему CXX_FLAGS. Это будет работать с достаточно новыми gcc.