-
Notifications
You must be signed in to change notification settings - Fork 10.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Driver][BoundsSafety] Add -fexperimental-bounds-safety flag #70480
base: main
Are you sure you want to change the base?
Conversation
-fbounds-safety-experimental is an experimental flag for -fbounds-safety, which is a bounds-safety extension for C. -fbounds-safety will require substantial changes across the Clang codebase. So we introduce this experimental flag is to gate our incremental patches until we push the essential functionality of the extension. -fbounds-safety-experimental currently doesn't do anything but reporting an error when the flag is used with an unsupported source language (currently only supports C).
I'll push BoundsSafety.rst documentation in a separate PR. Update: PR open here: #70749 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general looks good but there are some minor changes I'd like made.
@@ -0,0 +1,5 @@ | |||
; RUN: %clang -fbounds-safety-experimental -x ir -S %s -o /dev/null 2>&1 | FileCheck %s | |||
; RUN: %clang_cc1 -fbounds-safety-experimental -x ir -S %s -o /dev/null 2>&1 | FileCheck %s | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: It might be worth having a small comment here explaining why you expect these diagnostics to not fire.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We usually test %clang
for driver and %clang_cc1
for the others. Also, if we test %clang_cc1
, we omit %clang
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MaskRay fixed!
Also, LLVM IR and Unknown must be unreachable when parsing the language arguments.
5f241a6
to
8049f6f
Compare
def err_bounds_safety_lang_not_supported : Error< | ||
"bounds safety is only supported for C">; | ||
def warn_bounds_safety_asm_ignored : Warning< | ||
"'-fbounds-safety' is ignored for assembly">, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit. In this PR the flag is -fbounds-safety-experimental
not -fbounds-safety
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For assembly input, we generally ignore handling the option and leave a warning https://maskray.me/blog/2023-08-25-clang-wunused-command-line-argument#assembly-files
% fclang -fsanitize=address -fpatchable-function-entry=2 -c a.s
clang: warning: argument unused during compilation: '-fsanitize=address' [-Wunused-command-line-argument]
clang: warning: argument unused during compilation: '-fpatchable-function-entry=2' [-Wunused-command-line-argument]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MaskRay Thank you! I can use warning: argument unused during compilation
for the driver. Do you know what is the convention for unused flags in cc1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://maskray.me/blog/2023-08-25-clang-wunused-command-line-argument has some notes. If you don't call functions like getLastArg
that claim
the option, there will be a warning: argument unused during compilation: '-fsanitize=address' [-Wunused-command-line-argument]
for assembly input.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MaskRay So we aren't explicit using getLastArg
but it looks like we are calling it indirectly from addOptInFlag(...)
which I think means we are unconditionally claiming the option. In order to make your suggestion work we'd have to conditionally call addOptInFlag
in the driver based on the selected input language but I don't think that's going to work because no LangOptions
object exists for us query at that point in the driver AFAICT.
Also future patches we will upstream will add additional Args.getLastArg(options::OPT_fbounds_safety_experimental, ...)
calls in the driver which means the option will get claimed there too which will break any reliance on the option not being claimed that might be implemented in this PR.
This warning is really a frontend warning and not a driver warning so I think we should use the frontend warning that @rapidsna introduced in this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MaskRay Thanks. I still need the error for -fbounds-safety
not being supported in C++/Obj-C/etc in the frontend because it needs to be an error for cc1
.
I will change it so that the unused warning for assembly to be handled in the driver like the rest of the unused options for assembly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Conventionally the language compatibility checking and other checking is performed in Driver, not in Frontend.. If you move the language check to Driver, the diagnostic will be natural since clang integrated assembler uses
ClangAs
instead ofClang
.
@MaskRay It seems both Clang
and ClangAs
are invoked for the clang
command with an assembly input (i.e., ClangAs
is invoked after Clang
) so it seems to me that most options are already claimed
in Clang
and so warning: argument unused during compilation
doesn't seem to fire for clang
in most cases including -fsanitize=address
.
Also, we need to have the input language (InputKind
) to report the right diagnostics so the frontend still seems like the right place to handle this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MaskRay Sorry, it seems we can still do it in Driver with clang::driver::types
. I'm going to make a change to report the "unused" warning from the driver, and report the "unsupported language" error in the frontend. Does it sound okay to you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MaskRay It seems both
Clang
andClangAs
are invoked for theclang
command with an assembly input (i.e.,ClangAs
is invoked afterClang
) so it seems to me that most options are alreadyclaimed
inClang
and sowarning: argument unused during compilation
doesn't seem to fire forclang
in most cases including-fsanitize=address
.
I just confirmed that this is happening when the input file format is .s
and no -x
is provided from the command line. If we do -x assembler
then only ClangAs
is launched and the unused option warning fires as expected.
@MaskRay Is it expected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MaskRay I removed the frontend warning, so that it naturally follows how the driver handles other unused flags. Now only warning: argument unused during compilation
fires for -x assembler
, and not for -x assembler-with-cpp
to conform to most of other options.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rapidsna LGTM other than the nits.
The other experimental flags I see start with experimental, this one ends with it. Why isn't this called |
Oh, I can see most of them start with |
def warn_bounds_safety_asm_ignored : Warning< | ||
"'-fbounds-safety' is ignored for assembly">, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we even need a diagnostic specific to assembler? I get the point that some projects Makefiles pass compiler flags to the assembler invocation (which in the case of LLVM based builds, the assembler is clang). But to me "bounds safety is only supported for C" is a superset of of "is ignored for assembly." Seems like we could get away with only one diagnostic (the first "is not C" one).
Or was this particularly helpful for teams that got confused as to why the compiler (invoked as the assembler) was yelling at them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nickdesaulniers They are separate because the languages (C++/Obj-C/etc) we want to make an error until we have a proper support vs a warning for assembly (or maybe others?) that are meant to be ignored. For the latter I think we can use warning: argument unused during compilation
as @MaskRay suggested.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nickdesaulniers I managed to move -x ir test to the .c
test. Not sure why but adding -###
option seemed to make it work.
@@ -0,0 +1,11 @@ | |||
// RUN: %clang -c %s -### 2>&1 | FileCheck -check-prefix T0 %s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would have expected a test like this to be added to clang/test/Driver/clang_f_opts.c
why are the new tests in clang/test/BoundsSafety/ rather than the existing directory structures? I'm guessing to avoid merge conflicts when rebasing against upstream? Either way, let's conform to the existing layout?
This comment applies to ALL newly added test files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clang/test/Driver/clang_f_opts.c
mixes a lot of options. Newer -fxxx
tests can go to a dedicated clang/test/Driver/fxxx.c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nickdesaulniers I was hoping that we could keep -fbounds-safety tests under a separate folder clang/test/BoundsSafety in order to avoid them from getting mixed up with other tests. And make it easier to run bounds safety related tests only. But I can see this is not what other extensions do so I can move them to the existing layout.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nickdesaulniers As @rapidsna said there are multiple reasons:
- Provides a very convenient way to run only
-fbounds-safety
tests. - More likely to avoid merge conflicts with our internal code.
Out of those two I think (1.)
is the bigger problem because it is something I rely on for local development and I think merge conflicts are still unlikely even if we put tests under their proper location under clang/test
because we mostly created new tests and tried not to touch existing tests. If lit
had a way of labelling tests and only running tests with a particular label then the location of the tests would be irrelevant and I would be happy for the tests to live in their normal place under clang/test/
(e.g. clang/test/Driver
). Unfortunately AFAIK that feature doesn't exist in lit.
To workaround this would this kind of layout be acceptable?
test/Driver/BoundsSafety/...
test/Frontend/BoundsSafety/...
test/Sema/BoundsSafety/...
...
That way the tests are conforming to Clang's test layout but its still possible to run only the -fbounds-safety tests by doing something like:
$ bin/llvm-lit -vs tools/clang/test/{Driver,Frontend,Sema}/BoundsSafety
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test subdirectories are added conservatively. The main use cases are target-specific tests and flang. Driver/XRay
is somehow an unneeded use that you can ignore.
% echo {CodeGen,Sema,Driver}/*(/)
CodeGen/aarch64_neon_sve_bridge_intrinsics CodeGen/aarch64-sme-intrinsics CodeGen/aarch64-sve2-intrinsics CodeGen/aarch64-sve2p1-intrinsics CodeGen/aarch64-sve-intrinsics CodeGen/arc CodeGen/arm-mve-intrinsics CodeGen/assignment-tracking CodeGen/avr CodeGen/CSKY CodeGen/Inputs CodeGen/LoongArch CodeGen/PowerPC CodeGen/RISCV CodeGen/SystemZ CodeGen/VE CodeGen/WebAssembly CodeGen/X86 Sema/aarch64-sme-intrinsics Sema/aarch64-sve2-intrinsics Sema/aarch64-sve2p1-intrinsics Sema/aarch64-sve-intrinsics Sema/Inputs Driver/flang Driver/Inputs Driver/XRay
FWIW you can do
path/bin/llvm-lit clang/test/Driver/bounds-safety-*
LIT_OPTS=--filter=bounds-safety path/bin/llvm-lit clang/test
and even create an alias for LIT_OPTS=--filter=bounds-safety
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And make it easier to run bounds safety related tests only.
Provides a very convenient way to run only -fbounds-safety tests.
Then give each test a similar function name prefix, and then you can do llvm-lit -vv llvm/test/Driver/fbounds-safety-*
, as @MaskRay said. Don't start creating new directory structures that don't exist for every other feature implemented by the compiler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nickdesaulniers @MaskRay Thank you! I removed the new directory and moved the tests to conform to the existing layout.
|
||
// RUN: not %clang_cc1 -fbounds-safety-experimental -x cuda %s 2>&1 | FileCheck -check-prefix ERR %s | ||
|
||
// RUN: not %clang_cc1 -fbounds-safety-experimental -x renderscript %s 2>&1 | FileCheck -check-prefix ERR %s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not test -x ir
and -x assembler
in the same test file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think at least -x ir
had to be a separate file because how it recognize comments (//
vs ;
). I'll double check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, makes sense, consider adding that as a comment
case Language::Asm: | ||
Diags.Report(diag::warn_bounds_safety_asm_ignored); | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if I'm compiling -x assembler-with-cpp
(i.e. assembler that needs to be preprocessed, .S
files)?
Does this error?
What if I'm using the preprocessor to detect if -fbounds-safety
is supported?
#if __has_attribute(bidi_whatever)
mov x0, #42
#else
#error "oh no"
#endif
(or perhaps __has_feature(bounds_safety)
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, this is something we could potentially allow in future if we see actual use cases.
Until we support this, __has_feature(bounds_safety)
or such will return false
and we should have the compiler report the warning that -fbounds-safety
is ignored for assemblers.
def err_bounds_safety_lang_not_supported : Error< | ||
"bounds safety is only supported for C">; | ||
def warn_bounds_safety_asm_ignored : Warning< | ||
"'-fbounds-safety' is ignored for assembly">, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For assembly input, we generally ignore handling the option and leave a warning https://maskray.me/blog/2023-08-25-clang-wunused-command-line-argument#assembly-files
% fclang -fsanitize=address -fpatchable-function-entry=2 -c a.s
clang: warning: argument unused during compilation: '-fsanitize=address' [-Wunused-command-line-argument]
clang: warning: argument unused during compilation: '-fpatchable-function-entry=2' [-Wunused-command-line-argument]
@@ -0,0 +1,25 @@ | |||
// RUN: not %clang -fbounds-safety-experimental -x c++ %s 2>&1 | FileCheck -check-prefix ERR %s | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use a terse style and use a blank line to group different sets of tests. Here the blank line use is unnecessary and harms readability IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MaskRay Thank you! I removed the blank lines.
@tbaederr Thank you! I just renamed the flag to |
✅ With the latest revision this PR passed the C/C++ code formatter. |
|
||
// expected-no-diagnostics | ||
// RUN: %clang -fexperimental-bounds-safety -Xclang -verify -c -x c %s -o /dev/null | ||
// Unlike '-x assembler', '-x assembler-with-cpp' silently ignores unused options by default. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nickdesaulniers The unused warning doesn't appear for -x assembler-with-cpp
to follow the common behaviors. Instead, I added a note here that we need to add a targeted warning in future when assembler tries to use preprocessor directives to check if bounds safety ie enabled. WDYT?
@@ -0,0 +1,12 @@ | |||
// This reports a warning to follow the default behavior of ClangAs. | |||
// RUN: %clang -fexperimental-bounds-safety -x assembler -c %s -o /dev/null 2>&1 | FileCheck -check-prefix WARN %s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rapidsna Maybe there should be a version of this part of the test that doesn't pass -x <lang>
and instead passes to a .s
file to make sure the right thing happens when in that case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think testing with -x assembler
and -x assembler-with-cpp
are more specific and should cover what I want.
.s
must be -x assembler
(except for Darwin which does -x assembler-with-cpp
for .s
), but that specific behavior should be tested separately by related updates, and not in this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you make all of the newly added tests share a similar filename prefix, it will be easier to test via llvm-lit -vv clang/test/Driver/bounds-safety*
. fbounds-safety.c
seems to break that convention.
// expected-no-diagnostics | ||
// RUN: %clang -fexperimental-bounds-safety -Xclang -verify -c -x c %s -o /dev/null | ||
// Unlike '-x assembler', '-x assembler-with-cpp' silently ignores unused options by default. | ||
// XXX: Should report a targeted warning in future when assembler tries to use preprocessor directives to conditionalize behavior when bounds safety is enabled. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@delcypher Perfect, thanks!
@@ -3618,6 +3618,27 @@ void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts, | |||
GenerateArg(Consumer, OPT_frandomize_layout_seed_EQ, Opts.RandstructSeed); | |||
} | |||
|
|||
static void CheckBoundsSafetyLang(InputKind IK, DiagnosticsEngine &Diags) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to handle this language check in Driver, similar to OPT_fminimize_whitespace
but for all Inputs
.
It's not necessary to mention
This is especially important because some build systems, like xcbuild and somewhat clumsy Makefiles, will pass
C_FLAGS to Clang while building assembly files.
since this is general and applies to other options. No need to emphasize it specifically for bounds checking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to handle this language check in Driver, similar to OPT_fminimize_whitespace but for all Inputs.
@MaskRay If we do it in Driver, wouldn't the option be silently ignored for unsupported languages when we invoke clang -cc1
directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO, that might be problematic because people would reasonably expect bounds-safety would work for C++/Obj-C/Obj-C++.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are various different options here.
- The frontend silently accepts
-fexperimental-bounds-safety
with-x c++
but doesn't actually enablefexperimental-bounds-safety
. - The frontend accepts
-fexperimental-bounds-safety
with-x c++
enables-fexperimental-bounds-safety
and probably crashes because it's not supported. - The frontend rejects
-fexperimental-bounds-safety
with-x c++
with an error.
I don't like (1.) because when writing frontend only tests it is inviting someone to accidentally write a test incorrectly because the frontend would never complain about the invalid configuration.
I don't like (2.) because crashing really isn't good when it could so easily be prevented.
I think we should do (3.) because it has none of the previously mentioned downsides.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be clear. I think it's fine to also do the language check in the driver but I think the frontend should also do language check so that it is not possible to run it in an invalid configuration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to handle this language check in Driver, similar to
OPT_fminimize_whitespace
but for allInputs
.
@MaskRay Thanks for the review! I made it a driver error here:
f125532
We also keep the -cc1 check in order to prevent it from running with an unsupported mode (we still need some checks in cc1 anyway in case we want to silently ignore the option, instead of triggering the broken mode).
@@ -1412,6 +1412,9 @@ def FunctionMultiVersioning | |||
|
|||
def NoDeref : DiagGroup<"noderef">; | |||
|
|||
// Bounds safety specific warnings | |||
def IgnoredBoundsSafety : DiagGroup<"ignored-bounds-safety">; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is -Wignored-bounds-safety
is not used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed it!
The patch implements |
I just updated the title. I'll make sure I update the text when I squash the commits. |
It depends whether an option leads to a warning or is silently ignored for a language that is not implemented. Sometimes, it is useful report a warning/error to prevent misuses. However, implementing the checking in clang/lib/Driver is much more common. I am not convinced by your argument that you want to catch -cc1 misuses. |
@MaskRay To be clear, the check is now in Driver as you suggested. It's just that the frontend also has some extra checks too. So, you want me to remove the extra checks in the frontend? |
Ok. I'll fix this. |
Yes, otherwise it's duplicated check. In addition, I think our convention is to add the option when the feature is ready, rather than add a no-op option, than build functional patches on top of it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-fbounds-safety-experimental is an experimental flag for -fbounds-safety,
-fexperimental-bounds-safety
Changed the description in the PR! I'll adjust the commit message too when I squash all the changes once I get your approval. |
I just removed the check in the frontend! |
@MaskRay This feature will involve a lot of incremental patches. And we will still need an experimental flag to test the incremental functionalities that we will be adding. I can make it a CC1 only flag for now. Would it work for you better? |
Yes. This sound good to me. You will need to remove the lib/Driver change and the language compatibility check, then. I think the patch should still be held off until the first patch that does some basic functionality is ready. |
-fexperimental-bounds-safety is an experimental flag for -fbounds-safety, which is a bounds-safety extension for C. -fbounds-safety will require substantial changes across the Clang codebase. So we introduce this experimental flag is to gate our incremental patches until we push the essential functionality of the extension.
-fexperimental-bounds-safety currently doesn't do anything but reporting an error when the flag is used with an unsupported source language (currently only supports C).