【问题标题】:What is getOpcode in LLVM?LLVM 中的 getOpcode 是什么?
【发布时间】:2018-07-31 07:57:49
【问题描述】:

函数 getOpcode() 在 LLVM 代码生成器部分的 MCInstrDescMachineInstr 类中返回什么?我无法与机器的实际操作码联系起来。

例如,getOpcode() 函数为x86 中的RET 指令返回2515。然而,x86 中真正的操作码是C3(十进制的195)。

什么关系?

【问题讨论】:

  • 好吧,我的问题是它与我在示例中解释的英特尔操作码不匹配。另外,我正在与MachineInstr 合作,而不是 IR 的 Instruction
  • 文档说明了您需要知道的一切。操作码是枚举的一部分,它不是二进制文件中使用的操作码。
  • 它是内部值,某种枚举,不确定它是如何/在哪里定义的,我被 IDE 宠坏了,我可以在其中单击项目并进行定义,而且我没有 LLVM源安装。它与目标平台机器代码指令无关。您可以检查下一个 API 函数 getOpcodeName(检查 C 实现,例如如何使用“操作码”值)以获取类似 "add" 字符串的 Instruction::Add 值。

标签: assembly llvm code-generation opcode


【解决方案1】:

MCInstrDescMachineInstr 上的 getOpcode() 成员函数返回枚举值,该值标识指令代表 X86InstrInfo.td 中的哪个操作码。在其他后端,编号对应于该后端的 instr 信息,通常是一个名为 [BACKEND]InstrInfo.td 的文件。

您可以找到一个在许多 X86 后端传递中使用的示例,例如 X86ExpandPseudo.cpp 中处理尾调用返回的以下代码。

switch (MI.getOpcode()) {
default:
  return false;
case X86::TCRETURNdi:
case X86::TCRETURNdicc:
case X86::TCRETURNri:
case X86::TCRETURNmi:
case X86::TCRETURNdi64:
case X86::TCRETURNdi64cc:
case X86::TCRETURNri64:
case X86::TCRETURNmi64: {
  /* ... */
}
/* ... */
}

【讨论】:

    【解决方案2】:

    我最近在为同样的问题而苦苦挣扎,终于明白了。很久没有提出你的问题了,但我写这个是为了如果有人仍然困惑。

    正如你所说的getOpcode() 函数返回机器指令的枚举,而这些不是像@dtolnay 所说的那样持有{BackendArchName}InstrInfo.td。这些枚举将在 LLVM 库构建后生成,并且它与真正的十进制操作码编号无关,并且操作码的十进制枚举表示可能会因不同的 llvm 版本或库中有关后端的自定义更改而发生变化。这听起来很合理,因为它不会阻止自定义,您可以在后端添加或删除操作码,并且在库构建后,操作码的枚举会动态更改和生成。如果这些枚举在构建之前一直存在于库中的某个位置,例如,您可能无法为目标添加新的操作码或 这样的小细节可能很难改变。

    因此,构建后您可以在此文件夹中找到具有这些操作码表示的文件:

    {your-llvm-directory}/{your-llvm-build-directory}/lib/Target/{which-backend-target}

    这个.inc 文件包含操作码的枚举:

    {which-backend-target}GenInstrInfo.inc

    例如,在我的电脑中为 riscv 目标构建后,我可以在这里找到枚举:

    ~/llvm/llvm-project/build/lib/Target/RISCV/RISCVGenInstrInfo.inc

    这些枚举的一部分:

    /*===- TableGen'erated file -------------------------------------*- C++ -*-===*\
    |*                                                                            *|
    |* Target Instruction Enum Values and Descriptors                             *|
    |*                                                                            *|
    |* Automatically generated file, do not edit!                                 *|
    |*                                                                            *|
    \*===----------------------------------------------------------------------===*/
    
    #ifdef GET_INSTRINFO_ENUM
    #undef GET_INSTRINFO_ENUM
    namespace llvm {
    
    namespace RISCV {
      enum {
    
    // ........................................
    
        AND = 323,
        ANDI    = 324,
        ANDN    = 325,
        AUIPC   = 326,
        BDEP    = 327,
        BDEPW   = 328,
        BEQ = 329,
        BEXT    = 330,
        BEXTW   = 331,
        BFP = 332,
        BFPW    = 333,
        BGE = 334,
        BGEU    = 335,
        BLT = 336,
        BLTU    = 337,
        BMATFLIP    = 338,
        BMATOR  = 339,
        BMATXOR = 340,
        BNE = 341,
        CLMUL   = 342,
        CLMULH  = 343,
        CLMULHW = 344,
        CLMULR  = 345,
        CLMULRW = 346,
        CLMULW  = 347,
        CLZ = 348,
        CLZW    = 349,
        CMIX    = 350,
        CMOV    = 351,
        CRC32B  = 352,
    
    // ........................................
    
        CRC32H  = 358,
        CRC32W  = 359,
        CSRRC   = 360,
        CSRRCI  = 361,
        CSRRS   = 362,
        CSRRSI  = 363,
        CSRRW   = 364,
        CSRRWI  = 365,
        CTZ = 366,
        CTZW    = 367,
        C_ADD   = 368,
        C_ADDI  = 369,
        C_ADDI16SP  = 370,
        C_ADDI4SPN  = 371,
        C_ADDIW = 372,
        C_ADDI_HINT_IMM_ZERO    = 373,
        C_ADDI_HINT_X0  = 374,
        C_ADDI_NOP  = 375,
    // ........................................
    
      };
    
    } // end namespace RISCV
    } // end namespace llvm
    

    我只是举了一个 RISCV 的例子,它与 X86 等其他目标的过程相同。在您的示例中,RET2515 应该在 X86GenInstrInfo.inc 文件中作为枚举编号,如上所示。

    因为这些枚举是在构建之后生成的,所以在任何未构建的llvm library 中都找不到这些枚举是很正常的,比如在 github 中。

    额外信息:

    因为枚举编号会因不同的场景而改变,所以在使用 LLVM C++ API 时不应将它们用作数字,而应使用枚举名称来调用它们。例如:

    #include "RISCV.h" //You should include backend header for using enums.
    #include "llvm/MC/MCInst.h"
    
    //Some code here ... 
    
    if(MI->getOpcode() == RISCV::ADD){  // MI is machine instruction for example const MCInst *MI
    
    //Some code here...
    
    }
    
    //Some code here...
    
    

    上面的代码,你可以看到无论什么数字返回 getOpcode 函数,因为我可以用派生自(RISCV header->RISCV namespace->RISCV Opcode Enum Name)的名称来控制它。但是,如果您想直接获取返回的枚举编号的名称,您可以使用 getOpcodeName 函数,例如:

    #include "llvm/MC/MCInst.h"
    #include "llvm/MC/MCInstPrinter.h"
    #include "llvm/ADT/StringRef.h" //for llvm string variable
    
    //Some code here ... 
    
    StringRef opcodeName = IP.getOpcodeName(MI->getOpcode()); // IP is instruction printer for example MCInstPrinter &IP
    
    //Some code here ... 
    
    

    【讨论】:

      猜你喜欢
      • 2019-09-10
      • 1970-01-01
      • 2011-01-22
      • 2012-10-07
      • 2020-07-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多