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