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

Я нахожусь в процессе написания компилятора для подмножества Java, используя boost::spirit, для lexing и разбора. Во время компиляции фазы лексера / парсера компилятор потребляет 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
...

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

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

Заметка; Я собираю с помощью cygwin64,

10

Решение

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

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


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

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

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

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

  • Я заменил открытое переключение типов своими собственными незначительными усилиями (я не смог заставить его работать; также с буст-вариантом он все встроен):

    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) но я знаю, что я оставил некоторые места, потому что задача была достаточно большой, как это было.

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

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

  • значительное снижение динамического полиморфизма,
  • избегая семантических действий
  • где это возможно, использование буст-конструкций, чтобы получить более «автоматическую» интеграцию с духом
  • показывая различные идеи, которые вы можете выбрать ваши любимые (ы) из: /
8

Другие решения

Пытаться

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

Включение оптимизации (-O1 флаг) решил проблему за меня.

2

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

0
По вопросам рекламы [email protected]