Skip to content

Conversation

@HankChang736
Copy link
Owner

  • 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>>>
Copy link

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?

Copy link
Owner Author

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.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Suggested change
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"
Copy link

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.

Copy link
Owner Author

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 ?

Copy link

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) {
Copy link
Owner Author

Choose a reason for hiding this comment

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

Suggested change
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) {
Copy link
Owner Author

Choose a reason for hiding this comment

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

Suggested change
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) {
Copy link
Owner Author

Choose a reason for hiding this comment

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

Suggested change
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)
Copy link
Owner Author

Choose a reason for hiding this comment

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

Suggested change
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>.
@HankChang736
Copy link
Owner Author

@topperc
I just update the return type in getTgtMemIntrinsic() with dropping std::optional, now it use nullptr in PtrVal to check empty MemIntrinsicInfo and use empty() to check empty SmallVector.

The commit that include this update is f2abced960e7be54d8eb3aa6557dbc472c4f40f2

@@ -0,0 +1,55 @@
//===--------- Definition of the InterestingMemoryOperand class -*- C++
Copy link

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>>
Copy link

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?

Copy link
Owner Author

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:

  1. The target didn’t implement getTgtMemIntrinsic.

  2. The target did implement it and there are truly no SmallVector<InterestingMemoryOperand, 1>.

Copy link

@topperc topperc Aug 26, 2025

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?

Copy link
Owner Author

@HankChang736 HankChang736 Aug 27, 2025

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)

Copy link

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?

Copy link
Owner Author

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) {
Copy link

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) {
Copy link

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.

Copy link
Owner Author

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.
@HankChang736
Copy link
Owner Author

@topperc
I make SmallVector<InterestingMemoryOperand, 1> a member of MemIntrinsic Info, and fix the passes that use getTgtMemIntrinsic in commit e89d6ba.


bool GCNTTIImpl::getTgtMemIntrinsic(IntrinsicInst *Inst,
MemIntrinsicInfo &Info) const {
MemIntrinsicInfo &Info) const {
Copy link

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;
Copy link

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;

Copy link

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)) {
Copy link

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 =
Copy link

Choose a reason for hiding this comment

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

revert this formatting change

@HankChang736
Copy link
Owner Author

HankChang736 commented Sep 10, 2025

@topperc
The format is changed in commit 9f26e22, 665ebae and 766d8c5.

Copy link

@topperc topperc left a comment

Choose a reason for hiding this comment

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

LGTM

@HankChang736
Copy link
Owner Author

I just created a pr in llvm upstream.

llvm#157863

HankChang736 pushed a commit that referenced this pull request Sep 19, 2025
…), 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]>
HankChang736 pushed a commit that referenced this pull request Sep 19, 2025
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).
HankChang736 pushed a commit that referenced this pull request Sep 23, 2025
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".
HankChang736 pushed a commit that referenced this pull request Oct 7, 2025
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
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants