Diagnosing Incompatible Attributes Between a Calling Function and the Callee Function

Hi everyone,

I’m working on adding function level Clang attributes for the Speculative Load Hardening (SLH) feature, so devs who know what they are doing can enable or disable SLH function by function. There are two attributes ‘no_speculative_load_hardening’ and ‘speculative_load_hardening.’

As a part of this, I want to diagnose a special case where a function marked

‘no_speculative_load_hardening’ will still have ‘speculative_load_hardening’ enabled.

Whenever a function marked ‘speculative_load_hardening’ is inlined into another function, then that function that it was inlined into will have SLH enabled no matter what. [If you want more info on the rationale for this, check out the patch comments here: https://reviews.llvm.org/D54909?id=175599#inline-487979]

I want to diagnose cases similar to this example:

attribute((speculative_load_hardening)) inline int foo(int i) {

return i;

}

attribute((no_speculative_load_hardening)) int bar(int i) {

return foo(i); // Warning

}

I’ve thought about three different ways to implement this.

  1. Add the diagnosis to Sema::ActOnFinishFunctionBody in SemaDecl.cpp. The caller function declaration and the caller body is available. For functions marked with SLH, I can walk the function body nodes and see whether any function calls have the incompatible attributes of SLH + inline.

Downside: This is an expensive way to diagnose this edge case. It doesn’t seem worth the expense, so after discussing this with Aaron Ballman, I’ll leave out the diagnosis if this is the only way to do it.

  1. Add the diagnosis to CheckFunctionCall or checkCall in SemaChecking.cpp

Why doesn’t this work?

The context about the caller of the callee isn’t available. I need to know whether the caller has the ‘no_speculative_load_hardening’ attribute.

  1. Add the diagnosis to Sema::ActOnCallExpr in SemaExpr.cpp

Would this work?

I’m not sure. Maybe this is a place where it would work since I have the Scope which contains info about the caller and the FunctionDecl of the callee which is all the info I need about the callee (hasAttr && isInlineSpecified). The part I don’t know how to do is distinguishing the calls I want to check from other calls that pass through that function (I assume constructors? Maybe other things?).

Questions

  • How do I distinguish between FunctionDecls that are direct function calls like I was to diagnose vs ones that are calls in other contexts? I think if I could make this distinction, then ActOnCallExpr would be a workable place to diagnose this. Since I don’t have a good sense of what other calls are available, I’m having a hard time characterizing the distinguishing features of the calls I want to diagnose. (I’m relatively unknowledgeable about compilers and programming languages.)

  • If I only check direct function calls (which are already singled out in ActOnCallExpr, would that be an appropriate subset of calls for me to try to diagnose? How do I know whether or not that’s the right subset?

  • If there’s some documentation or book chapter I could read to get a better understanding of how to figure this out, please feel free to recommend that.

  • Am I missing a way to get the caller function when given a function call as in CheckFunctionCall?

  • Are there any recommendations about where else might be an appropriate place to diagnose this issue? Pointers about what to check into would be appreciated. SemaChecking.cpp seems like the right file, but I didn’t see any other likely candidates from my inspection of the functions defined there.

Zola Bridges

Hi everyone,

*

I'm working on adding function level Clang attributes for the Speculative Load Hardening (SLH) feature, so devs who know what they are doing can enable or disable SLH function by function. There are two attributes 'no_speculative_load_hardening' and 'speculative_load_hardening.'

*

As a part of this, I want to diagnose a special case where a function marked

'no_speculative_load_hardening' will still have 'speculative_load_hardening' enabled.

*

Whenever a function marked 'speculative_load_hardening' is inlined into another function, then that function that it was inlined into will have SLH enabled no matter what. [If you want more info on the rationale for this, check out the patch comments here: https://reviews.llvm.org/D54909?id=175599#inline-487979]

I want to diagnose cases similar to this example:

*

__attribute__((speculative_load_hardening)) inline int foo(int i) {

The "inline" keyword here is basically irrelevant; we aren't required to inline functions marked inline, and we can inline functions which are not marked "inline". (The "inline" keyword has other effects related to linkage, but they don't have any effect on the arguments here.)

In general, it's impossible to catch all cases of a no_speculative_load_hardening calling a speculative_load_hardening function in the frontend: a no_speculative_load_hardening might call a speculative_load_hardening indirectly, or the callee might be in a different translation unit. (The optimizer may or may not discover the call later, depending on the exact construct in question and the optimization flags.)

Given that, I'm not sure your proposed diagnostic makes sense; even if you could diagnose constructs like your example, you'd be missing all the important cases.

Also, if the general rule is "the optimizer may choose to perform SLH on a function even if it's marked no_speculative_load_hardening", you should probably avoid specifically mentioning inlining in the documentation, since it's misleading. (Other optimizations might be affected by this rule, like function merging or outlining.)

*

2. Add the diagnosis to CheckFunctionCall or checkCall in SemaChecking.cpp

*

Why doesn't this work?

The context about the caller of the callee isn't available. I need to know whether the caller has the 'no_speculative_load_hardening' attribute.

The context should be available; you can call Sema::getEnclosingFunction(), or something like that.

-Eli

Thanks for the info! For now, I’ve committed the patch without the diagnostic and will come back to it when I get some time. I’ll think about whether this diagnostic is better left out. I’m not sure if I’ll decide to not include it at all since you, as you mentioned, it would miss a lot of major cases.