Skip to content
Open
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
16 changes: 13 additions & 3 deletions clang/lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4265,8 +4265,11 @@ void Driver::handleArguments(Compilation &C, DerivedArgList &Args,
Args.AddFlagArg(nullptr,
getOpts().getOption(options::OPT_frtlib_add_rpath));
}
// Emitting LLVM while linking disabled except in HIPAMD Toolchain
if (Args.hasArg(options::OPT_emit_llvm) && !Args.hasArg(options::OPT_hip_link))
// Emitting LLVM while linking disabled except in the HIPAMD or SPIR-V
// Toolchains
if (Args.hasArg(options::OPT_emit_llvm) &&
!Args.hasArg(options::OPT_hip_link) &&
!C.getDefaultToolChain().getTriple().isSPIRV())
Diag(clang::diag::err_drv_emit_llvm_link);
if (C.getDefaultToolChain().getTriple().isWindowsMSVCEnvironment() &&
LTOMode != LTOK_None &&
Expand Down Expand Up @@ -4595,7 +4598,14 @@ void Driver::BuildDefaultActions(Compilation &C, DerivedArgList &Args,
LA->propagateHostOffloadInfo(C.getActiveOffloadKinds(),
/*BoundArch=*/nullptr);
} else {
LA = C.MakeAction<LinkJobAction>(LinkerInputs, types::TY_Image);
// If we are linking but were passed -emit-llvm, we will be calling
// llvm-link, so set the output type accordingly. This is only allowed in
// rare cases, so make sure we aren't going to error about it.
types::ID LT =
Args.hasArg(options::OPT_emit_llvm) && !Diags.hasErrorOccurred()
? types::TY_LLVM_BC
: types::TY_Image;
LA = C.MakeAction<LinkJobAction>(LinkerInputs, LT);
}
if (!UseNewOffloadingDriver)
LA = OffloadBuilder->processHostLinkAction(LA);
Expand Down
26 changes: 26 additions & 0 deletions clang/lib/Driver/ToolChains/SPIRV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,28 @@ void SPIRV::constructAssembleCommand(Compilation &C, const Tool &T,
Exec, CmdArgs, Input, Output));
}

void SPIRV::constructLLVMLinkCommand(Compilation &C, const Tool &T,
const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
const llvm::opt::ArgStringList &Args) {
// Construct llvm-link command.
// The output from llvm-link is a bitcode file.
ArgStringList LlvmLinkArgs;

assert(!Inputs.empty() && "Must have at least one input.");

LlvmLinkArgs.append({"-o", Output.getFilename()});
for (auto Input : Inputs)
LlvmLinkArgs.push_back(Input.getFilename());

const char *LlvmLink =
C.getArgs().MakeArgString(T.getToolChain().GetProgramPath("llvm-link"));
C.addCommand(std::make_unique<Command>(JA, T, ResponseFileSupport::None(),
LlvmLink, LlvmLinkArgs, Inputs,
Output));
}

void SPIRV::Translator::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
Expand Down Expand Up @@ -121,6 +143,10 @@ void SPIRV::Linker::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfoList &Inputs,
const ArgList &Args,
const char *LinkingOutput) const {
if (JA.getType() == types::TY_LLVM_BC) {
constructLLVMLinkCommand(C, *this, JA, Output, Inputs, {});
return;
}
const ToolChain &ToolChain = getToolChain();
std::string Linker = ToolChain.GetProgramPath(getShortName());
ArgStringList CmdArgs;
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Driver/ToolChains/SPIRV.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ void constructAssembleCommand(Compilation &C, const Tool &T,
const InputInfo &Input,
const llvm::opt::ArgStringList &Args);

void constructLLVMLinkCommand(Compilation &C, const Tool &T,
const JobAction &JA, const InputInfo &Output,
const InputInfoList &Inputs,
const llvm::opt::ArgStringList &Args);

class LLVM_LIBRARY_VISIBILITY Translator : public Tool {
public:
Translator(const ToolChain &TC)
Expand Down
31 changes: 31 additions & 0 deletions clang/test/Driver/spirv-llvm-link.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Check BC input
// RUN: mkdir -p %t
// RUN: touch %t/a.bc
// RUN: touch %t/b.bc
// RUN: %clang -### --target=spirv64 -emit-llvm %t/a.bc %t/b.bc 2>&1 | FileCheck --check-prefix=CHECK-TOOL-BC %s

// CHECK-TOOL-BC: "-cc1" {{.*}} "-o" "[[TMP1_BC:.+]]" "-x" "ir" "{{.*}}.bc"
// CHECK-TOOL-BC: "-cc1" {{.*}} "-o" "[[TMP2_BC:.+]]" "-x" "ir" "{{.*}}.bc"
// CHECK-TOOL-BC: llvm-link{{.*}} "-o" {{.*}} "[[TMP1_BC]]" "[[TMP2_BC]]"

// RUN: %clang -ccc-print-bindings --target=spirv64 -emit-llvm %t/a.bc %t/b.bc 2>&1 | FileCheck -check-prefix=CHECK-BINDINGS-BC %s

// CHECK-BINDINGS-BC: "spirv64" - "clang", inputs: ["{{.*}}.bc"], output: "[[TMP1_BINDINGS_BC:.+]]"
// CHECK-BINDINGS-BC: "spirv64" - "clang", inputs: ["{{.*}}.bc"], output: "[[TMP2_BINDINGS_BC:.+]]"
// CHECK-BINDINGS-BC: "spirv64" - "SPIR-V::Linker", inputs: ["[[TMP1_BINDINGS_BC]]", "[[TMP2_BINDINGS_BC]]"], output: "{{.*}}.bc"

// Check source input
// RUN: touch %t/foo.c
// RUN: touch %t/bar.c

// RUN: %clang -### --target=spirv64 -emit-llvm %t/foo.c %t/bar.c 2>&1 | FileCheck --check-prefix=CHECK-TOOL-SRC %s

// CHECK-TOOL-SRC: "-cc1" {{.*}} "-o" "[[TMP1_SRC_BC:.+]]" "-x" "c" "{{.*}}foo.c"
// CHECK-TOOL-SRC: "-cc1" {{.*}} "-o" "[[TMP2_SRC_BC:.+]]" "-x" "c" "{{.*}}bar.c"
// CHECK-TOOL-SRC: llvm-link{{.*}} "-o" {{.*}} "[[TMP1_SRC_BC]]" "[[TMP2_SRC_BC]]"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if that's a good idea to call llvm-link directly. Can't we just call clang?

Copy link
Member Author

@sarnex sarnex Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the problem we're trying to solve, how can we link bitcode with clang directly? The only option I know would be the mlink_bitcode_file group of options, and those are really more for linking a BC file into source code being compiled, rather than to link a group of BC files.
Also, AMD also calls llvm-link directly in their equivalent of this flag --hip-link -emit-llvm.


// RUN: %clang -ccc-print-bindings --target=spirv64 -emit-llvm %t/foo.c %t/bar.c 2>&1 | FileCheck -check-prefix=CHECK-BINDINGS-SRC %s

// CHECK-BINDINGS-SRC: "spirv64" - "clang", inputs: ["{{.*}}foo.c"], output: "[[TMP1_BINDINGS_SRC_BC:.+]]"
// CHECK-BINDINGS-SRC: "spirv64" - "clang", inputs: ["{{.*}}bar.c"], output: "[[TMP2_BINDINGS_SRC_BC:.+]]"
// CHECK-BINDINGS-SRC: "spirv64" - "SPIR-V::Linker", inputs: ["[[TMP1_BINDINGS_SRC_BC]]", "[[TMP2_BINDINGS_SRC_BC]]"], output: "{{.*}}.bc"