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