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