Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 6 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -5880,6 +5880,12 @@ def o : JoinedOrSeparate<["-"], "o">,
Visibility<[ClangOption, CC1Option, CC1AsOption, FC1Option, FlangOption]>,
HelpText<"Write output to <file>">, MetaVarName<"<file>">,
MarshallingInfoString<FrontendOpts<"OutputFile">>;
def foutput_file_base : Joined<["-"], "foutput-file-base=">,
Copy link
Member

Choose a reason for hiding this comment

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

Is this a new driver option? For extra output files we can use -dumpdir https://maskray.me/blog/2023-04-25-compiler-output-files

Copy link
Collaborator Author

@jdenny-ornl jdenny-ornl Jul 17, 2025

Choose a reason for hiding this comment

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

Thanks, I didn't know about -dumpdir. It would indeed be better to use an existing option.

However, I ran into a couple of issues:

  1. The primary output file name has an arbitrary hash because it goes in /tmp. For example: /tmp/a.out.amdgcn.gfx906-78e4d4.img. With the -dumpdir solution, the auxiliary output files, which the user actually wants, would end up with those too, making their names unpredictable. Would implementing gcc's -dumpbase be the right way to avoid that?
  2. Because -foutput-file-base is an internal option not really meant for users, I took the liberty of having Clang always disable any warnings about it being unused. Is there some way for clang-linker-wrapper to tell Clang not to warn if -dumpdir is unused without suppressing warnings about all other options? Of course, we could add another option for that, but I'm hoping there's some existing way.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For 2, I just discovered --start-no-unused-arguments, so never mind that one.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For 2, I later discovered that unused warnings don't happen for -dumpdir in this case anyway due to another use of it.

For 1, I had misunderstood the behavior of -dumpdir: it replaces the output file name too, not just its directory. So, I've replaced my -foutput-file-base with -dumpdir.

I ran into a potential issue: The clang driver passes -dumpdir to various clang frontend calls. For LTO, that was previously being ignored, and now it's not. That changes some auxiliary file names, as revealed by changes in some existing tests' expected output: clang/test/Driver/opt-record.c and clang/test/Driver/lto-dwo.c. Are there backward compatibility concerns here?

Flags<[NoXarchOption, HelpHidden]>,
Visibility<[ClangOption]>,
HelpText<"Name extra output files after <base> not the main output file">,
MetaVarName<"<base>">,
MarshallingInfoString<FrontendOpts<"OutputFileBase">>;
def object_file_name_EQ : Joined<["-"], "object-file-name=">,
Visibility<[ClangOption, CC1Option, CC1AsOption, CLOption, DXCOption]>,
HelpText<"Set the output <file> for debug infos">, MetaVarName<"<file>">,
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Frontend/FrontendOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@ class FrontendOptions {
/// The output file, if any.
std::string OutputFile;

/// The base, if any, to use instead of OutputFile for extra output files.
std::string OutputFileBase;

/// If given, the new suffix for fix-it rewritten files.
std::string FixItSuffix;

Expand Down
8 changes: 6 additions & 2 deletions clang/lib/Driver/ToolChains/CommonArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,9 @@ static void renderRemarksOptions(const ArgList &Args, ArgStringList &CmdArgs,
Format = A->getValue();

SmallString<128> F;
const Arg *A = Args.getLastArg(options::OPT_foptimization_record_file_EQ);
if (A)
if (const Arg *A = Args.getLastArg(options::OPT_foptimization_record_file_EQ))
F = A->getValue();
else if (const Arg *A = Args.getLastArg(options::OPT_foutput_file_base))
F = A->getValue();
else if (Output.isFilename())
F = Output.getFilename();
Expand Down Expand Up @@ -1320,6 +1321,9 @@ void tools::addLTOOptions(const ToolChain &ToolChain, const ArgList &Args,
if (Args.hasArg(options::OPT_ftime_report))
CmdArgs.push_back(
Args.MakeArgString(Twine(PluginOptPrefix) + "-time-passes"));

// clang-linker-wrapper adds this without checking if it is needed.
Args.ClaimAllArgs(options::OPT_foutput_file_base);
}

void tools::addOpenMPRuntimeLibraryPath(const ToolChain &TC,
Expand Down
32 changes: 16 additions & 16 deletions clang/test/Driver/linker-wrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ __attribute__((visibility("protected"), used)) int x;
// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
// RUN: --linker-path=/usr/bin/ld %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=NVPTX-LINK

// NVPTX-LINK: clang{{.*}} -o {{.*}}.img --target=nvptx64-nvidia-cuda -march=sm_70 {{.*}}.o {{.*}}.o
// NVPTX-LINK: clang{{.*}} -o {{.*}}.img -foutput-file-base=a.out.nvptx64.sm_70.img --target=nvptx64-nvidia-cuda -march=sm_70 {{.*}}.o {{.*}}.o

// RUN: clang-offload-packager -o %t.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70 \
Expand All @@ -40,7 +40,7 @@ __attribute__((visibility("protected"), used)) int x;
// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
// RUN: --linker-path=/usr/bin/ld %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=AMDGPU-LINK

// AMDGPU-LINK: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa -mcpu=gfx908 -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o
// AMDGPU-LINK: clang{{.*}} -o {{.*}}.img -foutput-file-base=a.out.amdgcn.gfx908.img --target=amdgcn-amd-amdhsa -mcpu=gfx908 -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o

// RUN: clang-offload-packager -o %t.out \
// RUN: --image=file=%t.amdgpu.bc,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx1030 \
Expand All @@ -57,7 +57,7 @@ __attribute__((visibility("protected"), used)) int x;
// RUN: not clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
// RUN: --linker-path=/usr/bin/ld %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=SPIRV-LINK

// SPIRV-LINK: clang{{.*}} -o {{.*}}.img --target=spirv64-unknown-unknown {{.*}}.o --sycl-link -Xlinker -triple=spirv64-unknown-unknown -Xlinker -arch=
// SPIRV-LINK: clang{{.*}} -o {{.*}}.img -foutput-file-base=a.out.spirv64..img --target=spirv64-unknown-unknown {{.*}}.o --sycl-link -Xlinker -triple=spirv64-unknown-unknown -Xlinker -arch=

// RUN: clang-offload-packager -o %t.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=x86_64-unknown-linux-gnu \
Expand All @@ -68,7 +68,7 @@ __attribute__((visibility("protected"), used)) int x;
// RUN: --linker-path=/usr/bin/ld.lld --whole-archive %t.a --no-whole-archive \
// RUN: %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=CPU-LINK

// CPU-LINK: clang{{.*}} -o {{.*}}.img --target=x86_64-unknown-linux-gnu -Wl,--no-undefined {{.*}}.o {{.*}}.o -Wl,-Bsymbolic -shared -Wl,--whole-archive {{.*}}.a -Wl,--no-whole-archive
// CPU-LINK: clang{{.*}} -o {{.*}}.img -foutput-file-base=a.out.x86_64..img --target=x86_64-unknown-linux-gnu -Wl,--no-undefined {{.*}}.o {{.*}}.o -Wl,-Bsymbolic -shared -Wl,--whole-archive {{.*}}.a -Wl,--no-whole-archive

// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o
// RUN: clang-linker-wrapper --dry-run --host-triple=x86_64-unknown-linux-gnu -mllvm -openmp-opt-disable \
Expand Down Expand Up @@ -100,8 +100,8 @@ __attribute__((visibility("protected"), used)) int x;
// RUN: clang-linker-wrapper --dry-run --host-triple=x86_64-unknown-linux-gnu \
// RUN: --linker-path=/usr/bin/ld %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=CUDA

// CUDA: clang{{.*}} -o [[IMG_SM70:.+]] --target=nvptx64-nvidia-cuda -march=sm_70
// CUDA: clang{{.*}} -o [[IMG_SM52:.+]] --target=nvptx64-nvidia-cuda -march=sm_52
// CUDA: clang{{.*}} -o [[IMG_SM70:.+]] -foutput-file-base=a.out.nvptx64.sm_70.img --target=nvptx64-nvidia-cuda -march=sm_70
// CUDA: clang{{.*}} -o [[IMG_SM52:.+]] -foutput-file-base=a.out.nvptx64.sm_52.img --target=nvptx64-nvidia-cuda -march=sm_52
// CUDA: fatbinary{{.*}}-64 --create {{.*}}.fatbin --image=profile=sm_70,file=[[IMG_SM70]] --image=profile=sm_52,file=[[IMG_SM52]]
// CUDA: usr/bin/ld{{.*}} {{.*}}.openmp.image.{{.*}}.o {{.*}}.cuda.image.{{.*}}.o

Expand All @@ -127,8 +127,8 @@ __attribute__((visibility("protected"), used)) int x;
// RUN: --compress --compression-level=6 \
// RUN: --linker-path=/usr/bin/ld %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=HIP

// HIP: clang{{.*}} -o [[IMG_GFX90A:.+]] --target=amdgcn-amd-amdhsa -mcpu=gfx90a
// HIP: clang{{.*}} -o [[IMG_GFX908:.+]] --target=amdgcn-amd-amdhsa -mcpu=gfx908
// HIP: clang{{.*}} -o [[IMG_GFX90A:.+]] -foutput-file-base=a.out.amdgcn.gfx90a.img --target=amdgcn-amd-amdhsa -mcpu=gfx90a
// HIP: clang{{.*}} -o [[IMG_GFX908:.+]] -foutput-file-base=a.out.amdgcn.gfx908.img --target=amdgcn-amd-amdhsa -mcpu=gfx908
// HIP: clang-offload-bundler{{.*}}-type=o -bundle-align=4096 -compress -compression-level=6 -targets=host-x86_64-unknown-linux-gnu,hip-amdgcn-amd-amdhsa--gfx90a,hip-amdgcn-amd-amdhsa--gfx908 -input={{/dev/null|NUL}} -input=[[IMG_GFX90A]] -input=[[IMG_GFX908]] -output={{.*}}.hipfb

// RUN: clang-offload-packager -o %t.out \
Expand Down Expand Up @@ -157,7 +157,7 @@ __attribute__((visibility("protected"), used)) int x;
// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run --clang-backend \
// RUN: --linker-path=/usr/bin/ld %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=CLANG-BACKEND

// CLANG-BACKEND: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa -mcpu=gfx908 -flto -Wl,--no-undefined {{.*}}.o
// CLANG-BACKEND: clang{{.*}} -o {{.*}}.img -foutput-file-base=a.out.amdgcn.gfx908.img --target=amdgcn-amd-amdhsa -mcpu=gfx908 -flto -Wl,--no-undefined {{.*}}.o

// RUN: clang-offload-packager -o %t.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70
Expand All @@ -180,8 +180,8 @@ __attribute__((visibility("protected"), used)) int x;
// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
// RUN: --linker-path=/usr/bin/ld %t-on.o %t-off.o %t.a -o a.out 2>&1 | FileCheck %s --check-prefix=AMD-TARGET-ID

// AMD-TARGET-ID: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa -mcpu=gfx90a:xnack+ -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o
// AMD-TARGET-ID: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa -mcpu=gfx90a:xnack- -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o
// AMD-TARGET-ID: clang{{.*}} -o {{.*}}.img -foutput-file-base=a.out.amdgcn.gfx90a:xnack+.img --target=amdgcn-amd-amdhsa -mcpu=gfx90a:xnack+ -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o
// AMD-TARGET-ID: clang{{.*}} -o {{.*}}.img -foutput-file-base=a.out.amdgcn.gfx90a:xnack-.img --target=amdgcn-amd-amdhsa -mcpu=gfx90a:xnack- -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o

// RUN: clang-offload-packager -o %t-lib.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=generic
Expand All @@ -196,8 +196,8 @@ __attribute__((visibility("protected"), used)) int x;
// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
// RUN: --linker-path=/usr/bin/ld %t1.o %t2.o %t.a -o a.out 2>&1 | FileCheck %s --check-prefix=ARCH-ALL

// ARCH-ALL: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa -mcpu=gfx90a -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o
// ARCH-ALL: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa -mcpu=gfx908 -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o
// ARCH-ALL: clang{{.*}} -o {{.*}}.img -foutput-file-base=a.out.amdgcn.gfx90a.img --target=amdgcn-amd-amdhsa -mcpu=gfx90a -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o
// ARCH-ALL: clang{{.*}} -o {{.*}}.img -foutput-file-base=a.out.amdgcn.gfx908.img --target=amdgcn-amd-amdhsa -mcpu=gfx908 -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o

// RUN: clang-offload-packager -o %t.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=x86_64-unknown-linux-gnu \
Expand All @@ -207,7 +207,7 @@ __attribute__((visibility("protected"), used)) int x;
// RUN: --linker-path=/usr/bin/ld.lld -r %t.o \
// RUN: %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=RELOCATABLE-LINK

// RELOCATABLE-LINK: clang{{.*}} -o {{.*}}.img --target=x86_64-unknown-linux-gnu
// RELOCATABLE-LINK: clang{{.*}} -o {{.*}}.img -foutput-file-base=a.out.x86_64..img --target=x86_64-unknown-linux-gnu
// RELOCATABLE-LINK: /usr/bin/ld.lld{{.*}}-r
// RELOCATABLE-LINK: llvm-objcopy{{.*}}a.out --remove-section .llvm.offloading

Expand All @@ -219,7 +219,7 @@ __attribute__((visibility("protected"), used)) int x;
// RUN: --linker-path=/usr/bin/ld.lld -r %t.o \
// RUN: %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=RELOCATABLE-LINK-HIP

// RELOCATABLE-LINK-HIP: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa
// RELOCATABLE-LINK-HIP: clang{{.*}} -o {{.*}}.img -foutput-file-base=a.out.amdgcn.gfx90a.img --target=amdgcn-amd-amdhsa
// RELOCATABLE-LINK-HIP: clang-offload-bundler{{.*}} -type=o -bundle-align=4096 -targets=host-x86_64-unknown-linux-gnu,hip-amdgcn-amd-amdhsa--gfx90a -input={{/dev/null|NUL}} -input={{.*}} -output={{.*}}
// RELOCATABLE-LINK-HIP: /usr/bin/ld.lld{{.*}}-r
// RELOCATABLE-LINK-HIP: llvm-objcopy{{.*}}a.out --remove-section .llvm.offloading
Expand All @@ -233,7 +233,7 @@ __attribute__((visibility("protected"), used)) int x;
// RUN: --linker-path=/usr/bin/ld.lld -r %t.o \
// RUN: %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=RELOCATABLE-LINK-CUDA

// RELOCATABLE-LINK-CUDA: clang{{.*}} -o {{.*}}.img --target=nvptx64-nvidia-cuda
// RELOCATABLE-LINK-CUDA: clang{{.*}} -o {{.*}}.img -foutput-file-base=a.out.nvptx64.sm_89.img --target=nvptx64-nvidia-cuda
// RELOCATABLE-LINK-CUDA: fatbinary{{.*}} -64 --create {{.*}}.fatbin --image=profile=sm_89,file={{.*}}.img
// RELOCATABLE-LINK-CUDA: /usr/bin/ld.lld{{.*}}-r
// RELOCATABLE-LINK-CUDA: llvm-objcopy{{.*}}a.out --remove-section .llvm.offloading
Expand Down
11 changes: 11 additions & 0 deletions clang/test/Driver/opt-record.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,14 @@
// CHECK-PASS-RPASS-SAME: "-plugin-opt=opt-remarks-hotness-threshold=100"

// CHECK-PASS-AUTO: "-plugin-opt=opt-remarks-hotness-threshold=auto"

// Check -foutput-file-base effect on -foptimization-record-file.
// RUN: %clang --target=x86_64-linux -### -fuse-ld=lld -B%S/Inputs/lld -flto -fsave-optimization-record -foutput-file-base=/dir/file.ext %s 2>&1 | FileCheck %s -check-prefix=CHECK-BASE
// RUN: %clang --target=x86_64-linux -### -o FOO -fuse-ld=lld -B%S/Inputs/lld -flto -fsave-optimization-record -foutput-file-base=/dir/file.ext %s 2>&1 | FileCheck %s -check-prefix=CHECK-BASE
// RUN: %clang --target=x86_64-linux -### -fuse-ld=lld -B%S/Inputs/lld -flto -fsave-optimization-record -foptimization-record-file=user-file.ext -foutput-file-base=/dir/file.ext %s 2>&1 | FileCheck %s -check-prefix=CHECK-IGNORE-BASE

// CHECK-BASE: "-plugin-opt=opt-remarks-filename=/dir/file.ext.opt.ld.yaml"
// CHECK-BASE-SAME: "-plugin-opt=opt-remarks-format=yaml"

// CHECK-IGNORE-BASE: "-plugin-opt=opt-remarks-filename=user-file.ext.opt.ld.yaml"
// CHECK-IGNORE-BASE-SAME: "-plugin-opt=opt-remarks-format=yaml"
11 changes: 7 additions & 4 deletions clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include "clang/Basic/TargetID.h"
#include "clang/Basic/Version.h"
#include "clang/Driver/Options.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/BinaryFormat/Magic.h"
#include "llvm/Bitcode/BitcodeWriter.h"
Expand Down Expand Up @@ -474,10 +475,10 @@ Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, const ArgList &Args,
StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);
// Create a new file to write the linked device image to. Assume that the
// input filename already has the device and architecture.
auto TempFileOrErr =
createOutputFile(sys::path::filename(ExecutableName) + "." +
Triple.getArchName() + "." + Arch,
"img");
std::string OutputFileBase =
"." + Triple.getArchName().str() + "." + Arch.str();
auto TempFileOrErr = createOutputFile(
sys::path::filename(ExecutableName) + OutputFileBase, "img");
if (!TempFileOrErr)
return TempFileOrErr.takeError();

Expand All @@ -486,6 +487,8 @@ Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, const ArgList &Args,
"--no-default-config",
"-o",
*TempFileOrErr,
Args.MakeArgString("-foutput-file-base=" + ExecutableName +
OutputFileBase + ".img"),
Args.MakeArgString("--target=" + Triple.getTriple()),
};

Expand Down