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;
InitializeSPIRVTarget()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
SPIRVTranslate(Module * M,std::string & SpirvObj,std::string & ErrMsg,const std::vector<std::string> & AllowExtNames,llvm::CodeGenOptLevel OLevel,Triple TargetTriple)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
SPIRVTranslateModule(Module * M,std::string & SpirvObj,std::string & ErrMsg,const std::vector<std::string> & AllowExtNames,const std::vector<std::string> & Opts)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