Skip to content

[clang] Avoid evaluating the BitWidth expression over and over again #66203

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

Closed
wants to merge 1 commit into from

Conversation

tbaederr
Copy link
Contributor

It's usually a ConstantExpr with a saved constant integer anyway, so we can just return that.

It's usually a ConstantExpr with a saved constant integer anyway, so we
can just return that.
@tbaederr tbaederr requested a review from a team as a code owner September 13, 2023 12:21
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Sep 13, 2023
@llvmbot
Copy link
Member

llvmbot commented Sep 13, 2023

@llvm/pr-subscribers-clang

Changes It's usually a ConstantExpr with a saved constant integer anyway, so we can just return that. -- Full diff: https://github.com//pull/66203.diff

1 Files Affected:

  • (modified) clang/lib/AST/Decl.cpp (+8)
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 6109829cf20a678..0e964b7f1372227 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -4471,6 +4471,14 @@ void FieldDecl::setLazyInClassInitializer(LazyDeclStmtPtr NewInit) {
 
 unsigned FieldDecl::getBitWidthValue(const ASTContext &Ctx) const {
   assert(isBitField() && "not a bitfield");
+
+  // Try to avoid evaluating the expression in the overwhelmingly
+  // common case that we have a ConstantExpr.
+  if (const auto *CE = dyn_cast<ConstantExpr>(getBitWidth())) {
+    assert(CE->hasAPValueResult());
+    return CE->getResultAsAPSInt().getZExtValue();
+  }
+
   return getBitWidth()->EvaluateKnownConstInt(Ctx).getZExtValue();
 }
 

@cor3ntin
Copy link
Contributor

Hum... should we put that in EvaluateKnownConstInt directly? Bitfields are not the only use case

@tbaederr
Copy link
Contributor Author

Hum... should we put that in EvaluateKnownConstInt directly? Bitfields are not the only use case

See https://reviews.llvm.org/D155548 - I can't really support any of this with performance numbers (from my testing, I can't even support that we short-circuit IntegerLiterals...), but this just seems like the right thing to do to me.

Copy link
Contributor

@cor3ntin cor3ntin left a comment

Choose a reason for hiding this comment

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

I knew there was a deja-vu here :)

I'd rather we do the general solution then. Maybe we should pursue the discussion on phab. I can't imagine the general solution is going to ever be noticeably worse.

I have the possibility to push a branch here http://llvm-compile-time-tracker.com/
@AaronBallman

@tbaederr
Copy link
Contributor Author

@cor3ntin Looking at this again... while I'd also like to see the general solution, I'd probably apply this anyway since it makes it clearer that this function can actually be fast. Its name is terrible for what it does. It sounds like it's a simply getter but in reality it might be pretty costly to call. This patch would mitigate this somewhat and make the performance expectations clearer when reading the code of this function.

@tbaederr
Copy link
Contributor Author

Ping

@tbaederr
Copy link
Contributor Author

Ping

@tbaederr
Copy link
Contributor Author

tbaederr commented Oct 4, 2023

Ping

cor3ntin added a commit to cor3ntin/llvm-project that referenced this pull request Oct 5, 2023
The bounds of a c++ array is a _constant-expression_.
And in C++ it is also a constant expression.

But we also support VLAs, ie arrays with non-constant bounds.

We need to take care to handle the case of a consteval function
(which are specified to be only immediately called in
non-constant contexts) that appear in arrays bounds.

This introduces `Sema::isAlwayConstantEvaluatedContext`,
and a flag in ExpressionEvaluationContextRecord, such that
immediate functions in array bounds are always immediately invoked.

Sema had both `isConstantEvaluatedContext` and
`isConstantEvaluated`, so I took the opportunity to cleanup that.

The change in `TimeProfilerTest.cpp` is an unfortunate
manifestation of the problem that llvm#66203 seeks to address.

Fixes llvm#65520
cor3ntin added a commit that referenced this pull request Oct 5, 2023
The bounds of a c++ array is a _constant-expression_. And in C++ it is
also a constant expression.

But we also support VLAs, ie arrays with non-constant bounds.

We need to take care to handle the case of a consteval function (which
are specified to be only immediately called in non-constant contexts)
that appear in arrays bounds.

This introduces `Sema::isAlwayConstantEvaluatedContext`, and a flag in
ExpressionEvaluationContextRecord, such that immediate functions in
array bounds are always immediately invoked.

Sema had both `isConstantEvaluatedContext` and
`isConstantEvaluated`, so I took the opportunity to cleanup that.

The change in `TimeProfilerTest.cpp` is an unfortunate manifestation of
the problem that #66203 seeks to address.

Fixes #65520
@AaronBallman
Copy link
Collaborator

@cor3ntin Looking at this again... while I'd also like to see the general solution, I'd probably apply this anyway since it makes it clearer that this function can actually be fast. Its name is terrible for what it does. It sounds like it's a simply getter but in reality it might be pretty costly to call. This patch would mitigate this somewhat and make the performance expectations clearer when reading the code of this function.

Once we have the general solution, this code would be redundant, wouldn't it? (I also would prefer the more general solution if reasonable -- playing whack-a-mole on constant evaluation is going to make for maintenance problems and this is an issue that plagues other parts of the compiler as well.)

@tbaederr
Copy link
Contributor Author

tbaederr commented Oct 6, 2023

Well, this is not going to make a noticeable difference in runtime. https://reviews.llvm.org/D155548 didn't land because there are no measurements to make where this makes a measurable difference.

As for my earlier comment, it would also make sense to rename that function to computeBitWidth() or just cache the computed value (we compute it when parsing anyway to diagnose 0 size, etc. right?).

@AaronBallman
Copy link
Collaborator

Well, this is not going to make a noticeable difference in runtime. https://reviews.llvm.org/D155548 didn't land because there are no measurements to make where this makes a measurable difference.

Those changes didn't land because no measurements were attempted. Putting up a branch at https://llvm-compile-time-tracker.com/ would help get those measurements to at least start to see if there's benefit or harm from the changes.

As for my earlier comment, it would also make sense to rename that function to computeBitWidth() or just cache the computed value (we compute it when parsing anyway to diagnose 0 size, etc. right?).

Caching the computed value would make sense, but that's sort of the goal of D155548, right? That's a generalized caching mechanism that should mean when we compute it to diagnose 0 size, we never need to re-compute it again.

@tbaederr
Copy link
Contributor Author

tbaederr commented Oct 6, 2023

Well, this is not going to make a noticeable difference in runtime. https://reviews.llvm.org/D155548 didn't land because there are no measurements to make where this makes a measurable difference.

Those changes didn't land because no measurements were attempted. Putting up a branch at https://llvm-compile-time-tracker.com/ would help get those measurements to at least start to see if there's benefit or harm from the changes.

I did measure it, but only by myself, I can ask Nikita do use the compile-time tracker as well.

As for my earlier comment, it would also make sense to rename that function to computeBitWidth() or just cache the computed value (we compute it when parsing anyway to diagnose 0 size, etc. right?).

Caching the computed value would make sense, but that's sort of the goal of D155548, right? That's a generalized caching mechanism that should mean when we compute it to diagnose 0 size, we never need to re-compute it again.

Well yes, we would never compute it again, but we still go through quite a few function calls to figure out that we've already computed it before. For a function called getBitWidthValue() I basically assume it has exactly one statement: return BitWidthValue.

I thought the bitwidth expression will always be a ConstantExpr, unless it's value-dependent (in which case it will be a ConstantExpr when we instantiate it, right?), but I might be wrong.

@AaronBallman
Copy link
Collaborator

Well, this is not going to make a noticeable difference in runtime. https://reviews.llvm.org/D155548 didn't land because there are no measurements to make where this makes a measurable difference.

Those changes didn't land because no measurements were attempted. Putting up a branch at https://llvm-compile-time-tracker.com/ would help get those measurements to at least start to see if there's benefit or harm from the changes.

I did measure it, but only by myself, I can ask Nikita do use the compile-time tracker as well.

Ah, I didn't realize you had measured on your own. But yeah, having some concrete numbers to play with would help.

As for my earlier comment, it would also make sense to rename that function to computeBitWidth() or just cache the computed value (we compute it when parsing anyway to diagnose 0 size, etc. right?).

Caching the computed value would make sense, but that's sort of the goal of D155548, right? That's a generalized caching mechanism that should mean when we compute it to diagnose 0 size, we never need to re-compute it again.

Well yes, we would never compute it again, but we still go through quite a few function calls to figure out that we've already computed it before. For a function called getBitWidthValue() I basically assume it has exactly one statement: return BitWidthValue.

I'm hopeful the optimizer ends up inlining at least some of those calls, but otherwise, yeah you're right.

I thought the bitwidth expression will always be a ConstantExpr, unless it's value-dependent (in which case it will be a ConstantExpr when we instantiate it, right?), but I might be wrong.

Per spec, it needs to be a constant expression. In terms of our implementation, I would also assume it would either be ConstantExpr or IntegerLiteral (which, strangely is an Expr and not a ConstantExpr)

@cor3ntin
Copy link
Contributor

cor3ntin commented Oct 6, 2023

Ah, I didn't realize you had measured on your own. But yeah, having some concrete numbers to play with would help.

I'd like to see rough numbers too but I'm really hoping we can progress https://reviews.llvm.org/D155548.
I think it's the worse case it's harmless, at best it could have a pretty big positive impact.

@tbaederr
Copy link
Contributor Author

tbaederr commented Oct 6, 2023

@tbaederr
Copy link
Contributor Author

tbaederr commented Oct 6, 2023

^ Removing integer and boolean expressions seems to have some negative impact though.

@tbaederr
Copy link
Contributor Author

I've merged https://reviews.llvm.org/D155548, so closing this.

@tbaederr tbaederr closed this Oct 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants