Skip to content

Commit 0e0202e

Browse files
authored
[Driver][SYCL] Improve diagnostics when linking with mismatched objects (#6591)
When performing linking with -fsycl, it is required that the user provide the -fsycl-targets=triple option so the driver knows which targets to unbundle and use. There is no indication to the end user if they are using a command line that matches up with the generated objects and archives. Perform some additional checking against the archives and objects as they are provided on the command line and cross check with what the user has specified for targets. If these do not match, emit a diagnostic stating as such and also list out the targets that were found in the objects.
1 parent c0d639e commit 0e0202e

File tree

4 files changed

+181
-11
lines changed

4 files changed

+181
-11
lines changed

clang/include/clang/Basic/DiagnosticDriverKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,9 @@ def err_drv_sycl_missing_amdgpu_arch : Error<
342342
def warn_drv_sycl_offload_target_duplicate : Warning<
343343
"SYCL offloading target '%0' is similar to target '%1' already specified; "
344344
"will be ignored">, InGroup<SyclTarget>;
345+
def warn_drv_sycl_target_missing : Warning<
346+
"linked binaries do not contain expected '%0' target; found targets: '%1'">,
347+
InGroup<SyclTarget>;
345348
def err_drv_failed_to_deduce_target_from_arch : Error<
346349
"failed to deduce triple for target architecture '%0'; specify the triple "
347350
"using '-fopenmp-targets' and '-Xopenmp-target' instead.">;

clang/include/clang/Driver/Driver.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,10 @@ class Driver {
769769
bool checkForOffloadStaticLib(Compilation &C,
770770
llvm::opt::DerivedArgList &Args) const;
771771

772+
/// Checks for any mismatch of targets and provided input binaries.
773+
void checkForOffloadMismatch(Compilation &C,
774+
llvm::opt::DerivedArgList &Args) const;
775+
772776
/// Track filename used for the FPGA dependency info.
773777
mutable llvm::StringMap<const std::string> FPGATempDepFiles;
774778

clang/lib/Driver/Driver.cpp

Lines changed: 149 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,10 @@
8686
#include "llvm/Support/ErrorHandling.h"
8787
#include "llvm/Support/ExitCodes.h"
8888
#include "llvm/Support/FileSystem.h"
89+
#include "llvm/Support/FileUtilities.h"
8990
#include "llvm/Support/FormatVariadic.h"
9091
#include "llvm/Support/Host.h"
92+
#include "llvm/Support/LineIterator.h"
9193
#include "llvm/Support/MD5.h"
9294
#include "llvm/Support/Path.h"
9395
#include "llvm/Support/PrettyStackTrace.h"
@@ -1659,6 +1661,9 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
16591661
if (checkForSYCLDefaultDevice(*C, *TranslatedArgs))
16601662
setSYCLDefaultTriple(true);
16611663

1664+
// Check missing targets in archives/objects based on inputs from the user.
1665+
checkForOffloadMismatch(*C, *TranslatedArgs);
1666+
16621667
// Populate the tool chains for the offloading devices, if any.
16631668
CreateOffloadingDeviceToolChains(*C, Inputs);
16641669

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

3174+
static SmallVector<std::string, 4> getOffloadSections(Compilation &C,
3175+
const StringRef &File) {
3176+
// Do not do the check if the file doesn't exist
3177+
if (!llvm::sys::fs::exists(File))
3178+
return {};
3179+
3180+
bool IsArchive = isStaticArchiveFile(File);
3181+
if (!(IsArchive || isObjectFile(File.str())))
3182+
return {};
3183+
3184+
// Use the bundler to grab the list of sections from the given archive
3185+
// or object.
3186+
StringRef ExecPath(C.getArgs().MakeArgString(C.getDriver().Dir));
3187+
llvm::ErrorOr<std::string> BundlerBinary =
3188+
llvm::sys::findProgramByName("clang-offload-bundler", ExecPath);
3189+
const char *Input = C.getArgs().MakeArgString(Twine("-input=") + File.str());
3190+
// Always use -type=ao for bundle checking. The 'bundles' are
3191+
// actually archives.
3192+
SmallVector<StringRef, 6> BundlerArgs = {
3193+
BundlerBinary.get(), IsArchive ? "-type=ao" : "-type=o", Input, "-list"};
3194+
// Since this is run in real time and not in the toolchain, output the
3195+
// command line if requested.
3196+
bool OutputOnly = C.getArgs().hasArg(options::OPT__HASH_HASH_HASH);
3197+
if (C.getArgs().hasArg(options::OPT_v) || OutputOnly) {
3198+
for (StringRef A : BundlerArgs)
3199+
if (OutputOnly)
3200+
llvm::errs() << "\"" << A << "\" ";
3201+
else
3202+
llvm::errs() << A << " ";
3203+
llvm::errs() << '\n';
3204+
}
3205+
if (BundlerBinary.getError())
3206+
return {};
3207+
llvm::SmallString<64> OutputFile(
3208+
C.getDriver().GetTemporaryPath("bundle-list", "txt"));
3209+
llvm::FileRemover OutputRemover(OutputFile.c_str());
3210+
llvm::Optional<llvm::StringRef> Redirects[] = {
3211+
{""},
3212+
OutputFile.str(),
3213+
OutputFile.str(),
3214+
};
3215+
3216+
std::string ErrorMessage;
3217+
if (llvm::sys::ExecuteAndWait(BundlerBinary.get(), BundlerArgs, {}, Redirects,
3218+
/*SecondsToWait*/ 0, /*MemoryLimit*/ 0,
3219+
&ErrorMessage)) {
3220+
// Could not get the information, return false
3221+
return {};
3222+
}
3223+
3224+
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> OutputBuf =
3225+
llvm::MemoryBuffer::getFile(OutputFile.c_str());
3226+
if (!OutputBuf) {
3227+
// Could not capture output, return false
3228+
return {};
3229+
}
3230+
3231+
SmallVector<std::string, 4> Sections;
3232+
for (llvm::line_iterator LineIt(**OutputBuf); !LineIt.is_at_end(); ++LineIt)
3233+
Sections.push_back(LineIt->str());
3234+
if (Sections.empty())
3235+
return {};
3236+
3237+
return Sections;
3238+
}
3239+
31693240
static bool hasSYCLDefaultSection(Compilation &C, const StringRef &File) {
31703241
// Do not do the check if the file doesn't exist
31713242
if (!llvm::sys::fs::exists(File))
@@ -3180,20 +3251,21 @@ static bool hasSYCLDefaultSection(Compilation &C, const StringRef &File) {
31803251
// file and the target triple being looked for.
31813252
const char *Targets =
31823253
C.getArgs().MakeArgString(Twine("-targets=sycl-") + TT.str());
3183-
const char *Inputs =
3184-
C.getArgs().MakeArgString(Twine("-input=") + File.str());
3185-
// Always use -type=ao for bundle checking. The 'bundles' are
3186-
// actually archives.
3254+
const char *Inputs = C.getArgs().MakeArgString(Twine("-input=") + File.str());
31873255
SmallVector<StringRef, 6> BundlerArgs = {"clang-offload-bundler",
31883256
IsArchive ? "-type=ao" : "-type=o",
31893257
Targets, Inputs, "-check-section"};
31903258
return runBundler(BundlerArgs, C);
31913259
}
31923260

3193-
static bool hasOffloadSections(Compilation &C, const StringRef &Archive,
3261+
static bool hasOffloadSections(Compilation &C, const StringRef &File,
31943262
DerivedArgList &Args) {
31953263
// Do not do the check if the file doesn't exist
3196-
if (!llvm::sys::fs::exists(Archive))
3264+
if (!llvm::sys::fs::exists(File))
3265+
return false;
3266+
3267+
bool IsArchive = isStaticArchiveFile(File);
3268+
if (!(IsArchive || isObjectFile(File.str())))
31973269
return false;
31983270

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

3530+
// Goes through all of the arguments, including inputs expected for the
3531+
// linker directly, to determine if the targets contained in the objects and
3532+
// archives match target expectations being performed.
3533+
void Driver::checkForOffloadMismatch(Compilation &C,
3534+
DerivedArgList &Args) const {
3535+
// Check only if enabled with -fsycl
3536+
if (!Args.hasFlag(options::OPT_fsycl, options::OPT_fno_sycl, false))
3537+
return;
3538+
3539+
SmallVector<const char *, 16> OffloadLibArgs(getLinkerArgs(C, Args, true));
3540+
// Gather all of the sections seen in the offload objects/archives
3541+
SmallVector<std::string, 4> UniqueSections;
3542+
for (StringRef OLArg : OffloadLibArgs) {
3543+
SmallVector<std::string, 4> Sections(getOffloadSections(C, OLArg));
3544+
for (auto Section : Sections) {
3545+
// We only care about sections that start with 'sycl-'. Also remove
3546+
// the prefix before adding it.
3547+
std::string Prefix("sycl-");
3548+
if (Section.compare(0, Prefix.length(), Prefix) != 0)
3549+
continue;
3550+
std::string Arch = Section.substr(Prefix.length());
3551+
// There are a few different variants for FPGA, if we see one, just
3552+
// use the default FPGA triple to reduce possible match confusion.
3553+
if (Arch.compare(0, 4, "fpga") == 0)
3554+
Arch = C.getDriver().MakeSYCLDeviceTriple("spir64_fpga").str();
3555+
if (std::find(UniqueSections.begin(), UniqueSections.end(), Arch) ==
3556+
UniqueSections.end())
3557+
UniqueSections.push_back(Arch);
3558+
}
3559+
}
3560+
3561+
if (!UniqueSections.size())
3562+
return;
3563+
3564+
// Put together list of user defined and implied targets, we will diagnose
3565+
// each target individually.
3566+
SmallVector<StringRef, 4> Targets;
3567+
if (const Arg *A = Args.getLastArg(options::OPT_fsycl_targets_EQ)) {
3568+
for (const char *Val : A->getValues())
3569+
Targets.push_back(Val);
3570+
} else { // Implied targets
3571+
// No -fsycl-targets given, check based on -fintelfpga or default device
3572+
bool SYCLfpga = C.getInputArgs().hasArg(options::OPT_fintelfpga);
3573+
// -fsycl -fintelfpga implies spir64_fpga
3574+
Targets.push_back(SYCLfpga ? "spir64_fpga" : getDefaultSYCLArch(C));
3575+
}
3576+
3577+
for (auto SyclTarget : Targets) {
3578+
// Match found sections with user and implied targets.
3579+
llvm::Triple TT(C.getDriver().MakeSYCLDeviceTriple(SyclTarget));
3580+
// If any matching section is found, we are good.
3581+
if (std::find(UniqueSections.begin(), UniqueSections.end(), TT.str()) !=
3582+
UniqueSections.end())
3583+
continue;
3584+
// Didn't find any matches, return the full list for the diagnostic.
3585+
SmallString<128> ArchListStr;
3586+
int Cnt = 0;
3587+
for (std::string Section : UniqueSections) {
3588+
if (Cnt)
3589+
ArchListStr += ", ";
3590+
ArchListStr += Section;
3591+
Cnt++;
3592+
}
3593+
Diag(diag::warn_drv_sycl_target_missing) << SyclTarget << ArchListStr;
3594+
}
3595+
}
3596+
34593597
/// Check whether the given input tree contains any clang-offload-dependency
34603598
/// actions.
34613599
static bool ContainsOffloadDepsAction(const Action *A) {
@@ -5350,7 +5488,6 @@ class OffloadingActionBuilder final {
53505488
} else
53515489
continue;
53525490

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

53565493
// TODO: Support --no-cuda-gpu-arch, --{,no-}cuda-gpu-arch=all.
@@ -5380,6 +5517,7 @@ class OffloadingActionBuilder final {
53805517
}
53815518
ParsedArg->claim();
53825519
GpuArchList.emplace_back(*TargetBE, ArchStr);
5520+
A->claim();
53835521
}
53845522
}
53855523

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/// Check for diagnostic when command line link targets to not match object
2+
// RUN: %clangxx -fsycl -fsycl-targets=spir64_gen %S/Inputs/SYCL/liblin64.a \
3+
// RUN: -### %s 2>&1 \
4+
// RUN: | FileCheck %s -check-prefix=SPIR64_GEN_DIAG
5+
// RUN: %clangxx -fsycl -fsycl-targets=spir64_gen -L%S/Inputs/SYCL -llin64 \
6+
// RUN: -### %s 2>&1 \
7+
// RUN: | FileCheck %s -check-prefix=SPIR64_GEN_DIAG
8+
// RUN: %clangxx -fsycl -fsycl-targets=spir64_gen %S/Inputs/SYCL/objlin64.o \
9+
// RUN: -### %s 2>&1 \
10+
// RUN: | FileCheck %s -check-prefix=SPIR64_GEN_DIAG
11+
// SPIR64_GEN_DIAG: linked binaries do not contain expected 'spir64_gen' target; found targets: 'spir64-unknown-unknown{{.*}}, spir64-unknown-unknown{{.*}}' [-Wsycl-target]
12+
13+
// RUN: %clangxx -fsycl -fsycl-targets=spir64 %S/Inputs/SYCL/liblin64.a \
14+
// RUN: -### %s 2>&1 \
15+
// RUN: | FileCheck %s -check-prefix=SPIR64_DIAG
16+
// RUN: %clangxx -fsycl -fsycl-targets=spir64 -L%S/Inputs/SYCL -llin64 \
17+
// RUN: -### %s 2>&1 \
18+
// RUN: | FileCheck %s -check-prefix=SPIR64_DIAG
19+
// RUN: %clangxx -fsycl -fsycl-targets=spir64 %S/Inputs/SYCL/objlin64.o \
20+
// RUN: -### %s 2>&1 \
21+
// RUN: | FileCheck %s -check-prefix=SPIR64_DIAG
22+
// RUN: %clangxx -fsycl -fsycl-targets=spir64_gen %S/Inputs/SYCL/liblin64.a \
23+
// RUN: -Wno-sycl-target -### %s 2>&1 \
24+
// RUN: | FileCheck %s -check-prefix=SPIR64_DIAG
25+
// SPIR64_DIAG-NOT: linked binaries do not contain expected

0 commit comments

Comments
 (0)