Skip to content
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

[Lint][AMDGPU] No store to const addrspace #109181

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

jofrn
Copy link
Contributor

@jofrn jofrn commented Sep 18, 2024

Ensure store to const addrspace is not allowed by IR Verifier.

@llvmbot
Copy link
Collaborator

llvmbot commented Sep 18, 2024

@llvm/pr-subscribers-llvm-support
@llvm/pr-subscribers-llvm-analysis
@llvm/pr-subscribers-llvm-ir

@llvm/pr-subscribers-backend-amdgpu

Author: None (jofrn)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/109181.diff

2 Files Affected:

  • (modified) llvm/lib/IR/Verifier.cpp (+17)
  • (added) llvm/test/CodeGen/AMDGPU/const-store.ll (+9)
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 06a67346fbf959..805c9ef7b72729 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -4224,6 +4224,19 @@ void Verifier::visitLoadInst(LoadInst &LI) {
   visitInstruction(LI);
 }
 
+static bool isConstantAddressSpace(unsigned AS) {
+  switch (AS) {
+    using namespace AMDGPUAS;
+    case CONSTANT_ADDRESS: case CONSTANT_ADDRESS_32BIT:
+    case CONSTANT_BUFFER_0: case CONSTANT_BUFFER_1: case CONSTANT_BUFFER_2: case CONSTANT_BUFFER_3:
+    case CONSTANT_BUFFER_4: case CONSTANT_BUFFER_5: case CONSTANT_BUFFER_6: case CONSTANT_BUFFER_7:
+    case CONSTANT_BUFFER_8: case CONSTANT_BUFFER_9: case CONSTANT_BUFFER_10: case CONSTANT_BUFFER_11:
+    case CONSTANT_BUFFER_12: case CONSTANT_BUFFER_13: case CONSTANT_BUFFER_14: case CONSTANT_BUFFER_15:
+    return true;
+    default: return false;
+  }
+}
+
 void Verifier::visitStoreInst(StoreInst &SI) {
   PointerType *PTy = dyn_cast<PointerType>(SI.getOperand(1)->getType());
   Check(PTy, "Store operand must be a pointer.", &SI);
@@ -4246,6 +4259,10 @@ void Verifier::visitStoreInst(StoreInst &SI) {
     Check(SI.getSyncScopeID() == SyncScope::System,
           "Non-atomic store cannot have SynchronizationScope specified", &SI);
   }
+  if (TT.isAMDGPU()) {
+    Check(!isConstantAddressSpace(SI.getPointerAddressSpace()),
+          "Store cannot be to constant addrspace", &SI);
+  }
   visitInstruction(SI);
 }
 
diff --git a/llvm/test/CodeGen/AMDGPU/const-store.ll b/llvm/test/CodeGen/AMDGPU/const-store.ll
new file mode 100644
index 00000000000000..9447ef9db59986
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/const-store.ll
@@ -0,0 +1,9 @@
+; RUN: not llc -mtriple=amdgcn < %s |& FileCheck %s
+
+define amdgpu_kernel void @store_const(ptr addrspace(4) %out, i32 %a, i32 %b) {
+; CHECK: Store cannot be to constant addrspace
+; CHECK-NEXT: store i32 %r, ptr addrspace(4) %out
+  %r = add i32 %a, %b
+  store i32 %r, ptr addrspace(4) %out
+  ret void
+}

@jofrn jofrn requested a review from shiltian September 18, 2024 19:41
Copy link

github-actions bot commented Sep 18, 2024

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff 5f02558d820fdc9aa8ac7d3d887e72526574e1d9 393311ca3330fd9ed7baa598e032fc8ee1ebaa66 --extensions cpp,h -- llvm/include/llvm/Support/AMDGPUAddrSpace.h llvm/lib/Analysis/Lint.cpp
View the diff from clang-format here.
diff --git a/llvm/include/llvm/Support/AMDGPUAddrSpace.h b/llvm/include/llvm/Support/AMDGPUAddrSpace.h
index 6f03c3fca3..a7533b99a8 100644
--- a/llvm/include/llvm/Support/AMDGPUAddrSpace.h
+++ b/llvm/include/llvm/Support/AMDGPUAddrSpace.h
@@ -95,7 +95,7 @@ inline bool isExtendedGlobalAddrSpace(unsigned AS) {
 }
 
 inline bool isConstantAddressSpace(unsigned AS) {
-  switch(AS) {
+  switch (AS) {
     using namespace AMDGPUAS;
   case CONSTANT_ADDRESS:
   case CONSTANT_ADDRESS_32BIT:
diff --git a/llvm/lib/Analysis/Lint.cpp b/llvm/lib/Analysis/Lint.cpp
index 3503b6980c..d76e77cd0e 100644
--- a/llvm/lib/Analysis/Lint.cpp
+++ b/llvm/lib/Analysis/Lint.cpp
@@ -122,6 +122,7 @@ class Lint : public InstVisitor<Lint> {
   Value *findValue(Value *V, bool OffsetOk) const;
   Value *findValueImpl(Value *V, bool OffsetOk,
                        SmallPtrSetImpl<Value *> &Visited) const;
+
 public:
   Module *Mod;
   Triple TT;
@@ -136,9 +137,8 @@ public:
 
   Lint(Module *Mod, const DataLayout *DL, AliasAnalysis *AA,
        AssumptionCache *AC, DominatorTree *DT, TargetLibraryInfo *TLI)
-      : Mod(Mod), TT(Triple::normalize(Mod->getTargetTriple())),
-	DL(DL), AA(AA), AC(AC), DT(DT), TLI(TLI),
-        MessagesStr(Messages) {}
+      : Mod(Mod), TT(Triple::normalize(Mod->getTargetTriple())), DL(DL), AA(AA),
+        AC(AC), DT(DT), TLI(TLI), MessagesStr(Messages) {}
 
   void WriteValues(ArrayRef<const Value *> Vs) {
     for (const Value *V : Vs) {
@@ -404,8 +404,9 @@ void Lint::visitMemoryReference(Instruction &I, const MemoryLocation &Loc,
 
   if (Flags & MemRef::Write) {
     if (TT.isAMDGPU())
-      Check(!AMDGPU::isConstantAddressSpace(UnderlyingObject->getType()->getPointerAddressSpace()),
-        "Undefined behavior: Write to const memory", &I);
+      Check(!AMDGPU::isConstantAddressSpace(
+                UnderlyingObject->getType()->getPointerAddressSpace()),
+            "Undefined behavior: Write to const memory", &I);
 
     if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(UnderlyingObject))
       Check(!GV->isConstant(), "Undefined behavior: Write to read-only memory",

Ensure store to const addrspace is not allowed by IR Verifier.
llvm/test/CodeGen/AMDGPU/const-store.ll Outdated Show resolved Hide resolved
@@ -4224,6 +4224,33 @@ void Verifier::visitLoadInst(LoadInst &LI) {
visitInstruction(LI);
}

static bool isConstantAddressSpace(unsigned AS) {
Copy link
Contributor

Choose a reason for hiding this comment

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

constant address space is not an AMD only thing. I'm thinking probably we can split this function into target dependent (for example, to query whether an AS is a constant AS) and independent part.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What would the independent part contain if we do not have a use case for it yet?

@jayfoad
Copy link
Contributor

jayfoad commented Sep 19, 2024

Was there any discussion about this? I'm not sure if it should be a verifier error or just undefined behaviour at run time. Also there are other instructions that write to memory, like atomicrmw.

Copy link
Contributor

@arsenm arsenm left a comment

Choose a reason for hiding this comment

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

This must be UB, and not a verifier error. For example, we allow constant->flat casts. We do not want to have to consider the address spaces involved when folding something like store (addrspacecast ptr addrspace(4) %val to ptr)

@arsenm
Copy link
Contributor

arsenm commented Sep 20, 2024

Plus long term I don't think we should have a constant address space at all.

@jofrn
Copy link
Contributor Author

jofrn commented Sep 20, 2024

Was there any discussion about this? I'm not sure if it should be a verifier error or just undefined behaviour at run time. Also there are other instructions that write to memory, like atomicrmw.

It can be guarded with a flag perhaps. There was discussion to catch this in some way or another since it leads to more confusing error messages later on. Alternatively, we can have these checks go in a separate pass.

@jofrn
Copy link
Contributor Author

jofrn commented Sep 20, 2024

This must be UB, and not a verifier error. For example, we allow constant->flat casts. We do not want to have to consider the address spaces involved when folding something like store (addrspacecast ptr addrspace(4) %val to ptr)

The addrspace in store (addrspacecast ptr addrspace(4) %val to ptr) won't be affected by getPointerAddressSpace since this function asks about the second operand to the store, store i32 %val, ptr addrspace(4) %ptr, in particular, ptr addrspace(4) %ptr. Your example gives a complex expression on the value source, the first operand.

@nikic
Copy link
Contributor

nikic commented Sep 20, 2024

These kinds of checks should go into the Lint pass instead of the Verifier pass.

@arsenm
Copy link
Contributor

arsenm commented Sep 20, 2024

The addrspace in store (addrspacecast ptr addrspace(4) %val to ptr) won't be affected by getPointerAddressSpace since this function asks about the second operand to the store, store i32 %val, ptr addrspace(4) %ptr, in particular, ptr addrspace(4) %ptr. Your example gives a complex expression on the value source, the first operand.

You're reading the example too literally. It should be valid for a pass to strip away the cast, and store directly to the original address space of the pointer without considering any special information. It would be a target specific burden placed on any IR transformation, which is not OK.

This type of check belongs in Lint, which hardly ever gets used and I'm not sure is worth the effort

%r = add i32 %a, %b
store i32 %r, ptr addrspace(4) %out
ret void
}
Copy link
Contributor

Choose a reason for hiding this comment

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

test atomicrmw, cmpxchg, and a memset, memcpy? Also at least the addrspace(6) case

@@ -0,0 +1,9 @@
; RUN: not opt -mtriple=amdgcn --passes=lint --lint-abort-on-error %s -o - |& FileCheck %s

define amdgpu_kernel void @store_const(ptr addrspace(4) %out, i32 %a, i32 %b) addrspace(4) {
Copy link
Contributor

Choose a reason for hiding this comment

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

no reason to set the address space of the function

@@ -0,0 +1,9 @@
; RUN: not opt -mtriple=amdgcn --passes=lint --lint-abort-on-error %s -o - |& FileCheck %s

Copy link
Contributor

Choose a reason for hiding this comment

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

negative test with a different target

@@ -0,0 +1,9 @@
; RUN: not opt -mtriple=amdgcn --passes=lint --lint-abort-on-error %s -o - |& FileCheck %s
Copy link
Contributor

Choose a reason for hiding this comment

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

Should move this to test/Analysis/Lint

@jofrn jofrn force-pushed the amdgpu-irverifier branch 2 times, most recently from 0b69206 to b1c3d38 Compare September 20, 2024 19:52
@jofrn jofrn changed the title [Verifier][AMDGPU] No store to const addrspace [Lint][AMDGPU] No store to const addrspace Sep 20, 2024
Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

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

New approach looks reasonable to me.

if (TT.isAMDGPU()) {
switch(AS) {
using namespace AMDGPUAS;
case CONSTANT_ADDRESS:
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be a helper in AMDGPUAddrSpace.h? I'd expect this logic to already exist somewhere, but it looks like AMDGPUAA is only looking for CONSTANT_ADDRESS and nothing else:

if (LI->getPointerAddressSpace() == AMDGPUAS::CONSTANT_ADDRESS)

@@ -401,6 +403,10 @@ void Lint::visitMemoryReference(Instruction &I, const MemoryLocation &Loc,
"Unusual: Address one pointer dereference", &I);

if (Flags & MemRef::Write) {
if (TT.isAMDGPU())
Check(!AMDGPU::isConstantAddressSpace(UnderlyingObject->getType()->getPointerAddressSpace()),
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it possible or better to do the check via TTI? I'm not sure if it's a good idea to do the check based on triple.

; RUN: opt --mtriple=amdgcn --mcpu=gfx1030 --passes=lint %s -o /dev/null |& FileCheck %s --check-prefixes=CHECK,CHECK0
; RUN: opt --mtriple=x86_64 --passes=lint --lint-abort-on-error %s -o /dev/null |& FileCheck %s --allow-empty --check-prefix=NOERR
; NOERR: {{^$}}

Copy link
Contributor

@shiltian shiltian Sep 21, 2024

Choose a reason for hiding this comment

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

I think you will need ; REQUIRES: amdgpu-registered-target as well as other targets that you want to test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants