xref: /llvm-project/llvm/lib/Target/SPIRV/SPIRVAPI.cpp (revision df122fc734ce002632f3bfe8a5fc5010349dba16)
1 //===-- SPIRVAPI.cpp - SPIR-V Backend API ---------------------*- C++ -*---===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "SPIRVCommandLine.h"
10 #include "SPIRVSubtarget.h"
11 #include "SPIRVTargetMachine.h"
12 #include "llvm/Analysis/TargetLibraryInfo.h"
13 #include "llvm/CodeGen/CommandFlags.h"
14 #include "llvm/CodeGen/MachineFunctionPass.h"
15 #include "llvm/CodeGen/MachineModuleInfo.h"
16 #include "llvm/CodeGen/TargetPassConfig.h"
17 #include "llvm/CodeGen/TargetSubtargetInfo.h"
18 #include "llvm/IR/DataLayout.h"
19 #include "llvm/IR/LLVMContext.h"
20 #include "llvm/IR/LegacyPassManager.h"
21 #include "llvm/IR/Module.h"
22 #include "llvm/IR/Verifier.h"
23 #include "llvm/InitializePasses.h"
24 #include "llvm/MC/MCTargetOptionsCommandFlags.h"
25 #include "llvm/MC/TargetRegistry.h"
26 #include "llvm/Pass.h"
27 #include "llvm/Support/CommandLine.h"
28 #include "llvm/Support/FormattedStream.h"
29 #include "llvm/Support/InitLLVM.h"
30 #include "llvm/Support/TargetSelect.h"
31 #include "llvm/Target/TargetLoweringObjectFile.h"
32 #include "llvm/Target/TargetMachine.h"
33 #include "llvm/TargetParser/SubtargetFeature.h"
34 #include "llvm/TargetParser/Triple.h"
35 #include <optional>
36 #include <string>
37 #include <utility>
38 #include <vector>
39 
40 using namespace llvm;
41 
42 namespace {
43 
44 std::once_flag InitOnceFlag;
45 void InitializeSPIRVTarget() {
46   std::call_once(InitOnceFlag, []() {
47     LLVMInitializeSPIRVTargetInfo();
48     LLVMInitializeSPIRVTarget();
49     LLVMInitializeSPIRVTargetMC();
50     LLVMInitializeSPIRVAsmPrinter();
51   });
52 }
53 } // namespace
54 
55 namespace llvm {
56 
57 // The goal of this function is to facilitate integration of SPIRV Backend into
58 // tools and libraries by means of exposing an API call that translate LLVM
59 // module to SPIR-V and write results into a string as binary SPIR-V output,
60 // providing diagnostics on fail and means of configuring translation.
61 extern "C" LLVM_EXTERNAL_VISIBILITY bool
62 SPIRVTranslate(Module *M, std::string &SpirvObj, std::string &ErrMsg,
63                const std::vector<std::string> &AllowExtNames,
64                llvm::CodeGenOptLevel OLevel, Triple TargetTriple) {
65   // Fallbacks for option values.
66   static const std::string DefaultTriple = "spirv64-unknown-unknown";
67   static const std::string DefaultMArch = "";
68 
69   std::set<SPIRV::Extension::Extension> AllowedExtIds;
70   StringRef UnknownExt =
71       SPIRVExtensionsParser::checkExtensions(AllowExtNames, AllowedExtIds);
72   if (!UnknownExt.empty()) {
73     ErrMsg = "Unknown SPIR-V extension: " + UnknownExt.str();
74     return false;
75   }
76 
77   // SPIR-V-specific target initialization.
78   InitializeSPIRVTarget();
79 
80   if (TargetTriple.getTriple().empty()) {
81     TargetTriple.setTriple(DefaultTriple);
82     M->setTargetTriple(DefaultTriple);
83   }
84   const Target *TheTarget =
85       TargetRegistry::lookupTarget(DefaultMArch, TargetTriple, ErrMsg);
86   if (!TheTarget)
87     return false;
88 
89   // A call to codegen::InitTargetOptionsFromCodeGenFlags(TargetTriple)
90   // hits the following assertion: llvm/lib/CodeGen/CommandFlags.cpp:78:
91   // llvm::FPOpFusion::FPOpFusionMode llvm::codegen::getFuseFPOps(): Assertion
92   // `FuseFPOpsView && "RegisterCodeGenFlags not created."' failed.
93   TargetOptions Options;
94   std::optional<Reloc::Model> RM;
95   std::optional<CodeModel::Model> CM;
96   std::unique_ptr<TargetMachine> Target(TheTarget->createTargetMachine(
97       TargetTriple.getTriple(), "", "", Options, RM, CM, OLevel));
98   if (!Target) {
99     ErrMsg = "Could not allocate target machine!";
100     return false;
101   }
102 
103   // Set available extensions.
104   SPIRVTargetMachine *STM = static_cast<SPIRVTargetMachine *>(Target.get());
105   const_cast<SPIRVSubtarget *>(STM->getSubtargetImpl())
106       ->initAvailableExtensions(AllowedExtIds);
107 
108   if (M->getCodeModel())
109     Target->setCodeModel(*M->getCodeModel());
110 
111   std::string DLStr = M->getDataLayoutStr();
112   Expected<DataLayout> MaybeDL = DataLayout::parse(
113       DLStr.empty() ? Target->createDataLayout().getStringRepresentation()
114                     : DLStr);
115   if (!MaybeDL) {
116     ErrMsg = toString(MaybeDL.takeError());
117     return false;
118   }
119   M->setDataLayout(MaybeDL.get());
120 
121   TargetLibraryInfoImpl TLII(Triple(M->getTargetTriple()));
122   legacy::PassManager PM;
123   PM.add(new TargetLibraryInfoWrapperPass(TLII));
124   std::unique_ptr<MachineModuleInfoWrapperPass> MMIWP(
125       new MachineModuleInfoWrapperPass(Target.get()));
126   const_cast<TargetLoweringObjectFile *>(Target->getObjFileLowering())
127       ->Initialize(MMIWP.get()->getMMI().getContext(), *Target);
128 
129   SmallString<4096> OutBuffer;
130   raw_svector_ostream OutStream(OutBuffer);
131   if (Target->addPassesToEmitFile(PM, OutStream, nullptr,
132                                   CodeGenFileType::ObjectFile)) {
133     ErrMsg = "Target machine cannot emit a file of this type";
134     return false;
135   }
136 
137   PM.run(*M);
138   SpirvObj = OutBuffer.str();
139 
140   return true;
141 }
142 
143 // TODO: Remove this wrapper after existing clients switch into a newer
144 // implementation of SPIRVTranslate().
145 extern "C" LLVM_EXTERNAL_VISIBILITY bool
146 SPIRVTranslateModule(Module *M, std::string &SpirvObj, std::string &ErrMsg,
147                      const std::vector<std::string> &AllowExtNames,
148                      const std::vector<std::string> &Opts) {
149   // optional: Opts[0] is a string representation of Triple,
150   // take Module triple otherwise
151   Triple TargetTriple(Opts.empty() || Opts[0].empty()
152                           ? M->getTargetTriple()
153                           : Triple::normalize(Opts[0]));
154   // optional: Opts[1] is a string representation of CodeGenOptLevel,
155   // no optimization otherwise
156   llvm::CodeGenOptLevel OLevel = CodeGenOptLevel::None;
157   if (Opts.size() > 1 && !Opts[1].empty()) {
158     if (auto Level = CodeGenOpt::parseLevel(Opts[1][0])) {
159       OLevel = *Level;
160     } else {
161       ErrMsg = "Invalid optimization level!";
162       return false;
163     }
164   }
165   return SPIRVTranslate(M, SpirvObj, ErrMsg, AllowExtNames, OLevel,
166                         TargetTriple);
167 }
168 
169 } // namespace llvm
170