Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticDriverKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,9 @@ def err_drv_sycl_missing_amdgpu_arch : Error<
def warn_drv_sycl_offload_target_duplicate : Warning<
"SYCL offloading target '%0' is similar to target '%1' already specified; "
"will be ignored">, InGroup<SyclTarget>;
def warn_drv_sycl_target_missing : Warning<
"linked binaries do not contain expected '%0' target; found targets: '%1'">,
InGroup<SyclTarget>;
def err_drv_failed_to_deduce_target_from_arch : Error<
"failed to deduce triple for target architecture '%0'; specify the triple "
"using '-fopenmp-targets' and '-Xopenmp-target' instead.">;
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Driver/Driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,10 @@ class Driver {
bool checkForOffloadStaticLib(Compilation &C,
llvm::opt::DerivedArgList &Args) const;

/// Checks for any mismatch of targets and provided input binaries.
void checkForOffloadMismatch(Compilation &C,
llvm::opt::DerivedArgList &Args) const;

/// Track filename used for the FPGA dependency info.
mutable llvm::StringMap<const std::string> FPGATempDepFiles;

Expand Down
160 changes: 149 additions & 11 deletions clang/lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,10 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ExitCodes.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/LineIterator.h"
#include "llvm/Support/MD5.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
Expand Down Expand Up @@ -1659,6 +1661,9 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
if (checkForSYCLDefaultDevice(*C, *TranslatedArgs))
setSYCLDefaultTriple(true);

// Check missing targets in archives/objects based on inputs from the user.
checkForOffloadMismatch(*C, *TranslatedArgs);

// Populate the tool chains for the offloading devices, if any.
CreateOffloadingDeviceToolChains(*C, Inputs);

Expand Down Expand Up @@ -3166,6 +3171,72 @@ static bool hasFPGABinary(Compilation &C, std::string Object, types::ID Type) {
return runBundler(BundlerArgs, C);
}

static SmallVector<std::string, 4> getOffloadSections(Compilation &C,
const StringRef &File) {
// Do not do the check if the file doesn't exist
if (!llvm::sys::fs::exists(File))
return {};

bool IsArchive = isStaticArchiveFile(File);
if (!(IsArchive || isObjectFile(File.str())))
return {};

// Use the bundler to grab the list of sections from the given archive
// or object.
StringRef ExecPath(C.getArgs().MakeArgString(C.getDriver().Dir));
llvm::ErrorOr<std::string> BundlerBinary =
llvm::sys::findProgramByName("clang-offload-bundler", ExecPath);
const char *Input = C.getArgs().MakeArgString(Twine("-input=") + File.str());
// Always use -type=ao for bundle checking. The 'bundles' are
// actually archives.
SmallVector<StringRef, 6> BundlerArgs = {
BundlerBinary.get(), IsArchive ? "-type=ao" : "-type=o", Input, "-list"};
// Since this is run in real time and not in the toolchain, output the
// command line if requested.
bool OutputOnly = C.getArgs().hasArg(options::OPT__HASH_HASH_HASH);
if (C.getArgs().hasArg(options::OPT_v) || OutputOnly) {
for (StringRef A : BundlerArgs)
if (OutputOnly)
llvm::errs() << "\"" << A << "\" ";
else
llvm::errs() << A << " ";
llvm::errs() << '\n';
}
if (BundlerBinary.getError())
return {};
llvm::SmallString<64> OutputFile(
C.getDriver().GetTemporaryPath("bundle-list", "txt"));
llvm::FileRemover OutputRemover(OutputFile.c_str());
llvm::Optional<llvm::StringRef> Redirects[] = {
{""},
OutputFile.str(),
OutputFile.str(),
};

std::string ErrorMessage;
if (llvm::sys::ExecuteAndWait(BundlerBinary.get(), BundlerArgs, {}, Redirects,
/*SecondsToWait*/ 0, /*MemoryLimit*/ 0,
&ErrorMessage)) {
// Could not get the information, return false
return {};
}

llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> OutputBuf =
llvm::MemoryBuffer::getFile(OutputFile.c_str());
if (!OutputBuf) {
// Could not capture output, return false
return {};
}

SmallVector<std::string, 4> Sections;
for (llvm::line_iterator LineIt(**OutputBuf); !LineIt.is_at_end(); ++LineIt)
Sections.push_back(LineIt->str());
if (Sections.empty())
return {};

return Sections;
}

static bool hasSYCLDefaultSection(Compilation &C, const StringRef &File) {
// Do not do the check if the file doesn't exist
if (!llvm::sys::fs::exists(File))
Expand All @@ -3180,20 +3251,21 @@ static bool hasSYCLDefaultSection(Compilation &C, const StringRef &File) {
// file and the target triple being looked for.
const char *Targets =
C.getArgs().MakeArgString(Twine("-targets=sycl-") + TT.str());
const char *Inputs =
C.getArgs().MakeArgString(Twine("-input=") + File.str());
// Always use -type=ao for bundle checking. The 'bundles' are
// actually archives.
const char *Inputs = C.getArgs().MakeArgString(Twine("-input=") + File.str());
SmallVector<StringRef, 6> BundlerArgs = {"clang-offload-bundler",
IsArchive ? "-type=ao" : "-type=o",
Targets, Inputs, "-check-section"};
return runBundler(BundlerArgs, C);
}

static bool hasOffloadSections(Compilation &C, const StringRef &Archive,
static bool hasOffloadSections(Compilation &C, const StringRef &File,
DerivedArgList &Args) {
// Do not do the check if the file doesn't exist
if (!llvm::sys::fs::exists(Archive))
if (!llvm::sys::fs::exists(File))
return false;

bool IsArchive = isStaticArchiveFile(File);
if (!(IsArchive || isObjectFile(File.str())))
return false;

llvm::Triple TT(C.getDefaultToolChain().getTriple());
Expand All @@ -3202,10 +3274,9 @@ static bool hasOffloadSections(Compilation &C, const StringRef &Archive,
// TODO - Improve checking to check for explicit offload target instead
// of the generic host availability.
const char *Targets = Args.MakeArgString(Twine("-targets=host-") + TT.str());
const char *Inputs = Args.MakeArgString(Twine("-input=") + Archive.str());
// Always use -type=ao for bundle checking. The 'bundles' are
// actually archives.
SmallVector<StringRef, 6> BundlerArgs = {"clang-offload-bundler", "-type=ao",
const char *Inputs = Args.MakeArgString(Twine("-input=") + File.str());
SmallVector<StringRef, 6> BundlerArgs = {"clang-offload-bundler",
IsArchive ? "-type=ao" : "-type=o",
Targets, Inputs, "-check-section"};
return runBundler(BundlerArgs, C);
}
Expand Down Expand Up @@ -3456,6 +3527,73 @@ bool Driver::checkForOffloadStaticLib(Compilation &C,
return false;
}

// Goes through all of the arguments, including inputs expected for the
// linker directly, to determine if the targets contained in the objects and
// archives match target expectations being performed.
void Driver::checkForOffloadMismatch(Compilation &C,
DerivedArgList &Args) const {
// Check only if enabled with -fsycl
if (!Args.hasFlag(options::OPT_fsycl, options::OPT_fno_sycl, false))
return;

SmallVector<const char *, 16> OffloadLibArgs(getLinkerArgs(C, Args, true));
// Gather all of the sections seen in the offload objects/archives
SmallVector<std::string, 4> UniqueSections;
for (StringRef OLArg : OffloadLibArgs) {
SmallVector<std::string, 4> Sections(getOffloadSections(C, OLArg));
for (auto Section : Sections) {
// We only care about sections that start with 'sycl-'. Also remove
// the prefix before adding it.
std::string Prefix("sycl-");
if (Section.compare(0, Prefix.length(), Prefix) != 0)
continue;
std::string Arch = Section.substr(Prefix.length());
// There are a few different variants for FPGA, if we see one, just
// use the default FPGA triple to reduce possible match confusion.
if (Arch.compare(0, 4, "fpga") == 0)
Arch = C.getDriver().MakeSYCLDeviceTriple("spir64_fpga").str();
if (std::find(UniqueSections.begin(), UniqueSections.end(), Arch) ==
UniqueSections.end())
UniqueSections.push_back(Arch);
}
}

if (!UniqueSections.size())
return;

// Put together list of user defined and implied targets, we will diagnose
// each target individually.
SmallVector<StringRef, 4> Targets;
if (const Arg *A = Args.getLastArg(options::OPT_fsycl_targets_EQ)) {
for (const char *Val : A->getValues())
Targets.push_back(Val);
} else { // Implied targets
// No -fsycl-targets given, check based on -fintelfpga or default device
bool SYCLfpga = C.getInputArgs().hasArg(options::OPT_fintelfpga);
// -fsycl -fintelfpga implies spir64_fpga
Targets.push_back(SYCLfpga ? "spir64_fpga" : getDefaultSYCLArch(C));
}

for (auto SyclTarget : Targets) {
// Match found sections with user and implied targets.
llvm::Triple TT(C.getDriver().MakeSYCLDeviceTriple(SyclTarget));
// If any matching section is found, we are good.
if (std::find(UniqueSections.begin(), UniqueSections.end(), TT.str()) !=
UniqueSections.end())
continue;
// Didn't find any matches, return the full list for the diagnostic.
SmallString<128> ArchListStr;
int Cnt = 0;
for (std::string Section : UniqueSections) {
if (Cnt)
ArchListStr += ", ";
ArchListStr += Section;
Cnt++;
}
Diag(diag::warn_drv_sycl_target_missing) << SyclTarget << ArchListStr;
}
}

/// Check whether the given input tree contains any clang-offload-dependency
/// actions.
static bool ContainsOffloadDepsAction(const Action *A) {
Expand Down Expand Up @@ -5350,7 +5488,6 @@ class OffloadingActionBuilder final {
} else
continue;

A->claim();
auto ParsedArg = Opts.ParseOneArg(Args, Index);

// TODO: Support --no-cuda-gpu-arch, --{,no-}cuda-gpu-arch=all.
Expand Down Expand Up @@ -5380,6 +5517,7 @@ class OffloadingActionBuilder final {
}
ParsedArg->claim();
GpuArchList.emplace_back(*TargetBE, ArchStr);
A->claim();
}
}

Expand Down
25 changes: 25 additions & 0 deletions clang/test/Driver/sycl-target-mismatch.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/// Check for diagnostic when command line link targets to not match object
// RUN: %clangxx -fsycl -fsycl-targets=spir64_gen %S/Inputs/SYCL/liblin64.a \
// RUN: -### %s 2>&1 \
// RUN: | FileCheck %s -check-prefix=SPIR64_GEN_DIAG
// RUN: %clangxx -fsycl -fsycl-targets=spir64_gen -L%S/Inputs/SYCL -llin64 \
// RUN: -### %s 2>&1 \
// RUN: | FileCheck %s -check-prefix=SPIR64_GEN_DIAG
// RUN: %clangxx -fsycl -fsycl-targets=spir64_gen %S/Inputs/SYCL/objlin64.o \
// RUN: -### %s 2>&1 \
// RUN: | FileCheck %s -check-prefix=SPIR64_GEN_DIAG
// SPIR64_GEN_DIAG: linked binaries do not contain expected 'spir64_gen' target; found targets: 'spir64-unknown-unknown{{.*}}, spir64-unknown-unknown{{.*}}' [-Wsycl-target]

// RUN: %clangxx -fsycl -fsycl-targets=spir64 %S/Inputs/SYCL/liblin64.a \
// RUN: -### %s 2>&1 \
// RUN: | FileCheck %s -check-prefix=SPIR64_DIAG
// RUN: %clangxx -fsycl -fsycl-targets=spir64 -L%S/Inputs/SYCL -llin64 \
// RUN: -### %s 2>&1 \
// RUN: | FileCheck %s -check-prefix=SPIR64_DIAG
// RUN: %clangxx -fsycl -fsycl-targets=spir64 %S/Inputs/SYCL/objlin64.o \
// RUN: -### %s 2>&1 \
// RUN: | FileCheck %s -check-prefix=SPIR64_DIAG
// RUN: %clangxx -fsycl -fsycl-targets=spir64_gen %S/Inputs/SYCL/liblin64.a \
// RUN: -Wno-sycl-target -### %s 2>&1 \
// RUN: | FileCheck %s -check-prefix=SPIR64_DIAG
// SPIR64_DIAG-NOT: linked binaries do not contain expected