-
Notifications
You must be signed in to change notification settings - Fork 0
Change getTgtMemIntrinsic interface with returning a pair #1
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
Change getTgtMemIntrinsic interface with returning a pair #1
Conversation
HankChang736
commented
Aug 18, 2025
- This patch change the interface of getTgtMemIntrinsic from return "bool" into return "std::pair<std::optional,std::optional<SmallVectorImpl>>" to allow Asan or EarlyCSE to choose the data structure they need with the same TTI hook.
* This patch change the interface of getTgtMemIntrinsic from return "bool" into return "std::pair<std::optional<MemIntrinsicInfo>,std::optional<SmallVectorImpl<InterestingMemoryOperand>>>" to allow Asan or EarlyCSE to choose the data structure they need with the same TTI hook.
| MemIntrinsicInfo &Info) const { | ||
| return false; | ||
| virtual std::pair<std::optional<MemIntrinsicInfo>, | ||
| std::optional<SmallVector<InterestingMemoryOperand, 1>>> |
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 need an optional around the SmallVector? Can we use an empty vector to represent no interesting operand?
Looks like EarlyCSE will ignore MemIntrinsicInfo with a null PtrVal. Can we use that instead of an optional?
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 use an empty vector to represent no interesting operand is a great idea, in this way we don't need to use optional for SmallVector.
Use a null PtrVal to check MemIntrinsicInfo might be fine, I just checked the target that uses getTgtMemIntrinsic and find that all of them fill PtrVal and EarlyCSE/LoopStrengthReduce also ignore PtrVal with nullptr.
I will try to modify it with dropping optional.
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.
| std::optional<SmallVector<InterestingMemoryOperand, 1>>> | |
| std::pair<MemIntrinsicInfo, | |
| SmallVector<InterestingMemoryOperand, 1>> |
The interface after dropping std::optional will look like this.
| #include "llvm/Support/BranchProbability.h" | ||
| #include "llvm/Support/Compiler.h" | ||
| #include "llvm/Support/InstructionCost.h" | ||
| #include "llvm/Transforms/Instrumentation/AddressSanitizerCommon.h" |
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 don't think Analysis should include a header from Transforms. The Transforms library has a dependency on the Analysis library.
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.
Oh I see. The reason I include this header from Transforms is to use InterestingMemoryOperand, maybe move InterestingMemoryOperand from Transforms header to Analysis/InterestingMemoryOperand.h and include it in Transforms/Instrumentation/AddressSanitizerCommon.h ?
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.
That sounds good to me.
| std::pair<std::optional<MemIntrinsicInfo>, | ||
| std::optional<SmallVector<InterestingMemoryOperand, 1>>> | ||
| MemInfo = TTI.getTgtMemIntrinsic(II); | ||
| if (MemInfo.first != std::nullopt) { |
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 (MemInfo.first != std::nullopt) { | |
| if (MemInfo.first.PtrVal) { |
Check PtrVal is null or not to see whether the target fill MemIntrinsicInfo.
| std::pair<std::optional<MemIntrinsicInfo>, | ||
| std::optional<SmallVector<InterestingMemoryOperand, 1>>> | ||
| MemInfo = TTI.getTgtMemIntrinsic(II); | ||
| if (MemInfo.first != std::nullopt) { |
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 (MemInfo.first != std::nullopt) { | |
| if (MemInfo.first.PtrVal) { |
Same here
| std::pair<std::optional<MemIntrinsicInfo>, | ||
| std::optional<SmallVector<InterestingMemoryOperand, 1>>> | ||
| MemInfo = TTI.getTgtMemIntrinsic(II); | ||
| if (MemInfo.first != std::nullopt && MemInfo.first->PtrVal) { |
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 (MemInfo.first != std::nullopt && MemInfo.first->PtrVal) { | |
| if (MemInfo.first.PtrVal) { |
Check PtrVal here too.
| std::optional<SmallVector<InterestingMemoryOperand, 1>>> | ||
| MemInfo; | ||
| MemInfo = TTI->getTgtMemIntrinsic(II); | ||
| if (MemInfo.second != std::nullopt) |
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 (MemInfo.second != std::nullopt) | |
| if (!MemInfo.second.empty()) |
Check the SmallVector is empty or not.
This commit drops std::optional in getTgtMemIntrinsic return type, use nullptr in PtrVal to identify empty MemIntrinsicInfo and empty() to identify empty SmallVector<InterestingMemoryOperand, 1>.
|
@topperc The commit that include this update is f2abced960e7be54d8eb3aa6557dbc472c4f40f2 |
| @@ -0,0 +1,55 @@ | |||
| //===--------- Definition of the InterestingMemoryOperand class -*- 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.
Don't wrap this line
| /// if false is returned. | ||
| LLVM_ABI bool getTgtMemIntrinsic(IntrinsicInst *Inst, | ||
| MemIntrinsicInfo &Info) const; | ||
| LLVM_ABI std::pair<MemIntrinsicInfo, SmallVector<InterestingMemoryOperand, 1>> |
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.
Could we make InterestingMemoryOperand a member of MemIntrinsicInfo and keep the original signature?
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’m worried the original signature by returning bool might be ambiguous for targets that never produce SmallVector<InterestingMemoryOperand, 1>.
With SmallVector<InterestingMemoryOperand, 1> embedded in MemIntrinsicInfo, an empty vector can mean either:
-
The target didn’t implement getTgtMemIntrinsic.
-
The target did implement it and there are truly no
SmallVector<InterestingMemoryOperand, 1>.
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.
Aren't those both possibilities if you return a SmallVector as part of a pair? Is the difference between those two cases meaningful for the current use case? Should we go back to the std::optional?
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.
Yes, it is.
But returning a pair essentially makes the Transform passes that use getTgtMemIntrinsic directly check whether the returned SmallVector is empty or not with an if condition. On the other hand, embedding SmallVector inside MemIntrinsicInfo while keeping the original signature bool getTgtMemIntrinsic(IntrinsicInst *Inst, MemIntrinsicInfo &Info) introduces another subtlety: since returning true only guarantees that MemIntrinsicInfo is filled, but some targets like AArch64, PowerPC and AMDGPU don't fill SmallVector.
This means that passes like AddressSanitizer, which only rely on SmallVector, could end up accessing it even when it is empty, leading to ambiguity or potential misuse. As a result, we would still need to double-check whether the SmallVector is empty to ensure the target actually provided the corresponding data.
I think making InterestingMemoryOperand a member of MemIntrinsicInfo is also an approach. Since it keeps the original implementation, but it requires a double-check like this in AddressSanitizer pass :
if (TTI.getTgtMemIntrinsic(Inst, Info) && !Info.Interesting.empty())Also it needs to check the PtrVal in EarlyCSE to ensure whether the target really fills the information for it since those targets that support getTgtMemIntrinsic all fills PtrVal.
if (TTI.getTgtMemIntrinsic(Inst, Info) && Info.PtrVal)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.
Doesn't EarlyCSE check ParseMemoryInst::isValid() to check if the PtrVal is null before doing anything with it.
This means that passes like AddressSanitizer, which only rely on SmallVector, could end up accessing it even when it is empty, leading to ambiguity or potential misuse. As a result, we would still need to double-check whether the SmallVector is empty to ensure the target actually provided the corresponding data.
Doesn't AddressSanitizer need to check the vector is non-empty before using it regardless of where it is stored?
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, I see — that makes sense now.
For EarlyCSE, ParseMemoryInst::isValid() already ensures that PtrVal is non-null before using it, so we don’t actually need an extra check there.
And for AddressSanitizer, it explicitly verifies that the vector is non-empty before using it. So it looks like we can indeed make InterestingMemoryOperand a member of MemIntrinsicInfo safely.
Thanks for pointing this out — it clears up my earlier concern.
| bool AddressSanitizer::instrumentFunction(Function &F, | ||
| const TargetLibraryInfo *TLI) { | ||
| const TargetLibraryInfo *TLI, | ||
| const TargetTransformInfo *TTI) { |
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.
weird formatting
| if (IntrInfo.PtrVal == OperandVal) | ||
| std::pair<MemIntrinsicInfo, SmallVector<InterestingMemoryOperand, 1>> | ||
| MemInfo = TTI.getTgtMemIntrinsic(II); | ||
| if (MemInfo.first.PtrVal != nullptr) { |
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.
Is OperandVal always non-null? If so, it could never match a null MemInfo.first.PtrVal.
If we do this check, then use && to join the two ifs instead of nesting 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.
I'm not sure if OperandVal is always non-null here. Maybe we keep this check since we make SmallVector a member of MemIntrinsicInfo and keep the original signatures.
…nsicInfo This commit make SmallVector<InterestingMemoryOperand> a member of MemIntrinsicInfo so that we can keep the original interface of getTgtMemIntrinsic.
|
|
||
| bool GCNTTIImpl::getTgtMemIntrinsic(IntrinsicInst *Inst, | ||
| MemIntrinsicInfo &Info) const { | ||
| MemIntrinsicInfo &Info) const { |
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.
Please revert these formatting changes
| break; | ||
| } | ||
|
|
||
| return false; |
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.
Please revert this change
| bool canSaveCmp(Loop *L, BranchInst **BI, ScalarEvolution *SE, LoopInfo *LI, | ||
| DominatorTree *DT, AssumptionCache *AC, | ||
| TargetLibraryInfo *LibInfo) const override; | ||
|
|
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.
revert this change
| if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(Inst)) { | ||
| IntrID = II->getIntrinsicID(); | ||
| if (TTI.getTgtMemIntrinsic(II, Info)) | ||
| if (TTI.getTgtMemIntrinsic(II, Info)) { |
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.
Drop curly braces
| if (TTI.getTgtMemIntrinsic(II, IntrInfo) && IntrInfo.PtrVal) { | ||
| AccessTy.AddrSpace | ||
| = IntrInfo.PtrVal->getType()->getPointerAddressSpace(); | ||
| AccessTy.AddrSpace = |
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.
revert this formatting change
topperc
left a comment
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.
LGTM
|
I just created a pr in llvm upstream. |
…), C)) (llvm#155141) Hi, I compared the following LLVM IR with GCC and Clang, and there is a small difference between the two. The LLVM IR is: ``` define i64 @test_smin_neg_one(i64 %a) { %1 = tail call i64 @llvm.smin.i64(i64 %a, i64 -1) %retval.0 = xor i64 %1, -1 ret i64 %retval.0 } ``` GCC generates: ``` cmp x0, 0 csinv x0, xzr, x0, ge ret ``` Clang generates: ``` cmn x0, #1 csinv x8, x0, xzr, lt mvn x0, x8 ret ``` Clang keeps flipping x0 through x8 unnecessarily. So I added the following folds to DAGCombiner: fold (xor (smax(x, C), C)) -> select (x > C), xor(x, C), 0 fold (xor (smin(x, C), C)) -> select (x < C), xor(x, C), 0 alive2: https://alive2.llvm.org/ce/z/gffoir --------- Co-authored-by: Yui5427 <[email protected]> Co-authored-by: Matt Arsenault <[email protected]> Co-authored-by: Simon Pilgrim <[email protected]>
llvm#158769) …52471)" This reverts commit e4eccd6. This was causing ASan failures in some situations involving unordered multimap containers. Details and a reproducer were posted on the original PR (llvm#152471).
A few improvements to logging when lldb-dap is started in **Server Mode** AND when the **`lldb-dap.logFolder`** setting is used (not `lldb-dap.log-path`). ### Improvement #1 **Avoid the prompt of restarting the server when starting each debug session.** That prompt is caused by the combination of the following facts: 1. The log filename changes every time a new debug session is starting (see [here](https://github.com/llvm/llvm-project/blob/9d6062c490548a5e6fea103e010ab3c9bc73a86d/lldb/tools/lldb-dap/src-ts/logging.ts#L47)) 2. The log filename is passed to the server via an environment variable called "LLDBDAP_LOG" (see [here](https://github.com/llvm/llvm-project/blob/9d6062c490548a5e6fea103e010ab3c9bc73a86d/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts#L263-L269)) 3. All environment variables are put into the "spawn info" variable (see [here](https://github.com/llvm/llvm-project/blob/9d6062c490548a5e6fea103e010ab3c9bc73a86d/lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts#L170-L172)). 4. The old and new "spawn info" are compared to decide if a prompt should show (see [here](https://github.com/llvm/llvm-project/blob/9d6062c490548a5e6fea103e010ab3c9bc73a86d/lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts#L107-L110)). The fix is to remove the "LLDBDAP_LOG" from the "spawn info" variable, so that the same server can be reused if the log path is the only thing that has changed. ### Improvement llvm#2 **Avoid log file conflict when multiple users share a machine and start server in the same second.** The problem: If two users start lldb-dap server in the same second, they will share the same log path. The first user will create the log file. The second user will find that they cannot access the same file, so their server will fail to start. The fix is to add a part of the VS Code session ID to the log filename. ### Improvement llvm#3 **Avoid restarting the server when the order of environment variables changed.** This is done by sorting the environment variables before putting them into the "spawn info".
Specifically, `X & M ?= C --> (C << clz(M)) ?= (X << clz(M))` where M is a non-empty sequence of ones starting at the least significant bit with the remainder zero and C is a constant subset of M that cannot be materialised into a SUBS (immediate). Proof: https://alive2.llvm.org/ce/z/haqdJ4. This improves the comparison in isinf, for example: ```cpp int isinf(float x) { return __builtin_isinf(x); } ``` Before: ``` isinf: fmov w9, s0 mov w8, #2139095040 and w9, w9, #0x7fffffff cmp w9, w8 cset w0, eq ret ``` After: ``` isinf: fmov w9, s0 mov w8, #-16777216 cmp w8, w9, lsl #1 cset w0, eq ret ```