简体   繁体   English

Boost Spirit-使用-O1构建项目,但不使用-O2构建项目

[英]Boost Spirit - project builds with -O1 but not with -O2

I have a small project (similar to their example - https://github.com/cierelabs/x3_fun/ ) using Boost Spirit X3 and it builds in debug but not in release. 我有一个使用Boost Spirit X3的小项目(类似于他们的示例-https://github.com/cierelabs/x3_fun/ ),它是在debug中构建的,而不是在发布中构建的。

X3 program structure recommendation + another example: https://www.boost.org/doc/libs/1_69_0/libs/spirit/doc/x3/html/spirit_x3/tutorials/minimal.html X3程序结构建议+另一个示例: https : //www.boost.org/doc/libs/1_69_0/libs/spirit/doc/x3/html/spirit_x3/tutorials/minimal.html

compiler: GCC 8.2.0 编译器:GCC 8.2.0

OS: Windows 10 作业系统:Windows 10

After git bisect and some project configuration experiments I narrowed the problem down to compiler optimization flag. 经过git bisect和一些项目配置实验后,我将问题缩小到编译器优化标志。

this builds: 这建立:

g++ -std=c++17 -O1 -pedantic -Wall -Wextra -c -fmessage-length=0

this does not build: 这不会建立:

g++ -std=c++17 -O2 -pedantic -Wall -Wextra -c -fmessage-length=0

The error I get: 我得到的错误:

C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: ./src/main.o:main.cpp:(.text+0x2156): undefined reference to `bool fs::parser::parse_rule<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::spirit::x3::unused_type, boost::spirit::x3::unused_type const>(boost::spirit::x3::rule<fs::parser::whitespace_class, boost::spirit::x3::unused_type, false>, __gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&, __gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > const&, boost::spirit::x3::unused_type const&, boost::spirit::x3::unused_type const&)'

It looks like the optimizer is removing parse_rule definition (which is a core function generated from Spirit) which is actually needed. 看起来优化器正在删除实际需要的parse_rule定义(这是从Spirit生成的核心函数)。

nm -C main.o dump (left is -O2 , right -O1 ): https://www.diffchecker.com/YAFmJkbN nm -C main.o转储(左边是-O2 ,右-O1 ): https://www.diffchecker.com/YAFmJkbN

nm -C grammar.o dump (left is -O2 , right -O1 ): https://www.diffchecker.com/6dUgMiEK nm -C grammar.o转储(左侧为-O2 ,右侧为-O1 ): https : //www.diffchecker.com/6dUgMiEK

nm -C utility.o dump (left is -O2 , right -O1 ): https://www.diffchecker.com/mXGf2Hx7 (no difference except addresses) nm -C utility.o转储(左侧为-O2 ,右侧为-O1 ): https : //www.diffchecker.com/mXGf2Hx7 (除了地址没有其他区别)

Initially I thought the problem is similar to c++ linker error: undefined references only on optimized build but I have no templates in source files, apart from what Boost might define in their macros. 最初,我认为问题与c ++链接器错误类似:未定义的引用仅在优化的构建上出现,但我在源文件中没有模板,除了Boost可能在其宏中定义的内容之外。

What can be the cause of the optimizer erroneously removing function definition? 优化器错误地删除函数定义的原因可能是什么?

Code: 码:

grammar.hpp

#pragma once
#include <boost/spirit/home/x3.hpp>
#include <tuple>
#include <utility>

namespace fs::parser
{

namespace x3 = boost::spirit::x3;
// symbols that denote filter's language constants
struct booleans_ : x3::symbols<bool>
{
    booleans_()
    {
        add
            ("True", true)
            ("False", false)
        ;
    }

};
const booleans_ booleans;

// some constants to aovid code duplication
constexpr auto keyword_boolean = "Boolean";
constexpr auto keyword_number  = "Number";
constexpr auto keyword_true    = "True";
constexpr auto keyword_false   = "False";
constexpr auto assignment_operator = '=';
constexpr auto newline_character   = '\n';

auto set_first = [](auto& ctx){ _val(ctx).first += _attr(ctx); };
auto set_second = [](auto& ctx){ _val(ctx).second += _attr(ctx); };

using x3::raw;
using x3::lexeme;
using x3::alpha;
using x3::alnum;
using x3::char_;
using x3::lit;

// comment - a line that starts with #
using comment_type = x3::rule<class comment_class>;
BOOST_SPIRIT_DECLARE(comment_type)

// identifier
using identifier_type = x3::rule<class identifier_class, std::string>;
BOOST_SPIRIT_DECLARE(identifier_type)

// whitespace
// Filter Spirit grammar skips any whitespace except newline character
using whitespace_type = x3::rule<class whitespace_class>;
BOOST_SPIRIT_DECLARE(whitespace_type)

// string
using string_type = x3::rule<class string_class, std::string>;
BOOST_SPIRIT_DECLARE(string_type)

// boolean definition: Boolean b = True
using constant_boolean_definition_type = x3::rule<class constant_boolean_definition_class, std::pair<std::string, bool>>;
BOOST_SPIRIT_DECLARE(constant_boolean_definition_type)

// number definition: Number n = 3
using constant_number_definition_type = x3::rule<class constant_number_definition_class, std::pair<std::string, int>>;
BOOST_SPIRIT_DECLARE(constant_number_definition_type)

// constants
using constant_definition_type = x3::rule<class constant_definition_class, boost::variant<constant_boolean_definition_type::attribute_type, constant_number_definition_type::attribute_type>>;
BOOST_SPIRIT_DECLARE(constant_definition_type)

// filter language consists of lines, of which every is a comment or some code
using code_line_type = x3::rule<class code_line_class, boost::optional<constant_definition_type::attribute_type>>;
BOOST_SPIRIT_DECLARE(code_line_type)

// the entire language grammar
using grammar_type = x3::rule<class grammar_class, std::vector<code_line_type::attribute_type>>;
BOOST_SPIRIT_DECLARE(grammar_type)

using skipper_type = whitespace_type;

// Boost Spirit recommends that this should be in a separate config.hpp file but
// in our case we need skipper type to be visible so we place configuration here
using iterator_type = std::string::const_iterator;
using context_type = x3::phrase_parse_context<skipper_type>::type;


}

namespace fs
{

parser::grammar_type grammar();
parser::skipper_type skipper();

}

grammar_def.hpp

#pragma once
#include "grammar.hpp"

namespace fs::parser
{

const comment_type comment = "comment";
const auto comment_def = lexeme['#' >> *(char_ - newline_character)];
BOOST_SPIRIT_DEFINE(comment)

const identifier_type identifier = "identifier";
const auto identifier_def = lexeme[(alpha | '_') >> *(alnum | '_')];
BOOST_SPIRIT_DEFINE(identifier)

const whitespace_type whitespace = "whitespace";
const auto whitespace_def = x3::space - newline_character;
BOOST_SPIRIT_DEFINE(whitespace)

const string_type string = "string";
const auto string_def = lexeme['"' >> +(char_ - '"') >> '"'];
BOOST_SPIRIT_DEFINE(string)

const constant_boolean_definition_type constant_boolean_definition = "Boolean definition";
const auto constant_boolean_definition_def = lit(keyword_boolean) >> identifier[set_first] >> lit(assignment_operator) >> booleans[set_second];
BOOST_SPIRIT_DEFINE(constant_boolean_definition)

const constant_number_definition_type constant_number_definition = "Number definition";
const auto constant_number_definition_def = lit(keyword_number) >> identifier[set_first] >> lit(assignment_operator) >> x3::int_[set_second];
BOOST_SPIRIT_DEFINE(constant_number_definition)

const constant_definition_type constant_definition = "value definition";
const auto constant_definition_def = constant_boolean_definition | constant_number_definition;
BOOST_SPIRIT_DEFINE(constant_definition)

const code_line_type code_line = "line";
const auto code_line_def = newline_character | comment | constant_definition;
BOOST_SPIRIT_DEFINE(code_line)

const grammar_type grammar = "code";
const auto grammar_def = *code_line;
BOOST_SPIRIT_DEFINE(grammar)

}

namespace fs
{

inline
parser::grammar_type grammar()
{
    return parser::grammar;
}

inline
parser::skipper_type skipper()
{
    return parser::whitespace;
}

}

grammar.cpp

#include "grammar_def.hpp"

namespace fs::parser
{

BOOST_SPIRIT_INSTANTIATE(grammar_type, iterator_type, context_type)

}

utility.hpp

#pragma once

#include <string>
#include <optional>

namespace fs::utility
{

bool file_exists(const char* path);
inline
bool file_exists(const std::string& path) { return file_exists(path.c_str()); }

std::optional<std::string> load_file(const char* file_path);
inline
std::optional<std::string> load_file(const std::string& file_path) { return load_file(file_path.c_str()); }

}

utility.cpp

#include "utility.hpp"
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>

namespace fs::utility
{

bool file_exists(const char* path)
{
    struct stat buffer;
    return stat(path, &buffer) == 0;
}

std::optional<std::string> load_file(const char* file_path)
{
    std::ifstream file(file_path);

    if (!file.good())
        return {};

    return std::string(std::istreambuf_iterator<char>{file}, {});
}

}

main.cpp

#include "utility.hpp"
#include "grammar.hpp"

#include <iostream>

bool parse(std::string const& source)
{
    namespace x3 = boost::spirit::x3;

    fs::parser::iterator_type it = source.begin();
    const fs::parser::iterator_type end = source.end();
    fs::parser::grammar_type::attribute_type data;

    const bool result = x3::phrase_parse(
        it,
        end,
        fs::grammar(),
        fs::skipper(),
        data
    );

    if (it != end) // fail if we did not get a full match
    {
        std::cout << "parse failure at pos: " << it - source.begin() << "\n";
        return false;
    }

    return result;
}


int main(int argc, char* argv[])
{
    if (argc < 2)
    {
        std::cout << "usage: " << "./program filepath\n";
        return -1;
    }

    if (parse(fs::utility::load_file(argv[1]).value()))
    {
        std::cout << "parse successful\n";
    }
    else
    {
        std::cout << "parse failure\n";
    }
}

I have managed to fix it myself. 我已经设法自己修复了。

Solution: add #include "grammar_def.hpp" to main.cpp . 解决方案:将#include "grammar_def.hpp"添加到main.cpp

What was happenning: BOOST_SPIRIT_DEFINE declares inline function templates. 发生了什么: BOOST_SPIRIT_DEFINE声明了内联函数模板。 All are stripped away if they are not immediately used in any given translation unit. 如果没有立即在所有给定的翻译单元中使用它们,则将所有内容剥离。 They need to be visible in the same TU where x3::phrase_parse is used or otherwise they will be considered unneeded by the compiler - hence undefined reference error. 它们必须在使用x3::phrase_parse的同一个TU中可见,否则编译器将认为它们是不必要的-因此未定义参考错误。

Edit: The source of the problem is a bug in Spirit. 编辑:问题的根源是精神中的错误。 This answer is just a workaround of TU mechanism. 这个答案只是TU机制的一种解决方法。 See other answer for the proper solution. 请参阅其他答案以获取适当的解决方案。

  1. The parser::grammar_type grammar() and parser::skipper_type skipper() are not exported (I got linker error about this, but you have not mention it in your question). parser::grammar_type grammar()parser::skipper_type skipper()不会导出(我得到了链接器错误,但是您没有在问题中提到它)。 Move them to grammar.cpp (and remove inline on them of course) to solve the issue. 将它们移至grammar.cpp (当然,并在其上删除inline )可解决该问题。

  2. Missing skipper instantiation (the only error message you have provided is about that). 缺少船长实例化(您提供的唯一错误消息是关于此的)。 Placing BOOST_SPIRIT_INSTANTIATE(whitespace_type, iterator_type, boost::spirit::x3::unused_type) into grammar.cpp should solve the problem, but... 配售BOOST_SPIRIT_INSTANTIATE(whitespace_type, iterator_type, boost::spirit::x3::unused_type)grammar.cpp应该解决的问题,但...

    But the error message will not gone, and that's most likely because of a bug in Spirit. 但是错误消息不会消失,这很可能是因为Spirit中的错误。 It instantiates parse_rule with an attribute type of the rule, and default is non-const unused_type , but it looks like somewhere at skipping it takes the attribute by const ref. 它使用规则的属性类型实例化parse_rule ,并且默认值为non-const unused_type ,但是看起来好像在跳过某个地方时,它使用const ref来获取属性。 To workaround this specify the skipper rule with a const attribute type x3::rule<class whitespace_class, boost::spirit::x3::unused_type const> . 要解决此问题,请使用const属性类型x3::rule<class whitespace_class, boost::spirit::x3::unused_type const>

The result https://gist.github.com/Kojoley/f7766422a5b6fd9e34b4b9d434325336 . 结果https://gist.github.com/Kojoley/f7766422a5b6fd9e34b4b9d434325336

Update : The PR https://github.com/boostorg/spirit/pull/437 will fix the issue for everyone. 更新 :PR https://github.com/boostorg/spirit/pull/437将为所有人解决此问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 带有g ++ -O2(或-Os,-O,-O1,...)的模板警告 - Warning for template with g++ -O2 ( or -Os, -O, -O1, … ) 是否是 gcc -O2 优化错误(与 -O1 的结果不同)? - Is it a gcc -O2 optimization bug (different result from -O1)? ICC中的-O3扰乱内在函数,使用-O1或-O2或相应的手动装配 - -O3 in ICC messes up intrinsics, fine with -O1 or -O2 or with corresponding manual assembly 为什么qmake在这种情况下会添加-O1和-O2优化标志? - Why does qmake add -O1 and -O2 optimization flags in this case? 具有不匹配的优化级别(-O3、-O2、-O1、-O0)的二进制文件是否会导致稳定性问题? - Does having binaries with mismatched optimization levels (-O3, -O2, -O1, -O0) cause stability issues? ICC中的-O2扰乱了汇编程序,ICC中的-O1很好,GCC / Clang中的所有优化都很好 - -O2 in ICC messes up assembler, fine with -O1 in ICC and all optimizations in GCC / Clang 启用优化(O1或O2或O3)时,程序被卡在pthread_spin_unlock语句中 - program is getting stuck at pthread_spin_unlock statement when optimizations (O1 or O2 or O3) are turned on bcc64优化-O1 vs -O2仍然比bcc32慢40%以上 - bcc64 optimizations -O1 vs -O2 still slower than bcc32 by 40% and more 是g ++ buggy?和clang ++呢?带有-O0和-O1选项的g ++编译代码表现不同,对于具有-O0和-O2的clang ++也是如此 - is g++ buggy ? and clang++ too ? g++ compiled code with -O0 and -O1 options behaves differently the same is true for clang++ with -O0 and -O2 boost::any_range 优化级别为 -O2 导致崩溃 - boost::any_range with optimization level -O2 causes crash
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM