提问者:小点点

如何在本地生成godbolt一样的清洁装配?


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

如何才能获得干净的程序集输出,而没有任何未使用的标签或指令?


共2个答案

匿名用户

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

它尝试使用C++本身来“清除”“gcc”的输出,并使“asm”可读。 它做了一些类似于编译器资源管理器的事情,试图删除所有的指令和未使用的标签,使asm变得干净。 只有标准库用于此。

我应该提的几件事:

  • 只使用gcc和clang
  • 仅用C++代码测试
  • -s-fno-asynchronous-unwind-tables-fno-dwarf2-cfi-asm-masm=intel编译,(如果需要AT&T asm,请删除-masm=)AT&T语法可能会起作用,但我没有测试太多。 另外两个选项是删除.cfi指令。 可以使用下面的代码来处理它,但是编译器本身在这方面做得更好。 请看上面Peter Cordes的回答。
  • abi::__cxa_demangle()用于解框
  • 免责声明:这不是一个完美的解决方案,而且还没有经过广泛测试。

用于清理asm的策略(可能有更好,更快,更有效的方法):

  1. 收集所有标签
  2. 逐行查看asm并检查标签是否已使用/未使用
  3. 如果标签未使用,它们将被删除
  4. 以“。”开头的每一行 获取删除,除非它是在某处使用的
#include <algorithm>
#include <cxxabi.h>
#include <iostream>
#include <fstream>
#include <regex>
#include <string>
#include <sstream>
#include <unordered_map>

// trim from start (in place)
static inline void ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
        return !std::isspace(ch);
    }));
}

// trim from end (in place)
static inline void rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
        return !std::isspace(ch);
    }).base(), s.end());
}

// trim from both ends (in place)
static inline void trim(std::string &s) {
    ltrim(s);
    rtrim(s);
}

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};

    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]:" };

    //<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 = "";

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

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

        if (std::regex_match(line, numericLabelsRe)) {
            continue;
        }
        //skip these
        if (line == "endbr64") {
            continue;
        }

       //don't indent labels
        if (line[line.size() - 1] == ':') {
            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 input asm file\n";
    }
    std::ifstream file("aboutdialog.s");
    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::ofstream out;
    out.open("out.asm");
    out << output;

    return 0;
}
  • 字符串trim()函数来自此SO应答

匿名用户

请注意,在本地安装Matt Godbolt的Compiler Explorer是可能的(而且显然不太难),因此您可以使用它来探索作为现有大型项目一部分的文件的asm输出,这些文件具有#include依赖项和所有内容。

如果你已经有了一些asm输出,@waqar的回答看起来很有用。 或者该功能可以通过node.js,idk从编译器资源管理器repo单独使用。

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

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

我想Matt在他的CppCon 2017关于编译器资源管理器的演讲中也提到了使用编译器资源管理器的本地克隆(也许是在结尾回答一个问题),“我的编译器最近为我做了什么? 打开编译器的盖子“,并建议使用它来玩那些使用了大量#include的代码,这些代码很难进入https://godbolt.org/。 (或封闭源代码)。