1//===--------- CodeGenSYCL.cpp - Code for SYCL kernel generation ----------===//
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 contains code required for generation of SYCL kernel caller offload
10// entry point functions.
11//
12//===----------------------------------------------------------------------===//
13
14#include "CodeGenFunction.h"
15#include "CodeGenModule.h"
16#include "clang/Basic/DiagnosticFrontend.h"
17#include "llvm/Frontend/Offloading/OffloadWrapper.h"
18#include "llvm/Support/MemoryBuffer.h"
19#include "llvm/Support/VirtualFileSystem.h"
20#include <cassert>
21
22using namespace clang;
23using namespace CodeGen;
24
25void CodeGenFunction::EmitSYCLKernelCallStmt(const SYCLKernelCallStmt &S) {
26 // SYCLKernelCallStmt instances are only injected in the definitions of
27 // functions declared with the sycl_kernel_entry_point attribute. ODR-use of
28 // such a function in code emitted during device compilation should be
29 // diagnosed. Thus, any attempt to emit a SYCLKernelCallStmt during device
30 // compilation indicates a missing diagnostic.
31 assert(!getLangOpts().SYCLIsDevice &&
32 "Attempt to emit a SYCL kernel call statement during device"
33 " compilation");
34 EmitStmt(S: S.getKernelLaunchStmt());
35}
36
37static void SetSYCLKernelAttributes(llvm::Function *Fn, CodeGenFunction &CGF) {
38 // SYCL 2020 device language restrictions require forward progress and
39 // disallow recursion.
40 Fn->setDoesNotRecurse();
41 if (CGF.checkIfFunctionMustProgress())
42 Fn->addFnAttr(Kind: llvm::Attribute::MustProgress);
43}
44
45void CodeGenModule::EmitSYCLKernelCaller(const FunctionDecl *KernelEntryPointFn,
46 ASTContext &Ctx) {
47 assert(Ctx.getLangOpts().SYCLIsDevice &&
48 "SYCL kernel caller offload entry point functions can only be emitted"
49 " during device compilation");
50
51 const auto *KernelEntryPointAttr =
52 KernelEntryPointFn->getAttr<SYCLKernelEntryPointAttr>();
53 assert(KernelEntryPointAttr && "Missing sycl_kernel_entry_point attribute");
54 assert(!KernelEntryPointAttr->isInvalidAttr() &&
55 "sycl_kernel_entry_point attribute is invalid");
56
57 // Find the SYCLKernelCallStmt.
58 SYCLKernelCallStmt *KernelCallStmt =
59 cast<SYCLKernelCallStmt>(Val: KernelEntryPointFn->getBody());
60
61 // Retrieve the SYCL kernel caller parameters from the OutlinedFunctionDecl.
62 FunctionArgList Args;
63 const OutlinedFunctionDecl *OutlinedFnDecl =
64 KernelCallStmt->getOutlinedFunctionDecl();
65 Args.append(in_start: OutlinedFnDecl->param_begin(), in_end: OutlinedFnDecl->param_end());
66
67 // Compute the function info and LLVM function type.
68 const CGFunctionInfo &FnInfo =
69 getTypes().arrangeDeviceKernelCallerDeclaration(resultType: Ctx.VoidTy, args: Args);
70 llvm::FunctionType *FnTy = getTypes().GetFunctionType(Info: FnInfo);
71
72 // Retrieve the generated name for the SYCL kernel caller function.
73 CanQualType KernelNameType =
74 Ctx.getCanonicalType(T: KernelEntryPointAttr->getKernelName());
75 const SYCLKernelInfo &KernelInfo = Ctx.getSYCLKernelInfo(T: KernelNameType);
76 auto *Fn = llvm::Function::Create(Ty: FnTy, Linkage: llvm::Function::ExternalLinkage,
77 N: KernelInfo.GetKernelName(), M: &getModule());
78
79 // Emit the SYCL kernel caller function.
80 CodeGenFunction CGF(*this);
81 SetLLVMFunctionAttributes(GD: GlobalDecl(), Info: FnInfo, F: Fn, IsThunk: false);
82 SetSYCLKernelAttributes(Fn, CGF);
83 addSYCLModuleIdAttr(Fn);
84 CGF.StartFunction(GD: GlobalDecl(), RetTy: Ctx.VoidTy, Fn, FnInfo, Args,
85 Loc: SourceLocation(), StartLoc: SourceLocation());
86 CGF.EmitFunctionBody(Body: OutlinedFnDecl->getBody());
87 setDSOLocal(Fn);
88 SetLLVMFunctionAttributesForDefinition(D: cast<Decl>(Val: OutlinedFnDecl), F: Fn);
89 CGF.FinishFunction();
90}
91
92llvm::Function *CodeGenModule::embedSYCLDeviceBinary() {
93 StringRef FileName = getCodeGenOpts().OffloadBinaryToEmbedFile;
94 auto BufferOrErr = getFileSystem()->getBufferForFile(Name: FileName);
95 if (std::error_code EC = BufferOrErr.getError()) {
96 getDiags().Report(DiagID: diag::err_cannot_open_file) << FileName << EC.message();
97 return nullptr;
98 }
99 std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrErr.get());
100 llvm::Function *RegistrationFunc = nullptr;
101 if (llvm::Error Err = llvm::offloading::wrapSYCLBinaries(
102 M&: getModule(),
103 Buffer: ArrayRef<char>(Buffer->getBufferStart(), Buffer->getBufferSize()),
104 Options: llvm::offloading::SYCLJITOptions(), /*IsFinalizedImage=*/true,
105 RegistrationFunc: &RegistrationFunc)) {
106 getDiags().Report(DiagID: diag::err_fe_error_backend)
107 << llvm::toString(E: std::move(Err));
108 return nullptr;
109 }
110 return RegistrationFunc;
111}
112