如何在本地生成像干净组装一样的godbolt?

Waq*_*med 6 c++ assembly gcc clang compiler-explorer

我想在本地生成像编译器资源管理器这样的干净程序集。请注意,我阅读了如何从 GCC/clang 程序集输出中删除“噪音”?在尝试之前。与 Godbolt 相比,使用该方法的输出没有那么干净或密集,并且仍然有很多 asm 指令和未使用的标签。

如何在没有任何未使用的标签或指令的情况下获得干净的汇编输出?

Pet*_*des 9

作为记录,可以(而且显然不太难)设置本地安装 Matt Godbolt 的 Compiler Explorer 内容,因此您可以使用它来探索作为现有大型项目一部分的文件的 asm 输出及其#include依赖项和所有内容.

如果您已经有一些 asm 输出,@Waqar 的回答看起来很有用。或者,也许可以通过 node.js、IDK 从 Compiler Explorer 存储库单独使用该功能。

根据https://github.com/compiler-explorer/compiler-explorer(Matt的 repo)自述文件中的安装信息,您可以make在安装了 node.js 的机器上克隆它后简单地运行。


我还发现https://isocpp.org/blog/2017/10/cpp-weekly-episode-83-installing-compiler-explorerjason-turner可能有更多细节(或者在这一点上已经过时,IDK)。

我认为 Matt 在他的 CppCon 2017 演讲中也提到了使用 Compiler Explorer 的本地克隆(可能在最后回答了一个问题),“我的编译器最近为我做了什么?Unbolting the Compiler's Lid ”,并推荐使用它来处理使用大量代码的代码,这些代码#include很难进入https://godbolt.org/。(或用于封闭源代码)。


Waq*_*med 6

不久前,我在本地需要这样的东西,所以我写了一个小工具来使 asm 可读。

它尝试使用 C++ 本身“清理”并使“gcc”的“asm”输出可读。它执行类似于Compiler Explorer 的操作,并尝试删除所有指令和未使用的标签,从而使 asm 干净。仅使用标准库。

我应该提到的一些事情:

  • 只会用 gcc 和 clang
  • 仅使用 C++ 代码进行测试
  • -S -fno-asynchronous-unwind-tables -fno-dwarf2-cfi-asm -masm=intel,编译(-masm=如果你想要 AT&T asm ,请删除) AT&T 语法可能会起作用,但我没有对其进行太多测试。另外两个选项是删除.cfi指令。可以使用下面的代码处理它,但编译器本身在这方面做得更好。请参阅上面Peter Cordes 的回答。
  • 这个程序可以独立工作,但我强烈建议阅读这个SO 答案来调整你的 asm 输出,然后使用这个程序处理它以删除未使用的标签/指令等。
  • abi::__cxa_demangle() 用于拆线
  • 免责声明:这不是一个完美的解决方案,也没有经过广泛的测试。

用于清理 asm 的策略(可能有更好、更快、更有效的方法来做到这一点):

  1. 收集所有标签
  2. 逐行检查 asm 并检查标签是否已使用/未使用
  3. 如果标签未使用,它们将被删除
  4. 以'.'开头的每一行 被删除,除非它在某处被使用

更新 1:现在并非所有静态数据都被删除。

#include <algorithm>
#include <cxxabi.h>
#include <fstream>
#include <iostream>
#include <regex>
#include <string>
#include <sstream>
#include <unordered_map>

// trim from both ends (in place)
std::string_view trim(std::string_view s)
{
    s.remove_prefix(std::min(s.find_first_not_of(" \t\r\v\n"), s.size()));
    s.remove_suffix(std::min(s.size() - s.find_last_not_of(" \t\r\v\n") - 1, s.size()));
    return s;
}

static inline bool startsWith(const std::string_view s, const std::string_view searchString)
{
    return (s.rfind(searchString, 0) == 0);
}

std::string demangle(std::string &&asmText)
{
    int next = 0;
    int last = 0;
    while (next != -1) {
        next = asmText.find("_Z", last);
        //get token
        if (next != -1) {
            int tokenEnd = asmText.find_first_of(":,.@[]() \n", next + 1);
            int len = tokenEnd - next;
            std::string tok = asmText.substr(next, len);
            int status = 0;
            char* name = abi::__cxa_demangle(tok.c_str(), 0, 0, &status);
            if (status != 0) {
                std::cout << "Demangling of: " << tok << " failed, status: " << status << '\n';
                continue;
            }
            std::string demangledName{name};
            demangledName.insert(demangledName.begin(), ' ');
            asmText.replace(next, len, demangledName);
            free((void*)name);
        }
    }
    return std::move(asmText);
}

std::string clean_asm(const std::string& asmText)
{
    std::string output;
    output.reserve(asmText.length());
    std::stringstream s{asmText};

    //1. collect all the labels
    //2. go through the asm line by line and check if the labels are used/unused
    //3. if the labels are unused, they get deleted
    //4. every line beginning with '.' gets deleted, unless it is a used label

    std::regex exp {"^\\s*[_|a-zA-Z]"};
    
    std::regex directiveRe { "^\\s*\\..*$" };
    std::regex labelRe { "^\\.*[a-zA-Z]+[0-9]+:$" };
    std::regex hasOpcodeRe { "^\\s*[a-zA-Z]" };
    std::regex numericLabelsRe { "\\s*[0-9]:" };

    const std::vector<std::string> allowedDirectives =
    {
        ".string", ".zero", ".byte", ".value", ".long", ".quad", ".ascii"
    };

    //<label, used>
    std::unordered_map<std::string, bool> labels;

    //1
    std::string line;
    while (std::getline(s, line)) {
        if (std::regex_match(line, labelRe)) {
            trim(line);
            // remove ':'
            line = line.substr(0, line.size() - 1);
            labels[line] = false;
        }
    }

    s.clear();
    s.str(asmText);
    line = "";

    //2
    while (std::getline(s, line)) {
        if (std::regex_match(line, hasOpcodeRe)) {
            auto it = labels.begin();   
            for (; it != labels.end(); ++it) {
                if (line.find(it->first)) {
                    labels[it->first] = true;
                }
            }
        }
    }

    //remove false labels from labels hash-map
    for (auto it = labels.begin(); it != labels.end();) {
        if (it->second == false)
            it = labels.erase(it);
        else
            ++it;
    }

    s.clear();
    s.str(asmText);
    line = "";

    std::string currentLabel;

    //3
    while (std::getline(s, line)) {
        trim(line);

        if (std::regex_match(line, labelRe)) {
            auto l = line;
            l = l.substr(0, l.size() - 1);
            currentLabel = "";
            if (labels.find(l) != labels.end()) {
                currentLabel = line;
                output += line + "\n";
            }
            continue;
        }

        if (std::regex_match(line, directiveRe)) {
            //if we are in a label
            if (!currentLabel.empty()) {
                auto trimmedLine = trim(line);
                for (const auto& allowedDir : allowedDirectives) {
                    if (startsWith(trimmedLine, allowedDir)) {
                        output += line;
                        output += '\n';
                    }
                }
            }
            continue;
        }

        if (std::regex_match(line, numericLabelsRe)) {
            continue;
        }

        if (line == "endbr64") {
            continue;
        }

        if (line[line.size() - 1] == ':' || line.find(':') != std::string::npos) {
            currentLabel = line;
            output += line + '\n';
            continue;
        }

        line.insert(line.begin(), '\t');

        output += line + '\n';
    }

    return output;
}

int main(int argc, char* argv[])
{
    if (argc < 2) {
        std::cout << "Please provide more than asm filename you want to process.\n";
    }
    std::ifstream file(argv[1]);
    std::string output;
    if (file.is_open()) {
        std::cout << "File '" << argv[1] << "' is opened\n";
        std::string line;
        while (std::getline(file, line)) {
            output += line + '\n';
        }
    }

    output = demangle(std::move(output));
    output = clean_asm(output);

    std::string fileName = argv[1];
    auto dotPos = fileName.rfind('.');
    if (dotPos != std::string::npos)
        fileName.erase(fileName.begin() + dotPos, fileName.end());

    std::cout << "Asm processed. Saving as '"<< fileName <<".asm'";
    std::ofstream out;
    out.open(fileName + ".asm");
    out << output;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 删除每个“.”行将删除“.byte”和“.ascii”等指令。有时您确实想查看编译器发出的静态数据/常量。(但这更难,所以作为第一步,把它全部杀死就可以了。) (4认同)