[llvm] IR2Builder tool for converting LLVM IR files into C++ IRBuilder API (PR #117129)

Craig Topper via llvm-commits llvm-commits at lists.llvm.org
Wed Mar 5 22:05:08 PST 2025


================
@@ -0,0 +1,1673 @@
+//===-- ir2builder.cpp - Transpiler from IR to builder API ------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a pass, which converts LLVM IR into llvm::IRBuilder
+// API in textual form.
+//
+// This tool can be used to simplify IR construction using IRBuilder by
+// writing the IR by hand and then converting it using this pass.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/IR/CallingConv.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/InlineAsm.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/ModuleSummaryIndex.h"
+#include "llvm/Support/AtomicOrdering.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cctype>
+#include <cmath>
+#include <cstdint>
+#include <sstream>
+
+using namespace llvm;
+
+cl::OptionCategory Ir2BCat("ir2builder Options");
+
+/// LLVM IR to convert.
+static cl::opt<std::string>
+    InputFilename(cl::Positional, cl::desc("<input .ll file>"), cl::init("-"));
+
+/// Output file for the generated C++ code.
+/// If not set then stdout will be used.
+static cl::opt<std::string> OutputFilename("o",
+                                           cl::desc("Override output filename"),
+                                           cl::value_desc("filename"),
+                                           cl::cat(Ir2BCat));
+
+/// Set this when you don't use `using namespace llvm` (don't forget :: at the
+/// end).
+static cl::opt<std::string> ScopePrefix(
+    "scope-prefix",
+    cl::desc(
+        "All generated calls to LLVM API will be prefixed with this scope. The "
+        "scope has to end with '::' (e.g.: '-scope-prefix=llvm::')"),
+    cl::cat(Ir2BCat), cl::init(""));
+
+/// Set this to your variable name for IRBuilder instance.
+static cl::opt<std::string> BuilderName(
+    "builder-name",
+    cl::desc("IRBuilder variable name that will be used in generated code"),
+    cl::cat(Ir2BCat), cl::init("Builder"));
+
+/// Set this to your LLVMContext variable name.
+static cl::opt<std::string> ContextName(
+    "context-name",
+    cl::desc("Context variable name that will be used in generated code"),
+    cl::cat(Ir2BCat), cl::init("Ctx"));
+
+/// Set this to your llvm::Module * name.
+static cl::opt<std::string> ModuleName(
+    "module-name",
+    cl::desc("Module variable name that will be used in generated code"),
+    cl::cat(Ir2BCat), cl::init("Mod"));
+
+/// Set this if you want custom data layout.
+static cl::opt<std::string> ClDataLayout("data-layout",
+                                         cl::desc("data layout string to use"),
+                                         cl::value_desc("layout-string"),
+                                         cl::init(""), cl::cat(Ir2BCat));
+
+/// This will generate fully compilable C++ program with main, which will
+/// use generated calls to create the original LLVM IR.
+/// Main purpose of this is to use it for testing and verification that
+/// generated program is correct and compilable.
+/// For example you can generate the code, run this program and then use
+/// llvm-diff to see if it matches the original:
+///    ```
+///    ./ir2builder code.ll --runnable > main.cpp &&
+///    g++ main.cpp `./llvm-conf --cxxflags --ldflags --system-libs --libs core`
+/// -I /llvm/include/ -o main &&
+///    ./main > generated.ll &&
+///    ./llvm-diff code.ll generated.ll
+///    ```
+static cl::opt<bool> GenerateRunnable(
+    "runnable", cl::desc("Generates whole cpp compilable program with main"),
+    cl::init(false), cl::cat(Ir2BCat));
+
+/// Disables verification of loaded llvm IR.
+/// Keep in mind that this will most likely result in C++ error as it probably
+/// won't be possible to create Builder calls for this.
+static cl::opt<bool>
+    DisableVerify("disable-verify", cl::Hidden,
+                  cl::desc("Do not run verifier on input LLVM (dangerous!)"),
+                  cl::cat(Ir2BCat));
+
+/// Sets the order of traversal for the LLVM IR.
+/// When enabled the traversal will be in reverse post order, which can handle
+/// when values are defined after (text-wise) their use.
+/// On the other hand using just linear traversal will also include parts that
+/// are outside of the graph (dead blocks).
+static cl::opt<bool>
+    UseRPO("use-rpo",
+           cl::desc("Traverses IR in reverse post order. This can help with "
+                    "\"was not declared\" errors"),
+           cl::init(true), cl::cat(Ir2BCat));
+
+/// \brief Transpiler from LLVM IR into IRBuilder API calls.
+/// The main purpose for this class is to hold variable counter and variable
+/// names for needed resources, such as LLVMContext.
+class IR2Builder {
+private:
+  unsigned long VarI = 0;
+  std::string LLVMPrefix;
+  std::string Builder;
+  std::string Ctx;
+  std::string ModName;
+
+  std::vector<std::string> PhiIncomings;
+
+  inline bool hasName(const Value *Op) {
+    return !isa<Constant>(Op) && !isa<InlineAsm>(Op);
+  }
+
+  void outputAttr(Attribute Att, raw_ostream &OS);
+
+  std::string getNextVar();
+
+  std::string asStr(GlobalValue::LinkageTypes Linkage);
+  std::string asStr(GlobalValue::ThreadLocalMode TLM);
+  std::string asStr(CmpInst::Predicate P);
+  std::string asStr(AtomicRMWInst::BinOp Op);
+  std::string asStr(AtomicOrdering AO);
+  std::string asStr(SyncScope::ID Sys);
+  std::string asStr(CallingConv::ID CC);
+  std::string asStr(ConstantRange &CR);
+
+  std::string asStr(const Value *Op);
+  std::string asStr(const Constant *Op);
+  std::string asStr(const Type *T);
+  std::string asStr(const InlineAsm *Op);
+  std::string asStr(const Metadata *Op);
+
+public:
+  IR2Builder()
+      : LLVMPrefix(ScopePrefix), Builder(BuilderName), Ctx(ContextName),
+        ModName(ModuleName) {}
+
+  /// Calls convert for all the functions in passed in module.
+  /// \param M Module to call convert over.
+  /// \param OS Stream to which output the builder calls.
+  void convert(Module &M, raw_ostream &OS);
+
+  /// Converts a function into IRBuilder API calls.
+  /// \param F Function to convert.
+  /// \param OS Stream to which output the builder calls.
+  void convert(Function &F, raw_ostream &OS);
+
+  /// Converts an instruction into IRBuilder API calls.
+  /// \param I Instruction to convert.
+  /// \param OS Stream to which output the builder calls.
+  /// \note Unsupported instructions or their operands should result
+  ///       in a TODO comment.
+  void convert(const Instruction *I, raw_ostream &OS);
+};
+
+std::string IR2Builder::getNextVar() { return "v0" + std::to_string(VarI++); }
+
+static std::string toStr(bool B) { return B ? "true" : "false"; }
+
+static std::string escape(std::string S) {
+  std::string Tmp;
+  raw_string_ostream OS(Tmp);
+  printEscapedString(S, OS);
+  return Tmp;
+}
+
+static std::string sanitize(std::string S) {
+  std::stringstream SS;
+  for (size_t I = 0; I < S.size(); ++I) {
+    if (!std::isalnum(S[I]))
+      SS << "_" << static_cast<unsigned>(S[I]) << "_";
+    else
+      SS << S[I];
+  }
+  return SS.str();
+}
+
+std::string IR2Builder::asStr(GlobalValue::LinkageTypes Linkage) {
+  std::string Link = LLVMPrefix + "GlobalValue::";
+
+  switch (Linkage) {
+  case GlobalValue::WeakODRLinkage:
+    return Link + "WeakODRLinkage";
+  case GlobalValue::LinkOnceODRLinkage:
+    return Link + "LinkOnceODRLinkage";
+  case GlobalValue::AvailableExternallyLinkage:
+    return Link + "AvailableExternallyLinkage";
+  case GlobalValue::WeakAnyLinkage:
+    return Link + "WeakAnyLinkage";
+  case GlobalValue::LinkOnceAnyLinkage:
+    return Link + "LinkOnceAnyLinkage";
+  case GlobalValue::CommonLinkage:
+    return Link + "CommonLinkage";
+  case GlobalValue::ExternalWeakLinkage:
+    return Link + "ExternalWeakLinkage";
+  case GlobalValue::ExternalLinkage:
+    return Link + "ExternalLinkage";
+  case GlobalValue::AppendingLinkage:
+    return Link + "AppendingLinkage";
+  case GlobalValue::InternalLinkage:
+    return Link + "InternalLinkage";
+  case GlobalValue::PrivateLinkage:
+    return Link + "PrivateLinkage";
+  default:
+    return "/* Unknown LinkageTypes (using value) */" + std::to_string(Linkage);
+  }
+}
+
+std::string IR2Builder::asStr(AtomicRMWInst::BinOp Op) {
+  switch (Op) {
+  case AtomicRMWInst::BinOp::Xchg:
+    return LLVMPrefix + "AtomicRMWInst::BinOp::Xchg";
+  case AtomicRMWInst::BinOp::Add:
+    return LLVMPrefix + "AtomicRMWInst::BinOp::Add";
+  case AtomicRMWInst::BinOp::Sub:
+    return LLVMPrefix + "AtomicRMWInst::BinOp::Sub";
+  case AtomicRMWInst::BinOp::And:
+    return LLVMPrefix + "AtomicRMWInst::BinOp::And";
+  case AtomicRMWInst::BinOp::Nand:
+    return LLVMPrefix + "AtomicRMWInst::BinOp::Nand";
+  case AtomicRMWInst::BinOp::Or:
+    return LLVMPrefix + "AtomicRMWInst::BinOp::Or";
+  case AtomicRMWInst::BinOp::Xor:
+    return LLVMPrefix + "AtomicRMWInst::BinOp::Xor";
+  case AtomicRMWInst::BinOp::Max:
+    return LLVMPrefix + "AtomicRMWInst::BinOp::Max";
+  case AtomicRMWInst::BinOp::Min:
+    return LLVMPrefix + "AtomicRMWInst::BinOp::Min";
+  case AtomicRMWInst::BinOp::UMax:
+    return LLVMPrefix + "AtomicRMWInst::BinOp::UMax";
+  case AtomicRMWInst::BinOp::UMin:
+    return LLVMPrefix + "AtomicRMWInst::BinOp::UMin";
+  case AtomicRMWInst::BinOp::FAdd:
+    return LLVMPrefix + "AtomicRMWInst::BinOp::FAdd";
+  case AtomicRMWInst::BinOp::FSub:
+    return LLVMPrefix + "AtomicRMWInst::BinOp::FSub";
+  case AtomicRMWInst::BinOp::FMax:
+    return LLVMPrefix + "AtomicRMWInst::BinOp::FMax";
+  case AtomicRMWInst::BinOp::FMin:
+    return LLVMPrefix + "AtomicRMWInst::BinOp::FMin";
+  case AtomicRMWInst::BinOp::UIncWrap:
+    return LLVMPrefix + "AtomicRMWInst::BinOp::UIncWrap";
+  case AtomicRMWInst::BinOp::UDecWrap:
+    return LLVMPrefix + "AtomicRMWInst::BinOp::UDecWrap";
+  default:
+    return "/* TODO: Unknown AtomicRMW operator (using value) */ " +
+           std::to_string(Op);
+  }
+}
+
+std::string IR2Builder::asStr(AtomicOrdering AO) {
+  switch (AO) {
+  case AtomicOrdering::NotAtomic:
+    return LLVMPrefix + "AtomicOrdering::NotAtomic";
+  case AtomicOrdering::Unordered:
+    return LLVMPrefix + "AtomicOrdering::Unordered";
+  case AtomicOrdering::Monotonic:
+    return LLVMPrefix + "AtomicOrdering::Monotonic";
+  case AtomicOrdering::Acquire:
+    return LLVMPrefix + "AtomicOrdering::Acquire";
+  case AtomicOrdering::Release:
+    return LLVMPrefix + "AtomicOrdering::Release";
+  case AtomicOrdering::AcquireRelease:
+    return LLVMPrefix + "AtomicOrdering::AcquireRelease";
+  case AtomicOrdering::SequentiallyConsistent:
+    return LLVMPrefix + "AtomicOrdering::SequentiallyConsistent";
+  default:
+    return "/* TODO: Unknown atomic ordering */";
+  }
+}
+
+std::string IR2Builder::asStr(CmpInst::Predicate P) {
+  switch (P) {
+  case CmpInst::Predicate::FCMP_FALSE:
+    return LLVMPrefix + "CmpInst::Predicate::FCMP_FALSE";
+  case CmpInst::Predicate::FCMP_OEQ:
+    return LLVMPrefix + "CmpInst::Predicate::FCMP_OEQ";
+  case CmpInst::Predicate::FCMP_OGT:
+    return LLVMPrefix + "CmpInst::Predicate::FCMP_OGT";
+  case CmpInst::Predicate::FCMP_OGE:
+    return LLVMPrefix + "CmpInst::Predicate::FCMP_OGE";
+  case CmpInst::Predicate::FCMP_OLT:
+    return LLVMPrefix + "CmpInst::Predicate::FCMP_OLT";
+  case CmpInst::Predicate::FCMP_OLE:
+    return LLVMPrefix + "CmpInst::Predicate::FCMP_OLE";
+  case CmpInst::Predicate::FCMP_ONE:
+    return LLVMPrefix + "CmpInst::Predicate::FCMP_ONE";
+  case CmpInst::Predicate::FCMP_ORD:
+    return LLVMPrefix + "CmpInst::Predicate::FCMP_ORD";
+  case CmpInst::Predicate::FCMP_UNO:
+    return LLVMPrefix + "CmpInst::Predicate::FCMP_UNO";
+  case CmpInst::Predicate::FCMP_UEQ:
+    return LLVMPrefix + "CmpInst::Predicate::FCMP_UEQ";
+  case CmpInst::Predicate::FCMP_UGT:
+    return LLVMPrefix + "CmpInst::Predicate::FCMP_UGT";
+  case CmpInst::Predicate::FCMP_UGE:
+    return LLVMPrefix + "CmpInst::Predicate::FCMP_UGE";
+  case CmpInst::Predicate::FCMP_ULT:
+    return LLVMPrefix + "CmpInst::Predicate::FCMP_ULT";
+  case CmpInst::Predicate::FCMP_ULE:
+    return LLVMPrefix + "CmpInst::Predicate::FCMP_ULE";
+  case CmpInst::Predicate::FCMP_UNE:
+    return LLVMPrefix + "CmpInst::Predicate::FCMP_UNE";
+  case CmpInst::Predicate::FCMP_TRUE:
+    return LLVMPrefix + "CmpInst::Predicate::FCMP_TRUE";
+  case CmpInst::Predicate::ICMP_EQ:
+    return LLVMPrefix + "CmpInst::Predicate::ICMP_EQ";
+  case CmpInst::Predicate::ICMP_NE:
+    return LLVMPrefix + "CmpInst::Predicate::ICMP_NE";
+  case CmpInst::Predicate::ICMP_UGT:
+    return LLVMPrefix + "CmpInst::Predicate::ICMP_UGT";
+  case CmpInst::Predicate::ICMP_UGE:
+    return LLVMPrefix + "CmpInst::Predicate::ICMP_UGE";
+  case CmpInst::Predicate::ICMP_ULT:
+    return LLVMPrefix + "CmpInst::Predicate::ICMP_ULT";
+  case CmpInst::Predicate::ICMP_ULE:
+    return LLVMPrefix + "CmpInst::Predicate::ICMP_ULE";
+  case CmpInst::Predicate::ICMP_SGT:
+    return LLVMPrefix + "CmpInst::Predicate::ICMP_SGT";
+  case CmpInst::Predicate::ICMP_SGE:
+    return LLVMPrefix + "CmpInst::Predicate::ICMP_SGE";
+  case CmpInst::Predicate::ICMP_SLT:
+    return LLVMPrefix + "CmpInst::Predicate::ICMP_SLT";
+  case CmpInst::Predicate::ICMP_SLE:
+    return LLVMPrefix + "CmpInst::Predicate::ICMP_SLE";
+  default:
+    return "/* TODO: Unknown CMP predicate (using value) */ " +
+           std::to_string(P);
+  }
+}
+
+std::string IR2Builder::asStr(SyncScope::ID Sys) {
+  if (Sys == SyncScope::System)
+    return LLVMPrefix + "SyncScope::System";
+  if (Sys == SyncScope::SingleThread)
+    return LLVMPrefix + "SyncScope::SingleThread";
+
+  return "/* TODO: Unknown SyncScope ID (using value) */ " +
+         std::to_string(Sys);
+}
+
+std::string IR2Builder::asStr(GlobalValue::ThreadLocalMode TLM) {
+  switch (TLM) {
+  case GlobalValue::ThreadLocalMode::NotThreadLocal:
+    return LLVMPrefix + "GlobalValue::ThreadLocalMode::NotThreadLocal";
+  case GlobalValue::ThreadLocalMode::GeneralDynamicTLSModel:
+    return LLVMPrefix + "GlobalValue::ThreadLocalMode::GeneralDynamicTLSModel";
+  case GlobalValue::ThreadLocalMode::LocalDynamicTLSModel:
+    return LLVMPrefix + "GlobalValue::ThreadLocalMode::LocalDynamicTLSModel";
+  case GlobalValue::ThreadLocalMode::InitialExecTLSModel:
+    return LLVMPrefix + "GlobalValue::ThreadLocalMode::InitialExecTLSModel";
+  case GlobalValue::ThreadLocalMode::LocalExecTLSModel:
+    return LLVMPrefix + "GlobalValue::ThreadLocalMode::LocalExecTLSModel";
+  default:
+    return "/* TODO: Unknown ThreadLocalMode (using value) */ " +
+           std::to_string(TLM);
+  }
+}
+
+std::string IR2Builder::asStr(CallingConv::ID CC) {
+  switch (CC) {
+  case CallingConv::C:
+    return LLVMPrefix + "CallingConv::C";
+  case CallingConv::Fast:
+    return LLVMPrefix + "CallingConv::Fast";
+  case CallingConv::Cold:
+    return LLVMPrefix + "CallingConv::Cold";
+  case CallingConv::GHC:
+    return LLVMPrefix + "CallingConv::GHC";
+  case CallingConv::HiPE:
+    return LLVMPrefix + "CallingConv::HiPE";
+  case CallingConv::AnyReg:
+    return LLVMPrefix + "CallingConv::AnyReg";
+  case CallingConv::PreserveMost:
+    return LLVMPrefix + "CallingConv::PreserveMost";
+  case CallingConv::PreserveAll:
+    return LLVMPrefix + "CallingConv::PreserveAll";
+  case CallingConv::Swift:
+    return LLVMPrefix + "CallingConv::Swift";
+  case CallingConv::CXX_FAST_TLS:
+    return LLVMPrefix + "CallingConv::CXX_FAST_TLS";
+  case CallingConv::Tail:
+    return LLVMPrefix + "CallingConv::Tail";
+  case CallingConv::CFGuard_Check:
+    return LLVMPrefix + "CallingConv::CFGuard_Check";
+  case CallingConv::SwiftTail:
+    return LLVMPrefix + "CallingConv::SwiftTail";
+  case CallingConv::PreserveNone:
+    return LLVMPrefix + "CallingConv::PreserveNone";
+  case CallingConv::FirstTargetCC:
+    return LLVMPrefix + "CallingConv::FirstTargetCC";
+  // CallingConv::X86_StdCall is the same as FirstTargetCC.
+  case CallingConv::X86_FastCall:
+    return LLVMPrefix + "CallingConv::X86_FastCall";
+  case CallingConv::ARM_APCS:
+    return LLVMPrefix + "CallingConv::ARM_APCS";
+  case CallingConv::ARM_AAPCS:
+    return LLVMPrefix + "CallingConv::ARM_AAPCS";
+  case CallingConv::ARM_AAPCS_VFP:
+    return LLVMPrefix + "CallingConv::ARM_AAPCS_VFP";
+  case CallingConv::MSP430_INTR:
+    return LLVMPrefix + "CallingConv::MSP430_INTR";
+  case CallingConv::X86_ThisCall:
+    return LLVMPrefix + "CallingConv::X86_ThisCall";
+  case CallingConv::PTX_Kernel:
+    return LLVMPrefix + "CallingConv::PTX_Kernel";
+  case CallingConv::PTX_Device:
+    return LLVMPrefix + "CallingConv::PTX_Device";
+  case CallingConv::SPIR_FUNC:
+    return LLVMPrefix + "CallingConv::SPIR_FUNC";
+  case CallingConv::SPIR_KERNEL:
+    return LLVMPrefix + "CallingConv::SPIR_KERNEL";
+  case CallingConv::Intel_OCL_BI:
+    return LLVMPrefix + "CallingConv::Intel_OCL_BI";
+  case CallingConv::X86_64_SysV:
+    return LLVMPrefix + "CallingConv::X86_64_SysV";
+  case CallingConv::Win64:
+    return LLVMPrefix + "CallingConv::Win64";
+  case CallingConv::X86_VectorCall:
+    return LLVMPrefix + "CallingConv::X86_VectorCall";
+  case CallingConv::DUMMY_HHVM:
+    return LLVMPrefix + "CallingConv::DUMMY_HHVM";
+  case CallingConv::DUMMY_HHVM_C:
+    return LLVMPrefix + "CallingConv::DUMMY_HHVM_C";
+  case CallingConv::X86_INTR:
+    return LLVMPrefix + "CallingConv::X86_INTR";
+  case CallingConv::AVR_INTR:
+    return LLVMPrefix + "CallingConv::AVR_INTR";
+  case CallingConv::AVR_SIGNAL:
+    return LLVMPrefix + "CallingConv::AVR_SIGNAL";
+  case CallingConv::AVR_BUILTIN:
+    return LLVMPrefix + "CallingConv::AVR_BUILTIN";
+  case CallingConv::AMDGPU_VS:
+    return LLVMPrefix + "CallingConv::AMDGPU_VS";
+  case CallingConv::AMDGPU_GS:
+    return LLVMPrefix + "CallingConv::AMDGPU_GS";
+  case CallingConv::AMDGPU_PS:
+    return LLVMPrefix + "CallingConv::AMDGPU_PS";
+  case CallingConv::AMDGPU_CS:
+    return LLVMPrefix + "CallingConv::AMDGPU_CS";
+  case CallingConv::AMDGPU_KERNEL:
+    return LLVMPrefix + "CallingConv::AMDGPU_KERNEL";
+  case CallingConv::X86_RegCall:
+    return LLVMPrefix + "CallingConv::X86_RegCall";
+  case CallingConv::AMDGPU_HS:
+    return LLVMPrefix + "CallingConv::AMDGPU_HS";
+  case CallingConv::MSP430_BUILTIN:
+    return LLVMPrefix + "CallingConv::MSP430_BUILTIN";
+  case CallingConv::AMDGPU_LS:
+    return LLVMPrefix + "CallingConv::AMDGPU_LS";
+  case CallingConv::AMDGPU_ES:
+    return LLVMPrefix + "CallingConv::AMDGPU_ES";
+  case CallingConv::AArch64_VectorCall:
+    return LLVMPrefix + "CallingConv::AArch64_VectorCall";
+  case CallingConv::AArch64_SVE_VectorCall:
+    return LLVMPrefix + "CallingConv::AArch64_SVE_VectorCall";
+  case CallingConv::WASM_EmscriptenInvoke:
+    return LLVMPrefix + "CallingConv::WASM_EmscriptenInvoke";
+  case CallingConv::AMDGPU_Gfx:
+    return LLVMPrefix + "CallingConv::AMDGPU_Gfx";
+  case CallingConv::M68k_INTR:
+    return LLVMPrefix + "CallingConv::M68k_INTR";
+  case CallingConv::AArch64_SME_ABI_Support_Routines_PreserveMost_From_X0:
+    return LLVMPrefix +
+           "CallingConv::AArch64_SME_ABI_Support_Routines_PreserveMost_From_X0";
+  case CallingConv::AArch64_SME_ABI_Support_Routines_PreserveMost_From_X2:
+    return LLVMPrefix +
+           "CallingConv::AArch64_SME_ABI_Support_Routines_PreserveMost_From_X2";
+  case CallingConv::AMDGPU_CS_Chain:
+    return LLVMPrefix + "CallingConv::AMDGPU_CS_Chain";
+  case CallingConv::AMDGPU_CS_ChainPreserve:
+    return LLVMPrefix + "CallingConv::AMDGPU_CS_ChainPreserve";
+  case CallingConv::M68k_RTD:
+    return LLVMPrefix + "CallingConv::M68k_RTD";
+  case CallingConv::GRAAL:
+    return LLVMPrefix + "CallingConv::GRAAL";
+  case CallingConv::ARM64EC_Thunk_X64:
+    return LLVMPrefix + "CallingConv::ARM64EC_Thunk_X64";
+  case CallingConv::ARM64EC_Thunk_Native:
+    return LLVMPrefix + "CallingConv::ARM64EC_Thunk_Native";
+  default:
+    return "/* Custom CC */" + std::to_string(CC);
+  }
+}
+
+std::string IR2Builder::asStr(const Type *T) {
+  std::string TCall;
+  if (auto IT = dyn_cast<IntegerType>(T)) {
+    switch (IT->getBitWidth()) {
+    case 8:
+    case 16:
+    case 32:
+    case 64:
+      TCall = formatv("getInt{0}Ty()", IT->getBitWidth());
+      break;
+    default:
+      TCall = formatv("getIntNTy({0})", IT->getBitWidth());
+      break;
+    }
+  } else if (T->isVoidTy())
+    TCall = "getVoidTy()";
+  else if (T->isFloatTy())
+    TCall = "getFloatTy()";
+  else if (T->isDoubleTy())
+    TCall = "getDoubleTy()";
+  else if (auto ST = dyn_cast<StructType>(T)) {
+    std::string Elements = LLVMPrefix + "ArrayRef<Type *>(";
+    if (ST->getNumElements() > 1)
+      Elements += "{";
+    bool First = true;
+    for (auto E : ST->elements()) {
+      if (!First)
+        Elements += ", ";
+      Elements += asStr(E);
+      First = false;
+    }
+    if (ST->getNumElements() > 1)
+      Elements += "}";
+    Elements += ")";
+    return formatv("{0}StructType::create({1}, {2})", LLVMPrefix, Ctx,
+                   Elements);
+  } else if (auto AT = dyn_cast<ArrayType>(T)) {
+    return formatv("{0}ArrayType::get({1}, {2})", LLVMPrefix,
+                   asStr(AT->getElementType()), AT->getArrayNumElements());
+  } else if (T->isBFloatTy())
+    TCall = "getBFloatTy()";
+  else if (auto VT = dyn_cast<VectorType>(T)) {
+    return formatv("{0}VectorType::get({1}, {0}ElementCount::get({2}, {3}))",
+                   LLVMPrefix, asStr(VT->getElementType()),
+                   VT->getElementCount().getKnownMinValue(),
+                   toStr(VT->getElementCount().isScalable()));
+  } else if (auto FT = dyn_cast<FunctionType>(T)) {
+    TCall = formatv("{0}FunctionType::get({1}, {{", LLVMPrefix,
+                    asStr(FT->getReturnType()));
+
+    bool IsFirst = true;
+    for (auto A : FT->params()) {
+      if (!IsFirst)
+        TCall += ", ";
+      TCall += asStr(A);
+      IsFirst = false;
+    }
+    TCall += "}, " + toStr(FT->isVarArg()) + ")";
+    return TCall;
+  } else if (T->isPointerTy())
+    TCall = "getPtrTy()";
+  else if (T->isHalfTy())
+    return formatv("{0}Type::getHalfTy({1})", LLVMPrefix, Ctx);
+  else if (T->isBFloatTy())
+    return formatv("{0}Type::getBFloatTy({1})", LLVMPrefix, Ctx);
+  else if (T->isX86_FP80Ty())
+    return formatv("{0}Type::getX86_FP80Ty({1})", LLVMPrefix, Ctx);
+  else if (T->isFP128Ty())
+    return formatv("{0}Type::getFP128Ty({1})", LLVMPrefix, Ctx);
+  else if (T->isPPC_FP128Ty())
+    return formatv("{0}Type::getPPC_FP128Ty({1})", LLVMPrefix, Ctx);
+  else if (T->isX86_AMXTy())
+    return formatv("{0}Type::getX86_AMXTy({1})", LLVMPrefix, Ctx);
+  else if (T->isLabelTy())
+    return formatv("{0}Type::getLabelTy({1})", LLVMPrefix, Ctx);
+  else if (T->isMetadataTy())
+    return formatv("{0}Type::getMetadataTy({1})", LLVMPrefix, Ctx);
+  else if (T->isTokenTy())
+    return formatv("{0}Type::getTokenTy({1})", LLVMPrefix, Ctx);
+  else
+    return "/* TODO: Unknown type */";
+
+  return Builder + "." + TCall;
+}
+
+std::string IR2Builder::asStr(const Constant *C) {
+  if (auto CI = dyn_cast<ConstantInt>(C)) {
+    // TODO: Sign has to be determined.
+    auto CVal = CI->getValue();
+    return formatv("{0}ConstantInt::get({1}, {2})", LLVMPrefix,
+                   asStr(C->getType()), CVal.getSExtValue());
+  }
+  if (auto CF = dyn_cast<ConstantFP>(C)) {
+    auto CVal = CF->getValue();
+    double DVal = CVal.convertToDouble();
+    std::string Val = std::to_string(DVal);
+    if (std::isnan(DVal) || std::isinf(DVal))
+      Val = "\"" + Val + "\"";
+    // TODO: Handle double to string conversion to include all digits.
+    return formatv("{0}ConstantFP::get({1}, {2})", LLVMPrefix,
+                   asStr(C->getType()), Val);
+  }
+  if (auto AT = dyn_cast<ConstantAggregate>(C)) {
+    std::string Values;
+    bool First = true;
+    for (unsigned I = 0; I < AT->getNumOperands(); ++I) {
+      if (!First)
+        Values += ", ";
+      Values += asStr(AT->getOperand(I));
+      First = false;
+    }
+
+    std::string ClassName;
+    if (isa<ConstantArray>(C)) {
+      ClassName = "ConstantArray";
+      Values = formatv(
+          "{0}ArrayRef<{0}Constant *>({1}{2}{3})", LLVMPrefix,
+          (AT->getNumOperands() > 1 ? std::string("{") : std::string("")),
+          Values,
+          (AT->getNumOperands() > 1 ? std::string("}") : std::string("")));
+    } else if (isa<ConstantStruct>(C))
+      ClassName = "ConstantStruct";
+    else if (isa<ConstantVector>(C)) {
+      Values = "{" + Values + "}";
+      // ConstantVector does not take type as 1st arg.
+      return formatv("{0}ConstantVector::get({1})", LLVMPrefix, Values);
+    } else
+      return "/* TODO: Unknown aggregate constant */";
+
+    assert(!ClassName.empty() && "Class name not set");
+    return formatv("{0}{1}::get({2}, {3})", LLVMPrefix, ClassName,
+                   asStr(C->getType()), Values);
+  }
+  if (auto CDS = dyn_cast<ConstantDataSequential>(C)) {
+    std::string Values;
+    std::string ClassName;
+    std::string ElemTy = "/* TODO */";
+    if (isa<ConstantDataArray>(C))
+      ClassName = "ConstantDataArray";
+    else if (isa<ConstantDataVector>(C))
+      ClassName = "ConstantDataVector";
+    else
+      return "/* TODO: Unknown data sequential constant */";
+    if (CDS->isString()) {
+      Values = "";
+      bool First = true;
+      for (auto a : CDS->getAsString().str()) {
+        if (First) {
+          Values += std::to_string(static_cast<uint8_t>(a));
+          First = false;
+        } else {
+          Values += ", " + std::to_string(static_cast<uint8_t>(a));
+        }
+      }
+      assert(!ClassName.empty() && "Class name not set");
+      return formatv("{0}{1}::get({2}, {0}ArrayRef<uint8_t>({3}{4}{5}))",
+                     LLVMPrefix, ClassName, Ctx, "{", Values, "}");
+    }
+    if (CDS->isCString()) {
+      Values = "";
+      bool First = true;
+      for (auto A : CDS->getAsCString().str()) {
+        if (First) {
+          Values += std::to_string(static_cast<uint8_t>(A));
+          First = false;
+        } else {
+          Values += ", " + std::to_string(static_cast<uint8_t>(A));
+        }
+      }
+      assert(!ClassName.empty() && "Class name not set");
+      return formatv("{0}{1}::get({2}, {0}ArrayRef<uint8_t>({3}{4}{5}))",
+                     LLVMPrefix, ClassName, Ctx, "{", Values, "}");
+    } else {
+      Type *ElemT = CDS->getElementType();
+      if (ElemT->isIntegerTy()) {
+        // There can be only 8, 16, 32 or 64 ints in ConstantDataVector.
+        ElemTy = "uint" + std::to_string(ElemT->getIntegerBitWidth()) + "_t";
+      } else if (ElemT->isDoubleTy()) {
+        ElemTy = "double";
+      } else if (ElemT->isFloatTy()) {
+        ElemTy = "float";
+      }
+      Values = formatv("{0}ArrayRef<{1}>(", LLVMPrefix, ElemTy);
+      if (CDS->getNumElements() > 1)
+        Values += "{";
+      bool First = true;
+      for (unsigned I = 0; I < CDS->getNumElements(); ++I) {
+        if (!First)
+          Values += ", ";
+        if (ElemT->isIntegerTy())
+          Values += std::to_string(CDS->getElementAsInteger(I));
+        else if (ElemT->isDoubleTy())
+          Values += std::to_string(CDS->getElementAsDouble(I));
+        else if (ElemT->isFloatTy())
+          Values += std::to_string(CDS->getElementAsFloat(I));
+        else
+          return "/* Unknown type in data sequential constant */";
+        First = false;
+      }
+      if (CDS->getNumElements() > 1)
+        Values += "}";
+      Values += ")";
+    }
+
+    return formatv("{0}{1}::get({2}, {3})", LLVMPrefix, ClassName, Ctx, Values);
+  }
+  if (isa<ConstantAggregateZero>(C))
+    return formatv("{0}ConstantAggregateZero::get({1})", LLVMPrefix,
+                   asStr(C->getType()));
+  if (isa<PoisonValue>(C))
+    return formatv("{0}PoisonValue::get({1})", LLVMPrefix, asStr(C->getType()));
+  if (isa<UndefValue>(C))
+    return formatv("{0}UndefValue::get({1})", LLVMPrefix, asStr(C->getType()));
+  if (auto ba = dyn_cast<BlockAddress>(C))
+    return formatv("{0}BlockAddress::get({1}, {2})", LLVMPrefix,
+                   asStr(ba->getFunction()), asStr(ba->getBasicBlock()));
+  if (isa<ConstantPointerNull>(C))
+    return formatv("{0}ConstantPointerNull::get({1})", LLVMPrefix,
+                   asStr(C->getType()));
+  if (auto CTN = dyn_cast<ConstantTargetNone>(C)) {
+    auto CTNType = CTN->getType();
+
+    std::string TypeStr = "{";
+    bool First = true;
+    for (unsigned I = 0; I < CTNType->getNumTypeParameters(); ++I) {
+      if (!First)
+        TypeStr += ", ";
+      TypeStr += asStr(CTNType->getTypeParameter(I));
+      First = false;
+    }
+    TypeStr += "}";
+
+    std::string IntsStr = "{";
+    First = true;
+    for (unsigned I = 0; I < CTNType->getNumIntParameters(); ++I) {
+      if (!First)
+        IntsStr += ", ";
+      IntsStr += std::to_string(CTNType->getIntParameter(I));
+      First = false;
+    }
+    IntsStr += "}";
+
+    return formatv("{0}ConstantTargetNone::get({0}TargetExtType::get({1}, "
+                   "\"{2}\", {3}, {4}))",
+                   LLVMPrefix, Ctx, escape(CTNType->getName().str()), TypeStr,
+                   IntsStr);
+  }
+  if (isa<ConstantTokenNone>(C)) {
+    return formatv("{0}ConstantTokenNone::get({1})", LLVMPrefix, Ctx);
+  }
+  if (auto CE = dyn_cast<ConstantExpr>(C)) {
+    (void)CE;
+    return "/* TODO: ConstantExpr creation */";
+    // TODO: Dunno how to create this... Fails either on out of range or
+    // even on incorrect opcode.
+    // return LLVMPrefix + "ConstantExpr::get(" +
+    // std::to_string(CE->getOpcode()) + ", " +
+    //       asStr(CE->getOperand(0)) + ", " + asStr(CE->getOperand(1)) + ")";
+  }
+  if (auto CE = dyn_cast<ConstantPtrAuth>(C)) {
+    (void)CE;
+    return "/* TODO: ConstantPtrAuth value creation */";
+  }
+  if (auto CE = dyn_cast<DSOLocalEquivalent>(C)) {
+    (void)CE;
+    return "/* TODO: DSOLocalEquivalent value creation */";
+  }
+  if (auto CE = dyn_cast<NoCFIValue>(C)) {
+    (void)CE;
+    return "/* TODO: NoCFIValue value creation */";
+  }
+  if (auto CE = dyn_cast<GlobalValue>(C)) {
+    // This should not really happen as asStr for Value should be always called.
+    return asStr(dyn_cast<Value>(CE));
+  }
+
+  return "/* TODO: Constant creation */";
+}
+
+std::string IR2Builder::asStr(const InlineAsm *Op) {
+  auto GetAsmDialect = [LLVMPrefix = LLVMPrefix](InlineAsm::AsmDialect D) {
+    switch (D) {
+    case InlineAsm::AsmDialect::AD_ATT:
+      return LLVMPrefix + "InlineAsm::AsmDialect::AD_ATT";
+    case InlineAsm::AsmDialect::AD_Intel:
+      return LLVMPrefix + "InlineAsm::AsmDialect::AD_Intel";
+    default:
+      return "/* TODO: Unknown AsmDialect (using value) */" + std::to_string(D);
+    }
+  };
+
+  return formatv("{0}InlineAsm::get({1}, \"{2}\", \"{3}\", {4}, {5}, {6}, {7})",
+                 asStr(Op->getFunctionType()), escape(Op->getAsmString()),
+                 escape(Op->getConstraintString()), toStr(Op->hasSideEffects()),
+                 toStr(Op->isAlignStack()), GetAsmDialect(Op->getDialect()),
+                 toStr(Op->canThrow()));
+}
+
+std::string IR2Builder::asStr(const Metadata *Op) {
+  if (auto MDN = dyn_cast<MDNode>(Op)) {
+    std::string Args = "{";
+    bool First = true;
+    for (unsigned I = 0; I < MDN->getNumOperands(); ++I) {
+      if (!First)
+        Args += ", ";
+      Args += asStr(MDN->getOperand(I));
+      First = false;
+    }
+    Args += "}";
+    return formatv("{0}MDNode::get({1}, {2})", LLVMPrefix, Ctx, Args);
+  }
+  if (auto VAM = dyn_cast<ValueAsMetadata>(Op))
+    return formatv("{0}ValueAsMetadata::get({1})", LLVMPrefix,
+                   asStr(VAM->getValue()));
+  if (auto MDS = dyn_cast<MDString>(Op))
+    return formatv("{0}MDString::get({1}, \"{2}\")", LLVMPrefix, Ctx,
+                   escape(MDS->getString().str()));
+
+  return "/* TODO: Metadata creation */";
+}
+
+// This is a DEBUG function in Value, so it's copied here for NDEBUG as well.
+static std::string getNameOrAsOperand(const Value *V) {
+  if (!V->getName().empty())
+    return std::string(V->getName());
+
+  std::string BBName;
+  raw_string_ostream OS(BBName);
+  V->printAsOperand(OS, false);
+  return OS.str();
+}
+
+std::string IR2Builder::asStr(const Value *Op) {
+  if (!Op)
+    return "nullptr";
+  if (isa<Constant>(Op) && !isa<GlobalValue>(Op))
+    return asStr(dyn_cast<Constant>(Op));
+  if (auto InA = dyn_cast<InlineAsm>(Op))
+    return asStr(InA);
+  if (auto Mtd = dyn_cast<MetadataAsValue>(Op)) {
+    return formatv("{0}MetadataAsValue::get({1}, {2})", LLVMPrefix, Ctx,
+                   asStr(Mtd->getMetadata()));
+  }
+  std::string OpName = getNameOrAsOperand(Op);
+  if (OpName[0] == '%')
+    OpName.erase(OpName.begin());
+  std::string Pref = "v_";
+  if (isa<GlobalValue>(Op))
+    Pref = "g_";
+  return Pref + sanitize(OpName);
+}
+
+std::string getBinArithOp(std::string Name, std::string Op1, std::string Op2,
+                          const Instruction *I) {
+  return formatv("Create{0}({1}, {2}, \"\", {3}, {4})", Name, Op1, Op2,
+                 toStr(I->hasNoUnsignedWrap()), toStr(I->hasNoSignedWrap()));
+}
+
+std::string getFPBinArithOp(std::string Name, std::string Op1, std::string Op2,
+                            const Instruction *I) {
+  return formatv("Create{0}({1}, {2})", Name, Op1, Op2);
+  // TODO: Handle FPMathTag.
+}
+
+std::string IR2Builder::asStr(ConstantRange &CR) {
+  auto NumBitsStr = std::to_string(CR.getBitWidth());
+  auto Lower = std::to_string(CR.getLower().getLimitedValue());
+  auto Upper = std::to_string(CR.getUpper().getLimitedValue());
+  return formatv(
+      "{0}ConstantRange(APInt({1}, {2}, true), APInt({1}, {3}, true))",
+      LLVMPrefix, NumBitsStr, Lower, Upper);
+}
+
+void IR2Builder::outputAttr(Attribute Att, raw_ostream &OS) {
+  // TODO: Handle special cases detected using "has" methods
+  // see Attribute::getAsString(bool InAttrGrp).
+  if (Att.isStringAttribute()) {
+    OS << LLVMPrefix << "Attribute::get(" << Ctx << ", \""
+       << Att.getKindAsString() << "\"";
+    auto Val = Att.getValueAsString();
+    if (Val.empty()) {
+      OS << ")";
+    } else {
+      OS << ", \"";
+      printEscapedString(Val, OS);
+      OS << "\")";
+    }
+  } else if (Att.isIntAttribute()) {
+    OS << LLVMPrefix << "Attribute::get(" << Ctx << ", (" << LLVMPrefix
+       << "Attribute::AttrKind)" << std::to_string(Att.getKindAsEnum())
+       << ", static_cast<uint64_t>(" << Att.getValueAsInt() << "))";
+  } else if (Att.isEnumAttribute()) {
+    OS << LLVMPrefix << "Attribute::get(" << Ctx << ", \""
+       << Att.getNameFromAttrKind(Att.getKindAsEnum()) << "\")";
+  } else if (Att.isTypeAttribute()) {
+    OS << LLVMPrefix << "Attribute::get(" << Ctx << ", (" << LLVMPrefix
+       << "Attribute::AttrKind)" << std::to_string(Att.getKindAsEnum()) << ", "
+       << asStr(Att.getValueAsType()) << ")";
+  } else if (Att.isConstantRangeAttribute()) {
+    auto CR = Att.getValueAsConstantRange();
+    OS << LLVMPrefix << "Attribute::get(" << Ctx << ", (" << LLVMPrefix
+       << "Attribute::AttrKind)" << std::to_string(Att.getKindAsEnum()) << ", "
+       << asStr(CR) << ")";
+  } else if (Att.isConstantRangeListAttribute()) {
+    std::string Args = "{";
+    bool First = true;
+    for (auto CR : Att.getValueAsConstantRangeList()) {
+      if (!First)
+        Args += ", ";
+      Args += asStr(CR);
+      First = false;
+    }
+    Args += "}";
+    OS << LLVMPrefix << "Attribute::get(" << Ctx << ", (" << LLVMPrefix
+       << "Attribute::AttrKind)" << std::to_string(Att.getKindAsEnum()) << ", "
+       << LLVMPrefix << Args << ")";
+  } else {
+    OS << "/* TODO: Attribute creation */";
+  }
+}
+
+void IR2Builder::convert(Module &M, raw_ostream &OS) {
+  // Prologue.
+  if (GenerateRunnable) {
+    // Top comment.
+    OS << "// This file was autogenerated using ir2builder tool\n";
+
+    // Includes.
+    // Currently we include all possibly needed files as this is done
+    // before the conversion of functions.
+    OS << R"(
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/CallingConv.h"
+#include "llvm/IR/ConstantRange.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/InlineAsm.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Type.h"
+#include "llvm/Support/AtomicOrdering.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/SourceMgr.h"
+#include <cstdint>
+
+using namespace llvm;
+
+int main(int argc, char** argv) {
+    InitLLVM X(argc, argv);
+)";
+
+    // Used objects and variables.
+    OS << "    LLVMContext " << Ctx << ";\n"
+       << "    Module *" << ModName
+       << "= new Module(\"ir2builder generated module\", " << Ctx << ");\n"
+       << "    IRBuilder<> " << Builder << "(" << Ctx << ");\n";
+
+    // Globals generation.
+    for (auto &G : M.globals()) {
+      std::string InitVal = "nullptr";
+      if (G.hasInitializer()) {
+        // In case of BlockAddress initializer we need to declare the function
+        // and basic block so it can be used.
+        if (auto BA = dyn_cast<BlockAddress>(G.getInitializer())) {
+          OS << formatv("auto {0} = {1}->getOrInsertFunction(\"{2}\", {3});\n",
+                        asStr(BA->getFunction()), ModName,
+                        BA->getFunction()->getName(),
+                        asStr(BA->getFunction()->getFunctionType()));
+          // Extract basic block.
+          // TODO: The function might need to be created in here
+          // since the basic blocks don't exist yet.
+          OS << "// TODO: Basic block extraction\n";
+        }
+        InitVal = asStr(G.getInitializer());
+      }
+
+      std::string GName = asStr(&G);
+      OS << formatv("auto {0} = new {1}GlobalVariable(*{2}, {3}, {4}, {5}, "
+                    "{6}, \"{7}\", nullptr, {8}, {9}, {10});\n",
+                    GName, LLVMPrefix, ModName, asStr(G.getValueType()),
+                    toStr(G.isConstant()), asStr(G.getLinkage()), InitVal,
+                    escape(G.getName().str()), asStr(G.getThreadLocalMode()),
+                    std::to_string(G.getAddressSpace()),
+                    toStr(G.isExternallyInitialized()));
+    }
+  }
+
+  // Function generation.
+  for (Function &F : M) {
+    if (!F.isDeclaration())
+      convert(F, OS);
+  }
+
+  // Epilogue.
+  if (GenerateRunnable) {
+    // Print generated code.
+    OS << "    " << ModName << "->print(outs(), nullptr);\n";
+    // Cleanup.
+    OS << "    delete " << ModName << ";\n"
+       << "    return 0;\n}\n";
+  }
+}
+
+void IR2Builder::convert(Function &F, raw_ostream &OS) {
+  PhiIncomings.clear();
+  // Function.
+  OS << "{\n\n";
+  auto FDecl = asStr(&F);
+  OS << formatv(
+      "{0}Function *{1} = {0}Function::Create({2}, {3}, \"{4}\", {5});\n\n",
+      LLVMPrefix, FDecl, asStr(F.getFunctionType()), asStr(F.getLinkage()),
+      F.getName(), ModName);
+
+  // Set attributes.
+  if (F.getCallingConv() != CallingConv::C) { // C is default.
+    OS << FDecl << "->setCallingConv(" + asStr(F.getCallingConv()) << ");\n";
+  }
+  AttributeList AL = F.getAttributes();
+  // TODO: Handle attributes with values.
+  if (AL.hasFnAttrs()) {
+    for (auto Att : AL.getFnAttrs()) {
+      OS << FDecl << "->addFnAttr(";
+      outputAttr(Att, OS);
+      OS << ");\n";
+    }
+  }
+  if (AL.hasRetAttrs()) {
+    for (auto Att : AL.getRetAttrs()) {
+      OS << FDecl << "->addRetAttr(";
+      outputAttr(Att, OS);
+      OS << ");\n";
+    }
+  }
+  for (size_t I = 0; I < F.arg_size(); ++I) {
+    if (AL.hasParamAttrs(I)) {
+      for (auto Att : AL.getAttributes(I + 1)) {
+        OS << FDecl << "->addParamAttr(" << I << ", ";
+        outputAttr(Att, OS);
+        OS << ");\n";
+      }
+    }
+  }
+
+  // Save arguments into variables for easy access.
+  for (const auto &[I, Arg] : enumerate(F.args())) {
+    OS << formatv("auto {0} = {1}->getArg({2});\n", asStr(&Arg), FDecl, I);
+  }
+
+  if (UseRPO) {
+    ReversePostOrderTraversal<Function *> RPOT(&F);
+
+    // Basic block declaration in order.
+    for (BasicBlock *BB : RPOT) {
+      OS << formatv(
+          "{0}BasicBlock *{1} = {0}BasicBlock::Create({2}, \"{3}\", {4});\n",
+          LLVMPrefix, asStr(BB), Ctx, BB->getName(), FDecl);
+    }
+
+    OS << "\n";
+
+    for (auto *BB : RPOT) {
+      OS << Builder << "."
+         << "SetInsertPoint(" << asStr(BB) << ");\n";
+
+      for (Instruction &Inst : *BB)
+        convert(&Inst, OS);
+
+      OS << "\n";
+    }
+  } else {
+    for (BasicBlock &BB : F) {
+      OS << formatv(
+          "{0}BasicBlock *{1} = {0}BasicBlock::Create({2}, \"{3}\", {4});\n",
+          LLVMPrefix, asStr(&BB), Ctx, BB.getName(), FDecl);
+    }
+
+    OS << "\n";
+
+    for (auto &BB : F) {
+      OS << Builder << "."
+         << "SetInsertPoint(" << asStr(&BB) << ");\n";
+
+      for (Instruction &Inst : BB)
+        convert(&Inst, OS);
+
+      OS << "\n";
+    }
+  }
+
+  // Output incoming values assignment into phis, this is needed as they
+  // might refer to a value not yet defined in the time of phi definition.
+  for (auto Line : PhiIncomings) {
+    OS << Line;
+  }
+
+  OS << "}\n";
+}
+
+void IR2Builder::convert(const Instruction *I, raw_ostream &OS) {
+  std::string Call;
+
+  std::string Op1 = "/* TODO */";
+  std::string Op2 = "/* TODO */";
+  std::string Op3 = "/* TODO */";
+
+  if (I->getNumOperands() > 0)
+    Op1 = asStr(I->getOperand(0));
+  if (I->getNumOperands() > 1)
+    Op2 = asStr(I->getOperand(1));
+  if (I->getNumOperands() > 2)
+    Op3 = asStr(I->getOperand(2));
+
+  switch (I->getOpcode()) {
+  case Instruction::Ret: {
+    if (I->getNumOperands() == 0)
+      Call = "CreateRetVoid()";
+    else
+      Call = "CreateRet(" + Op1 + ")";
+  } break;
+  case Instruction::Br: {
+    const BranchInst *BI = dyn_cast<BranchInst>(I);
+    if (BI->isUnconditional()) {
+      Call = formatv("CreateBr({0})", Op1);
+    } else {
+      Call = formatv("CreateCondBr({0}, {1}, {2})", Op1, Op3, Op2);
+    }
+  } break;
+  case Instruction::Switch: {
+    auto SwI = dyn_cast<SwitchInst>(I);
+    Call = formatv("CreateSwitch({0}, {1}, {2})", Op1, Op2,
+                   std::to_string(SwI->getNumCases()));
+
+    std::string SwVar = getNextVar();
+    // No need to save temporary var into symTable.
+    OS << formatv("auto {0} = {1}.{2};\n", SwVar, Builder, Call);
+
+    for (auto C : SwI->cases()) {
+      OS << formatv("{0}->addCase({1}, {2});\n", SwVar, asStr(C.getCaseValue()),
+                    asStr(C.getCaseSuccessor()));
+    }
+    return;
+  } break;
+  case Instruction::IndirectBr: {
+    auto InbrI = dyn_cast<IndirectBrInst>(I);
+    Call =
+        formatv("CreateIndirectBr({0}, {1})", Op1, InbrI->getNumDestinations());
+    std::string InbrVar = getNextVar();
+    OS << formatv("auto {0} = {1}.{2};\n", InbrVar, Builder, Call);
+
+    for (auto C : InbrI->successors()) {
+      OS << InbrVar << "->addDestination(" << asStr(C) << ");\n";
+    }
+  } break;
+  case Instruction::Invoke: {
+    auto InvI = dyn_cast<InvokeInst>(I);
+    std::string Args = "{";
+    bool First = true;
+    for (unsigned I = 0; I < InvI->arg_size(); ++I) {
+      if (!First)
+        Args += ", ";
+      Args += asStr(InvI->getArgOperand(I));
+      First = false;
+    }
+    Args += "}";
+    std::string FunDecl = getNextVar();
+    OS << formatv("auto {0} = {1}->getOrInsertFunction(\"{2}\", {3});\n",
+                  FunDecl, ModName, I->getOperand(2)->getName(),
+                  asStr(InvI->getFunctionType()));
+    Call = formatv("CreateInvoke({0}, {1}, {2}, {3})", FunDecl,
+                   asStr(InvI->getNormalDest()), asStr(InvI->getUnwindDest()),
+                   Args);
+    // TODO: Handle operand bundles.
+  } break;
+  case Instruction::Resume: {
+    Call = formatv("CreateResume({0})", Op1);
+  } break;
+  case Instruction::Unreachable: {
+    Call = "CreateUnreachable()";
+  } break;
+  case Instruction::CleanupRet: {
+    auto CurI = dyn_cast<CleanupReturnInst>(I);
+    Call = formatv("CreateCleanupRet({0}, {1})", Op1,
+                   asStr(CurI->getUnwindDest()));
+  } break;
+  case Instruction::CatchRet: {
+    Call = formatv("CreateCatchRet({0}, {1})", Op1, Op2);
+  } break;
+  case Instruction::CatchSwitch: {
+    auto SwI = dyn_cast<CatchSwitchInst>(I);
+    Call = formatv("CreateCatchSwitch({0}, {1}, {2})", Op1, Op2,
+                   std::to_string(SwI->getNumHandlers()));
+
+    std::string SwVar = asStr(SwI);
+    OS << formatv("auto {0} = {1}.{2};\n", SwVar, Builder, Call);
+    for (auto C : SwI->handlers()) {
+      OS << formatv("{0}->addHandler({1});\n", SwVar, asStr(C));
+    }
+    return;
+  } break;
+  case Instruction::CallBr: {
+    auto CallBRI = dyn_cast<CallBrInst>(I);
+    std::string Inddest = "{";
+    bool First = true;
+    for (unsigned I = 0; I < CallBRI->getNumIndirectDests(); ++I) {
+      if (!First)
+        Inddest += ", ";
+      Inddest += asStr(CallBRI->getIndirectDest(I));
+      First = false;
+    }
+    Inddest += "}";
+
+    std::string Args = "{";
+    First = true;
+    for (unsigned I = 0; I < CallBRI->arg_size(); ++I) {
+      if (!First)
+        Args += ", ";
+      Args += asStr(CallBRI->getArgOperand(I));
+      First = false;
+    }
+    Args += "}";
+    Call = formatv("CreateCallBr({0}, {1}, {2}, {3}, {4})",
+                   asStr(CallBRI->getFunctionType()),
+                   asStr(I->getOperand(I->getNumOperands() - 1)),
+                   asStr(CallBRI->getDefaultDest()), Inddest, Args);
+    // TODO: Handle operand bundles.
+  } break;
+  case Instruction::FNeg: {
+    Call = formatv("CreateFNeg({0})", Op1);
+    // TODO: Handle FPMathTag.
+  } break;
+  case Instruction::Add: {
+    Call = getBinArithOp("Add", Op1, Op2, I);
+  } break;
+  case Instruction::FAdd: {
+    Call = getFPBinArithOp("FAdd", Op1, Op2, I);
+  } break;
+  case Instruction::Sub: {
+    Call = getBinArithOp("Sub", Op1, Op2, I);
+  } break;
+  case Instruction::FSub: {
+    Call = getFPBinArithOp("FSub", Op1, Op2, I);
+  } break;
+  case Instruction::Mul: {
+    Call = getBinArithOp("Mul", Op1, Op2, I);
+  } break;
+  case Instruction::FMul: {
+    Call = getFPBinArithOp("FMul", Op1, Op2, I);
+  } break;
+  case Instruction::UDiv: {
+    Call = formatv("CreateUDiv({0}, {1}, \"\", {2})", Op1, Op2,
+                   toStr(I->isExact()));
+  } break;
+  case Instruction::SDiv: {
+    Call = formatv("CreateSDiv({0}, {1}, \"\", {2})", Op1, Op2,
+                   toStr(I->isExact()));
+  } break;
+  case Instruction::FDiv: {
+    Call = getFPBinArithOp("FDiv", Op1, Op2, I);
+  } break;
+  case Instruction::URem: {
+    Call = formatv("CreateURem({0}, {1})", Op1, Op2);
+  } break;
+  case Instruction::SRem: {
+    Call = formatv("CreateSRem({0}, {1})", Op1, Op2);
+  } break;
+  case Instruction::FRem: {
+    Call = getFPBinArithOp("FRem", Op1, Op2, I);
+  } break;
+  case Instruction::Shl: {
+    Call = getBinArithOp("Shl", Op1, Op2, I);
+  } break;
+  case Instruction::LShr: {
+    Call = formatv("CreateLShr({0}, {1}, \"\", {2})", Op1, Op2,
+                   toStr(I->isExact()));
+  } break;
+  case Instruction::AShr: {
+    Call = formatv("CreateAShr({0}, {1}, \"\", {2})", Op1, Op2,
+                   toStr(I->isExact()));
+  } break;
+  case Instruction::And: {
+    Call = formatv("CreateAnd({0}, {1})", Op1, Op2);
+  } break;
+  case Instruction::Or: {
+    Call = formatv("CreateOr({0}, {1})", Op1, Op2);
+  } break;
+  case Instruction::Xor: {
+    Call = formatv("CreateXor({0}, {1})", Op1, Op2);
+  } break;
+  case Instruction::Alloca: {
+    auto AlI = dyn_cast<AllocaInst>(I);
+    auto Val = AlI->getArraySize();
+    auto ValStr = Val ? asStr(Val) : "nullptr";
+    Call =
+        formatv("CreateAlloca({0}, {1}, {2})", asStr(AlI->getAllocatedType()),
+                AlI->getAddressSpace(), ValStr);
+  } break;
+  case Instruction::Load: {
+    auto LI = dyn_cast<LoadInst>(I);
+    Call = formatv("CreateLoad({0}, {1}, {2})", asStr(I->getType()), Op1,
+                   toStr(LI->isVolatile()));
+  } break;
+  case Instruction::Store: {
+    auto SI = dyn_cast<StoreInst>(I);
+    Call = formatv("CreateStore({0}, {1}, {2})", Op1, Op2,
+                   toStr(SI->isVolatile()));
+  } break;
+  case Instruction::GetElementPtr: {
+    auto GEPI = dyn_cast<GetElementPtrInst>(I);
+    std::string StrList = formatv("{0}ArrayRef<{0}Value*>(", LLVMPrefix);
+    if (I->getNumOperands() > 2)
+      StrList += "{";
+    bool First = true;
+    for (unsigned J = 1; J < I->getNumOperands(); ++J) {
+      if (!First)
+        StrList += ", ";
+      StrList += asStr(I->getOperand(J));
+      First = false;
+    }
+    if (I->getNumOperands() > 2)
+      StrList += "}";
+    StrList += ")";
+    Call = formatv("CreateGEP({0}, {1}, {2}, \"\", {3})",
+                   asStr(GEPI->getSourceElementType()), Op1, StrList,
+                   toStr(GEPI->isInBounds()));
+  } break;
+  case Instruction::Fence: {
+    auto FI = dyn_cast<FenceInst>(I);
+    Call = formatv("CreateFence({0}, {1})", asStr(FI->getOrdering()),
+                   asStr(FI->getSyncScopeID()));
+  } break;
+  case Instruction::AtomicCmpXchg: {
+    auto AcmpxI = dyn_cast<AtomicCmpXchgInst>(I);
+    Call = formatv(
+        "CreateAtomicCmpXchg({0}, {1}, {2}, Align({3}), {4}, {5}, {6})", Op1,
+        Op2, Op3, AcmpxI->getAlign().value(),
+        asStr(AcmpxI->getSuccessOrdering()),
+        asStr(AcmpxI->getFailureOrdering()), asStr(AcmpxI->getSyncScopeID()));
+  } break;
+  case Instruction::AtomicRMW: {
+    auto ArmwI = dyn_cast<AtomicRMWInst>(I);
+    Call = formatv("CreateAtomicRMW({0}, {1}, {2}, Align({3}), {4}, {5})",
+                   asStr(ArmwI->getOperation()), Op1, Op2,
+                   ArmwI->getAlign().value(), asStr(ArmwI->getOrdering()),
+                   asStr(ArmwI->getSyncScopeID()));
+  } break;
+  case Instruction::Trunc: {
+    Call = formatv("CreateTrunc({0}, {1})", Op1, asStr(I->getType()));
+  } break;
+  case Instruction::ZExt: {
+    Call = formatv("CreateZExt({0}, {1}, \"\", {2})", Op1, asStr(I->getType()),
+                   toStr(I->hasNonNeg()));
+  } break;
+  case Instruction::SExt: {
+    Call = formatv("CreateSExt({0}, {1})", Op1, asStr(I->getType()));
+  } break;
+  case Instruction::FPToUI: {
+    Call = formatv("CreateFPToUI({0}, {1})", Op1, asStr(I->getType()));
+  } break;
+  case Instruction::FPToSI: {
+    Call = formatv("CreateFPToSI({0}, {1})", Op1, asStr(I->getType()));
+  } break;
+  case Instruction::UIToFP: {
+    Call = formatv("CreateUIToFP({0}, {1})", Op1, asStr(I->getType()));
+  } break;
+  case Instruction::SIToFP: {
+    Call = formatv("CreateSIToFP({0}, {1})", Op1, asStr(I->getType()));
+  } break;
+  case Instruction::FPTrunc: {
+    Call = formatv("CreateFPTrunc({0}, {1})", Op1, asStr(I->getType()));
+  } break;
+  case Instruction::FPExt: {
+    Call = formatv("CreateFPExt({0}, {1})", Op1, asStr(I->getType()));
+  } break;
+  case Instruction::PtrToInt: {
+    Call = formatv("CreatePtrToInt({0}, {1})", Op1, asStr(I->getType()));
+  } break;
+  case Instruction::IntToPtr: {
+    Call = formatv("CreateIntToPtr({0}, {1})", Op1, asStr(I->getType()));
+  } break;
+  case Instruction::BitCast: {
+    Call = formatv("CreateBitCast({0}, {1})", Op1, asStr(I->getType()));
+  } break;
+  case Instruction::AddrSpaceCast: {
+    Call = formatv("CreateAddrSpaceCast({0}, {1})", Op1, asStr(I->getType()));
+  } break;
+  case Instruction::CleanupPad: {
+    auto CupI = dyn_cast<CleanupPadInst>(I);
+    std::string ArgsStr = "{";
+    bool First = true;
+    for (unsigned I = 0; I < CupI->arg_size(); ++I) {
+      if (!First)
+        ArgsStr += ", ";
+      ArgsStr += asStr(CupI->getArgOperand(I));
+      First = false;
+    }
+    ArgsStr += "}";
+    Call = formatv("CreateCleanupPad({0}, {1})", Op1, ArgsStr);
+  } break;
+  case Instruction::CatchPad: {
+    auto CapI = dyn_cast<CatchPadInst>(I);
+    std::string ArgsStr = "{";
+    bool First = true;
+    for (unsigned I = 0; I < CapI->arg_size(); ++I) {
+      if (!First)
+        ArgsStr += ", ";
+      ArgsStr += asStr(CapI->getArgOperand(I));
+      First = false;
+    }
+    ArgsStr += "}";
+    Call = formatv("CreateCatchPad({0}, {1})", Op1, ArgsStr);
+  } break;
+  case Instruction::ICmp: {
+    auto CmpI = dyn_cast<CmpInst>(I);
+    std::string CmpPredicate = LLVMPrefix + asStr(CmpI->getPredicate());
+    Call = formatv("CreateICmp({0}, {1}, {2})", CmpPredicate, Op1, Op2);
+  } break;
+  case Instruction::FCmp: {
+    auto CmpI = dyn_cast<CmpInst>(I);
+    std::string CmpPredicate = LLVMPrefix + asStr(CmpI->getPredicate());
+    Call = formatv("CreateFCmp({0}, {1}, {2})", CmpPredicate, Op1, Op2);
+  } break;
+  case Instruction::PHI: {
+    auto PhI = dyn_cast<PHINode>(I);
+    std::string PhVar = asStr(PhI);
+    OS << formatv("auto {0} = {1}.CreatePHI({2}, {3});\n", PhVar, Builder,
+                  asStr(I->getType()), PhI->getNumIncomingValues());
+
+    unsigned I = 0;
+    for (auto B : PhI->blocks()) {
+      auto IncVal = asStr(PhI->getIncomingValue(I));
+      // Phis might contain incoming not yet defined variable in such case
+      // we save the line we would output here and output it after the
+      // whole function body was outputted.
+      std::string Line =
+          formatv("{0}->addIncoming({1}, {2});\n", PhVar, IncVal, asStr(B));
+      PhiIncomings.push_back(Line);
+      ++I;
+    }
+    return;
+  } break;
+  case Instruction::Call: {
+    auto FcI = dyn_cast<CallBase>(I);
+    std::string ArgDecl = "";
+    if (FcI->arg_size() > 0) {
+      ArgDecl = getNextVar();
+      OS << "Value *" << ArgDecl << "[] = {";
+      bool IsFirst = true;
+      for (unsigned I = 0; I < FcI->arg_size(); ++I) {
+        if (!IsFirst)
+          OS << ", ";
+        OS << asStr(FcI->getArgOperand(I));
+        IsFirst = false;
+      }
+      OS << "};\n";
+    }
+
+    /* TODO: Implement.
+    std::string bundleDecl = "";
+    if (FcI->hasOperandBundles()) {
+        bundleDecl = getNextVar();
+        OS << "Value *" << bundleDecl << "[] = {";
+        IsFirst = true;
+        for(unsigned I = 0; I < FcI->getNumOperandBundles(); ++I) {
+            if(!IsFirst) OS << ", ";
+            // TODO: Probably create OperandBundleUse from the current gotten
+            // from getOperandBundleAt and cast construct OperandBundleDefT with
+            // it, but this requires conversion of Inputs which are Use.
+            OS << "...";
+            IsFirst = false;
+        }
+        OS << "};\n";
+    }
+    */
+
+    auto Fun = dyn_cast<Function>(I->getOperand(I->getNumOperands() - 1));
+    std::string FunDecl = getNextVar();
+    if (!Fun)
+      Fun = FcI->getCalledFunction();
+
+    if (!Fun) {
+      if (auto InA = dyn_cast<InlineAsm>(FcI->getCalledOperand())) {
+        if (ArgDecl.empty()) {
+          Call = formatv("CreateCall({0}, {1})", asStr(InA->getFunctionType()),
+                         asStr(I->getOperand(I->getNumOperands() - 1)));
+        } else {
+          Call = formatv(
+              "CreateCall({0}, {1}, {2})", asStr(InA->getFunctionType()),
+              asStr(I->getOperand(I->getNumOperands() - 1)), ArgDecl);
+        }
+      } else if (FcI->isIndirectCall()) {
+        if (ArgDecl.empty()) {
+          Call = formatv("CreateCall({0}, {1})", asStr(FcI->getFunctionType()),
+                         asStr(I->getOperand(I->getNumOperands() - 1)));
+        } else {
+          Call = formatv(
+              "CreateCall({0}, {1}, {2})", asStr(FcI->getFunctionType()),
+              asStr(I->getOperand(I->getNumOperands() - 1)), ArgDecl);
+        }
+      } else if (auto GA = dyn_cast<GlobalAlias>(FcI->getCalledOperand())) {
+        Fun = dyn_cast<Function>(GA->getAliaseeObject());
+        assert(Fun && "Global alias is not a function");
+        OS << formatv("auto {0} = {1}->getOrInsertFunction(\"{2}\", {3});\n",
+                      FunDecl, ModName, Fun->getName(),
+                      asStr(Fun->getFunctionType()));
+        if (!ArgDecl.empty())
+          Call = formatv("CreateCall({0}, {1})", FunDecl, ArgDecl);
+        else
+          Call = formatv("CreateCall({0})", FunDecl);
+      } else {
+        OS << "/* TODO: Unknown CallBase type in Call instruction */\n";
+        return;
+      }
+    } else {
+      OS << formatv("auto {0} = {1}->getOrInsertFunction(\"{2}\", {3});\n",
+                    FunDecl, ModName, Fun->getName(),
+                    asStr(Fun->getFunctionType()));
+      if (!ArgDecl.empty())
+        Call = formatv("CreateCall({0}, {1})", FunDecl, ArgDecl);
+      else
+        Call = formatv("CreateCall({0})", FunDecl);
+    }
+  } break;
+  case Instruction::Select: {
+    Call = formatv("CreateSelect({0}, {1}, {2})", Op1, Op2, Op3);
+  } break;
+  case Instruction::UserOp1: {
+    // Internal opcode.
+    OS << "// TODO: UserOp1 appeared in the IR\n";
+    return;
+  };
+  case Instruction::UserOp2: {
+    // Internal opcode.
+    OS << "// TODO: UserOp2 appeared in the IR\n";
+    return;
+  };
+  case Instruction::VAArg: {
+    Call = formatv("CreateVAArg({0}, {1})", Op1, asStr(I->getType()));
+  } break;
+  case Instruction::ExtractElement: {
+    Call = formatv("CreateExtractElement({0}, {1})", Op1, Op2);
+  } break;
+  case Instruction::InsertElement: {
+    Call = formatv("CreateInsertElement({0}, {1}, {2})", Op1, Op2, Op3);
+  } break;
+  case Instruction::ShuffleVector: {
+    auto SvI = dyn_cast<ShuffleVectorInst>(I);
----------------
topperc wrote:

dyn_cast -> cast

https://github.com/llvm/llvm-project/pull/117129


More information about the llvm-commits mailing list