1//===---------- DeviceOffload.cpp - Device Offloading------------*- 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
10// This file implements offloading to devices.
11//
12//===----------------------------------------------------------------------===//
13
14#include "DeviceOffload.h"
15
16#include "clang/Basic/TargetOptions.h"
17#include "clang/CodeGen/ModuleBuilder.h"
18#include "clang/Frontend/CompilerInstance.h"
19#include "clang/Interpreter/PartialTranslationUnit.h"
20
21#include "llvm/IR/LegacyPassManager.h"
22#include "llvm/IR/Module.h"
23#include "llvm/MC/TargetRegistry.h"
24#include "llvm/Target/TargetMachine.h"
25#include "llvm/TargetParser/Triple.h"
26
27namespace clang {
28
29IncrementalDeviceParser::IncrementalDeviceParser(
30 CompilerInstance &DeviceInstance, CompilerInstance &HostInstance,
31 IncrementalAction *DeviceAct,
32 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS,
33 llvm::Error &Err, std::list<PartialTranslationUnit> &PTUs)
34 : IncrementalParser(DeviceInstance, DeviceAct, Err, PTUs), VFS(FS),
35 CodeGenOpts(HostInstance.getCodeGenOpts()),
36 TargetOpts(DeviceInstance.getTargetOpts()) {}
37
38IncrementalDeviceParser::~IncrementalDeviceParser() {}
39
40IncrementalHIPDeviceParser::IncrementalHIPDeviceParser(
41 CompilerInstance &DeviceInstance, CompilerInstance &HostInstance,
42 IncrementalAction *DeviceAct,
43 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS,
44 llvm::Error &Err, std::list<PartialTranslationUnit> &PTUs)
45 : IncrementalDeviceParser(DeviceInstance, HostInstance, DeviceAct, FS, Err,
46 PTUs) {
47 if (Err)
48 return;
49 if (!llvm::Triple(TargetOpts.Triple).isAMDGCN()) {
50 Err = llvm::joinErrors(
51 E1: std::move(Err),
52 E2: llvm::make_error<llvm::StringError>(
53 Args: "Invalid HIP architecture for target '" + TargetOpts.Triple + "'",
54 Args: llvm::inconvertibleErrorCode()));
55 return;
56 }
57}
58
59llvm::Error IncrementalHIPDeviceParser::GenerateOffloadBinary() {
60 // FIXME: Generate the HSACO and bundle it for the host compilation.
61 return llvm::make_error<llvm::StringError>(
62 Args: "HIP offload binary generation is not supported yet.",
63 Args: llvm::inconvertibleErrorCode());
64}
65
66IncrementalHIPDeviceParser::~IncrementalHIPDeviceParser() {}
67
68IncrementalCUDADeviceParser::IncrementalCUDADeviceParser(
69 CompilerInstance &DeviceInstance, CompilerInstance &HostInstance,
70 IncrementalAction *DeviceAct,
71 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS,
72 llvm::Error &Err, std::list<PartialTranslationUnit> &PTUs)
73 : IncrementalDeviceParser(DeviceInstance, HostInstance, DeviceAct, FS, Err,
74 PTUs) {
75 if (Err)
76 return;
77 StringRef Arch = TargetOpts.CPU;
78 if (!Arch.starts_with(Prefix: "sm_") || Arch.substr(Start: 3).getAsInteger(Radix: 10, Result&: SMVersion)) {
79 Err = llvm::joinErrors(E1: std::move(Err), E2: llvm::make_error<llvm::StringError>(
80 Args: "Invalid CUDA architecture",
81 Args: llvm::inconvertibleErrorCode()));
82 return;
83 }
84}
85
86llvm::Expected<llvm::StringRef> IncrementalCUDADeviceParser::GeneratePTX() {
87 auto &PTU = PTUs.back();
88 std::string Error;
89
90 const llvm::Target *Target = llvm::TargetRegistry::lookupTarget(
91 TheTriple: PTU.TheModule->getTargetTriple(), Error);
92 if (!Target)
93 return llvm::make_error<llvm::StringError>(Args: std::move(Error),
94 Args: std::error_code());
95 llvm::TargetOptions TO = llvm::TargetOptions();
96 std::unique_ptr<llvm::TargetMachine> TM(Target->createTargetMachine(
97 TT: PTU.TheModule->getTargetTriple(), CPU: TargetOpts.CPU, Features: "", Options: TO,
98 RM: llvm::Reloc::Model::PIC_));
99
100 PTXCode.clear();
101 llvm::raw_svector_ostream dest(PTXCode);
102
103 llvm::legacy::PassManager PM;
104 if (TM->addPassesToEmitFile(PM, dest, nullptr,
105 llvm::CodeGenFileType::AssemblyFile)) {
106 return llvm::make_error<llvm::StringError>(
107 Args: "NVPTX backend cannot produce PTX code.",
108 Args: llvm::inconvertibleErrorCode());
109 }
110
111 PM.run(M&: *PTU.TheModule);
112
113 PTXCode += '\0';
114 while (PTXCode.size() % 8)
115 PTXCode += '\0';
116 return PTXCode.str();
117}
118
119llvm::Error IncrementalCUDADeviceParser::GenerateFatbinary() {
120 enum FatBinFlags {
121 AddressSize64 = 0x01,
122 HasDebugInfo = 0x02,
123 ProducerCuda = 0x04,
124 HostLinux = 0x10,
125 HostMac = 0x20,
126 HostWindows = 0x40
127 };
128
129 struct FatBinInnerHeader {
130 uint16_t Kind; // 0x00
131 uint16_t unknown02; // 0x02
132 uint32_t HeaderSize; // 0x04
133 uint32_t DataSize; // 0x08
134 uint32_t unknown0c; // 0x0c
135 uint32_t CompressedSize; // 0x10
136 uint32_t SubHeaderSize; // 0x14
137 uint16_t VersionMinor; // 0x18
138 uint16_t VersionMajor; // 0x1a
139 uint32_t CudaArch; // 0x1c
140 uint32_t unknown20; // 0x20
141 uint32_t unknown24; // 0x24
142 uint32_t Flags; // 0x28
143 uint32_t unknown2c; // 0x2c
144 uint32_t unknown30; // 0x30
145 uint32_t unknown34; // 0x34
146 uint32_t UncompressedSize; // 0x38
147 uint32_t unknown3c; // 0x3c
148 uint32_t unknown40; // 0x40
149 uint32_t unknown44; // 0x44
150 FatBinInnerHeader(uint32_t DataSize, uint32_t CudaArch, uint32_t Flags)
151 : Kind(1 /*PTX*/), unknown02(0x0101), HeaderSize(sizeof(*this)),
152 DataSize(DataSize), unknown0c(0), CompressedSize(0),
153 SubHeaderSize(HeaderSize - 8), VersionMinor(2), VersionMajor(4),
154 CudaArch(CudaArch), unknown20(0), unknown24(0), Flags(Flags),
155 unknown2c(0), unknown30(0), unknown34(0), UncompressedSize(0),
156 unknown3c(0), unknown40(0), unknown44(0) {}
157 };
158
159 struct FatBinHeader {
160 uint32_t Magic; // 0x00
161 uint16_t Version; // 0x04
162 uint16_t HeaderSize; // 0x06
163 uint32_t DataSize; // 0x08
164 uint32_t unknown0c; // 0x0c
165 public:
166 FatBinHeader(uint32_t DataSize)
167 : Magic(0xba55ed50), Version(1), HeaderSize(sizeof(*this)),
168 DataSize(DataSize), unknown0c(0) {}
169 };
170
171 FatBinHeader OuterHeader(sizeof(FatBinInnerHeader) + PTXCode.size());
172 FatbinContent.append(in_start: (char *)&OuterHeader,
173 in_end: ((char *)&OuterHeader) + OuterHeader.HeaderSize);
174
175 FatBinInnerHeader InnerHeader(PTXCode.size(), SMVersion,
176 FatBinFlags::AddressSize64 |
177 FatBinFlags::HostLinux);
178 FatbinContent.append(in_start: (char *)&InnerHeader,
179 in_end: ((char *)&InnerHeader) + InnerHeader.HeaderSize);
180
181 FatbinContent.append(in_start: PTXCode.begin(), in_end: PTXCode.end());
182
183 const PartialTranslationUnit &PTU = PTUs.back();
184
185 std::string FatbinFileName = "/" + PTU.TheModule->getName().str() + ".fatbin";
186
187 VFS->addFile(Path: FatbinFileName, ModificationTime: 0,
188 Buffer: llvm::MemoryBuffer::getMemBuffer(
189 InputData: llvm::StringRef(FatbinContent.data(), FatbinContent.size()),
190 BufferName: "", RequiresNullTerminator: false));
191
192 CodeGenOpts.OffloadBinaryToEmbedFile = std::move(FatbinFileName);
193
194 FatbinContent.clear();
195
196 return llvm::Error::success();
197}
198
199llvm::Error IncrementalCUDADeviceParser::GenerateOffloadBinary() {
200 llvm::Expected<llvm::StringRef> PTX = GeneratePTX();
201 if (!PTX)
202 return PTX.takeError();
203
204 return GenerateFatbinary();
205}
206
207IncrementalCUDADeviceParser::~IncrementalCUDADeviceParser() {}
208
209} // namespace clang
210