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