Я нахожусь в процессе написания компилятора для подмножества 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
...
Итак, мои вопросы:
too many sections (X)
), количество секций, которые будут сгенерированы, или это код ошибки?'-Os'
на мой компилятор. То есть, что я могу сделать, чтобы исправить проблему, а не обойти ее?Заметка; Я собираю с помощью cygwin64
,
Я сделал здесь несколько взломов и реорганизовал вещи, чтобы показать стиль не-во время исполнения-полиморфный:
Я надеюсь, что это не увеличит время компиляции 🙂 (я на самом деле не удосужился расщепление грамматика вверх, но она стала меньше).
Особенности:
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<>
и некоторые другие подходы):
Цель состояла в том, чтобы показать вам, что я имел в виду, когда упомянул, может быть,
Пытаться
Включение оптимизации (-O1
флаг) решил проблему за меня.
Попробуйте добавить -Wa,-mbig-obj
на ваш CXX_FLAGS
, Это будет работать с новым достаточно gcc
,