This attribute will allow users to opt specific functions out of
speculative load hardening. This compliments the Clang attribute
named speculative_load_hardening. When this attribute or the attribute
speculative_load_hardening is used in combination with the flags
-mno-speculative-load-hardening or -mspeculative-load-hardening,
the function level attribute will override the default during LLVM IR
generation. For example, in the case, where the flag opposes the
function attribute, the function attribute will take precendence.
The sticky inlining behavior of the speculative_load_hardening attribute
may cause a function with the no_speculative_load_hardening attribute
to be tagged with the speculative_load_hardening tag in
subsequent compiler phases which is desired behavior since the
speculative_load_hardening LLVM attribute is designed to be maximally
conservative.
Details
Diff Detail
- Repository
- rL LLVM
- Build Status
Buildable 25411 Build 25410: arc lint + arc unit
Event Timeline
clang/include/clang/Basic/AttrDocs.td | ||
---|---|---|
3667 | Hi Zola, I'm afraid I don't have time for an in-depth look in the next few days, Thanks, Kristof |
clang/include/clang/Basic/AttrDocs.td | ||
---|---|---|
3663 | Maybe say "is not needed for the function body" to make it more clear? | |
3667 | FWIW, I had suggested this design because I think it is simple, and addresses the only use case I have in mind. That use case is essentially to remove SLH from functions that are performance sensitive and manually mitigated (if at risk at all). The intent is that people can use this based on profiling to recover some performance hit from SLH where it is safe to do so. As a consequence, the goal was not what you describe. My thought process goes like this:
Now, we have to deal with what happens if the inliner tries to inline a function marked with an explicit attribute into code marked with this no_speculative_load_hardening attribute. I don't expect this to ever realistically occur, but we need to handle it if it does. Sadly, we can't just produce an error -- the backend may be the only thing that sees this. We could add an LLVM attribute as you suggest and block inlining in this case, but that seems very intrusive and is only designed to handle an edge case that we don't anticipate happens much if ever in practice. The final option which seems the most reasonable is what this implements: the positive attribute always wins. IMO, this is a reasonable handling of the edge case, and keeps things as simple as possible. |
clang/include/clang/Basic/AttrDocs.td | ||
---|---|---|
3640 | Add backticks around the command line flag. Bonus points if you can link it to the command line documentation for that command. | |
3663 | Also, use RST formatting for NOT rather than capitalizing it. (Either surround it like *this* for italics or like this for bold.) | |
3667 | I think the current design is the right approach, but I'd appreciate seeing a code example in the documentation that more clearly shows the expectation. Something like: __attribute__((speculative_load_hardening)) int foo(int i) { return i; } __attribute__((no_speculative_load_hardening)) int bar(int i) { // Note: bar() may still have speculative load hardening if foo() is // inlined into bar(). Mark foo() with __attribute__((noinline)) to // avoid this situation. return foo(i); } Feel free to devise a less terrible example, too. ;-) One question I have is: do we think it's worth diagnosing this slightly different case, given that the user has signaled explicitly that they want foo() inlined, but the call is has speculative load hardening disabled: __attribute__((speculative_load_hardening)) inline int foo(int i) { return i; } __attribute__((no_speculative_load_hardening)) int bar(int i) { return foo(i); // Warning? } |
clang/include/clang/Basic/AttrDocs.td | ||
---|---|---|
3667 | Thanks for explaining the detailed rationale. Referring to: "Now, we have to deal with what happens if the inliner tries to inline a function marked with an explicit attribute into code marked with this no_speculative_load_hardening attribute." , which is the case of "SLH-function ---inline into---> noSLH-function". Overall, I agree that it doesn't seem worthwhile to introduce the extra complexity needed for that remaining "SLH-function ---inline into----> noSLH-function" theoretical edge case. |
Fix indentation of code block in attribute documentation for no speculative load hardening
clang/include/clang/Basic/AttrDocs.td | ||
---|---|---|
3677 | I think this is meant to be .. code-block:: c ? | |
clang/test/SemaCXX/attr-no-speculative-load-hardening.cpp | ||
6 | Should this situation be diagnosed? __attribute__((no_speculative_load_hardening)) void func(); __attribute__((speculative_load_hardening)) void func(); Which attribute should "win"? If you want to diagnose, you can use checkAttrMutualExclusion() to handle this in SemaDeclAttr.cpp. |
clang/include/clang/Basic/AttrDocs.td | ||
---|---|---|
3667 | @aaron.ballman I will try to implement a warning like you proposed for that particular case. Still looking into it., but wanted to ack. | |
clang/test/SemaCXX/attr-speculative-load-hardening.cpp | ||
19 ↗ | (On Diff #178526) |
@aaron.ballman |
clang/test/SemaCXX/attr-speculative-load-hardening.cpp | ||
---|---|---|
19 ↗ | (On Diff #178526) | After looking more closely at this, I think I only need to write a function for this particular attribute to be merged differently than the default way which will cause the diagnostic if necessary in this case which is a smaller change than I thought, so I'll add the change to this patch. |
Give an error when a function was both attributes specified at different places in the code.
LGTM aside from a minor formatting question.
clang/lib/Sema/SemaDeclAttr.cpp | ||
---|---|---|
4095–4096 | Is this the formatting you get from clang-format? |
clang/lib/Sema/SemaDecl.cpp | ||
---|---|---|
13079 ↗ | (On Diff #180575) | @aaron.ballman Could you take a look at this clang diagnostic I added for the case you suggested in another comment? |
clang/include/clang/Sema/Sema.h | ||
---|---|---|
1718–1719 ↗ | (On Diff #180575) | You should give these parameters identifiers to help people reading the source code understand what should be passed for each argument. |
clang/lib/Sema/SemaDecl.cpp | ||
13080–13081 ↗ | (On Diff #180575) | The identifiers don't match the usual naming conventions (here and elsewhere). |
13082 ↗ | (On Diff #180575) | I would turn this into an early return rather than indent the entire function body one level for it. |
13087–13089 ↗ | (On Diff #180575) | llvm::copy(Node->children(), std::back_inserter(Nodes)); |
13090 ↗ | (On Diff #180575) | Should be const auto * (same below) rather than inferring the const qualification. Moreover, this seems incorrect -- you don't mean any declref to an attributed function, do you? I would have imagined that only call expressions matter. e.g., this should not diagnose: [[clang::speculative_load_hardening]] inline int bar(int x) { return x; } [[clang::no_speculative_load_hardening]] int foo() { if (bar) { // No reason to warn about speculative load hardening because there's no call. } return sizeof(bar(0)); // Unevaluated expression probably doesn't need a warning either. } I'm a bit concerned about the performance cost of touching every child AST node in the function body. I wonder if it would be possible to check CallExprs as they're handled instead? I'm not certain if there's a way to get from the Expr to the context in which the expression appears so that we can see if the expression is within a function with speculative load hardening disabled or not, so this may be a dead-end idea. If it turns out that handling it at the CallExpr site isn't feasible, then I think we should probably revert this diagnostic and go with the previous version of the patch. We can take another swing at a diagnostic if we find that people actually hit this corner case often enough to warrant the extra expense of the check (or we can solve it another way, through clang-tidy or the static analyzer). WDYT? |
13096 ↗ | (On Diff #180575) | You should pass in ND and Callee directly rather than calling getName() on them; the diagnostic engine will handle properly quoting the name that's output as well. |
13129 ↗ | (On Diff #180575) | Why are you qualifying the call? |
13294 ↗ | (On Diff #180575) | Spurious change? |
clang/lib/Sema/SemaDecl.cpp | ||
---|---|---|
13090 ↗ | (On Diff #180575) | I looked into handling this at the CallExpr site and it looks like the context needed for this diagnostic is available. I'll update the patch when I get that version working. That suggestion makes a lot of sense to me. As for checking every DeclRefExpr, that was due to my misunderstanding of the AST. Given what you said, I think what I really want are the DeclRefExpr that are children of CallExpr. That'll be reflected in the updated patch. I'll keep all these style comments in mind when making the update too. |
13294 ↗ | (On Diff #180575) | Yes, I'll revert that. |
I'm going to revert the diagnostic changes for now and commit this patch. I tried a few ways to implement this without success so far. I have one additional way I want to try based on some advice on the cfe-dev list, but I'll submit it in another patch once I get some time to try and implement it.
That sounds reasonable to me -- I didn't intend for it to hold your patch up; it can always be done separately.
Add backticks around the command line flag. Bonus points if you can link it to the command line documentation for that command.