10

私は、boost::spirit字句解析と解析に を使用して、Java のサブセット用のコンパイラを作成中です。lexer/parser フェーズのコンパイル中に、コンパイラ1.6GBは RAM ( 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. lexer と parse フェーズを 2 つの異なるフェーズ (およびコンパイル ユニット) に分割し、lexer イテレータを介して接続するだけで解決しますか?

ノート; を使ってコンパイルしてcygwin64います。

4

4 に答える 4

8

ここでいくつかのハッキングを行い、ランタイムではないポリモーフィック スタイルを示すためにリファクタリングを行いました。

コンパイル時間が長くならないことを願っています:) (実際には文法を分割することはできませんでしたが、小さくなりました)。


特徴:

  • ヒープが割り当てられた AST ノードはもうありません (expressionおよび/またはのようなツリーでもありませんstatement)。したがって、明示的なクローン作成や偽の const メンバーはもうありません。
  • Maybe.hpp を次のように置き換えました

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

    それは手っ取り早いですが、すべてコンパイルされます

  • 私はオープン型切り替えを自分の小さな努力に置き換えました(私はそれを動作させることができませんでした.boost-variantでもすべて組み込まれています):

    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 クラスが著しく単純になったことに注意してください (特に、メモリ割り当てに関しては、より簡単に正しい)。

  • セマンティック アクションの必要性が減少し、Phoenix に適合した関数が原因で、パーサー自体が縮小されたことに注意してください。

  • ここでは、LexerPosition 情報を忘れることを選択したことに注意してください(基本クラスでは「非表示」でしたが、現在は削除されています)。選択qi::on_error(qi::success, ...)した Ast ノードにソースの場所情報を非常にエレガントに(非介入的に)添付する方法を示すコンパイラ チュートリアルの例があります。

  • のさまざまな述語の代わりにast_helpers、多くの有用な特性ベースの述語 ( is_lvalueor などis_true_const) が存在する可能性があると予想しています。私はヘルパーを多かれ少なかれ現状のまま「維持」することを選択しました(これ完全に間違っている可能性があります。私は何もテストしていません)。

  • 値によるパラメーターの受け渡しを受け渡しに置き換えることを広く試みてきましたconst&(たとえば、 ast_pp.hpp を比較してください) が、タスクが十分に大きかったため、いくつかのスポットを残したことを認識しています。

巨大な免責事項: 私はおそらくさまざまな方法でパーサーを壊しました。私はそれで何かを解析しようとはしていません。編集内容はそのまま提供され、有用性を主張するものではありません。私は同様の問題を異なる方法で解決しました(かつてはtraits::tranform_attribute<>専門化され、かつては大きなセマンティックアクションでat_c<>あり、他のいくつかのアプローチでした):

目標は、多分と言ったときに私が念頭に置いていたことを示すことでした

  • 動的多型を大幅に減らし、
  • セマンティック アクションの回避
  • スピリットとのより「自動的な」統合を得るために、可能な限りブースト構造を採用する
  • あなたのお気に入りを選ぶことができるさまざまなアイデアを示しています:/
于 2013-09-11T02:54:49.100 に答える
3

試す

  • 異なる翻訳単位に分割する
  • デバッグ情報の無効化(多くの場合、デバッグ情報は他のオブジェクト データと同じように出力されるため、ファイル サイズが大きい場合に問題が発生します)
  • rtti を無効にする (最後の手段)
于 2013-09-09T08:50:18.853 に答える