Skip to content

[SPIR-V] Parse parameterized Memory Access operands with a shared helper#209262

Open
aobolensk wants to merge 7 commits into
llvm:mainfrom
aobolensk:llvm-spirv-Memory-Access-operands
Open

[SPIR-V] Parse parameterized Memory Access operands with a shared helper#209262
aobolensk wants to merge 7 commits into
llvm:mainfrom
aobolensk:llvm-spirv-Memory-Access-operands

Conversation

@aobolensk

Copy link
Copy Markdown
Contributor

Copy printing only special cased Aligned, so alias list and scope IDs were then misread as additional masks

Copy printing only special cased Aligned, so alias list and scope IDs were then misread as additional masks
@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-backend-spir-v

Author: Arseniy Obolenskiy (aobolensk)

Changes

Copy printing only special cased Aligned, so alias list and scope IDs were then misread as additional masks


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

3 Files Affected:

  • (modified) llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp (+41-22)
  • (modified) llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.h (+1)
  • (modified) llvm/test/CodeGen/SPIRV/llvm-intrinsics/logical-memcpy.ll (+26-2)
diff --git a/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp b/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp
index b7dcd10bac809..4454921753da7 100644
--- a/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp
+++ b/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp
@@ -113,6 +113,42 @@ void SPIRVInstPrinter::printOpConstantVarOps(const MCInst *MI,
   O << Imm;
 }
 
+unsigned SPIRVInstPrinter::printMemoryOperand(const MCInst *MI, unsigned OpNo,
+                                              raw_ostream &O) {
+  O << ' ';
+  if (OpNo >= MI->getNumOperands())
+    return OpNo;
+  const uint64_t Mask = MI->getOperand(OpNo).getImm();
+  printSymbolicOperand<OperandCategory::MemoryOperandOperand>(MI, OpNo, O);
+  unsigned NextOp = OpNo + 1;
+  if (Mask & SPIRV::MemoryOperand::Aligned) {
+    O << ' ';
+    printOperand(MI, NextOp, O);
+    ++NextOp;
+  }
+  if (Mask & SPIRV::MemoryOperand::MakePointerAvailableKHR) {
+    O << ' ';
+    printOperand(MI, NextOp, O);
+    ++NextOp;
+  }
+  if (Mask & SPIRV::MemoryOperand::MakePointerVisibleKHR) {
+    O << ' ';
+    printOperand(MI, NextOp, O);
+    ++NextOp;
+  }
+  if (Mask & SPIRV::MemoryOperand::AliasScopeINTELMask) {
+    O << ' ';
+    printOperand(MI, NextOp, O);
+    ++NextOp;
+  }
+  if (Mask & SPIRV::MemoryOperand::NoAliasINTELMask) {
+    O << ' ';
+    printOperand(MI, NextOp, O);
+    ++NextOp;
+  }
+  return NextOp;
+}
+
 void SPIRVInstPrinter::recordIntType(const MCInst *MI) {
   MCRegister IntTypeReg = MI->getOperand(0).getReg();
   unsigned Bitwidth = MI->getOperand(1).getImm();
@@ -192,10 +228,7 @@ void SPIRVInstPrinter::printInst(const MCInst *MI, uint64_t Address,
         switch (OpCode) {
         case SPIRV::OpLoad:
         case SPIRV::OpStore:
-          OS << ' ';
-          printSymbolicOperand<OperandCategory::MemoryOperandOperand>(
-              MI, FirstVariableIndex, OS);
-          printRemainingVariableOps(MI, FirstVariableIndex + 1, OS);
+          printMemoryOperand(MI, FirstVariableIndex, OS);
           break;
         case SPIRV::OpSwitch:
           if (MI->getFlags() & SPIRV::INST_PRINTER_WIDTH64) {
@@ -253,17 +286,8 @@ void SPIRVInstPrinter::printInst(const MCInst *MI, uint64_t Address,
         case SPIRV::OpCopyMemory:
         case SPIRV::OpCopyMemorySized: {
           const unsigned NumOps = MI->getNumOperands();
-          for (unsigned i = NumFixedOps; i < NumOps; ++i) {
-            OS << ' ';
-            printSymbolicOperand<OperandCategory::MemoryOperandOperand>(MI, i,
-                                                                        OS);
-            if (MI->getOperand(i).getImm() & MemoryOperand::Aligned) {
-              assert(i + 1 < NumOps && "Missing alignment operand");
-              OS << ' ';
-              printOperand(MI, i + 1, OS);
-              i += 1;
-            }
-          }
+          for (unsigned i = NumFixedOps; i < NumOps;)
+            i = printMemoryOperand(MI, i, OS);
           break;
         }
         case SPIRV::OpConstantI:
@@ -346,13 +370,8 @@ void SPIRVInstPrinter::printInst(const MCInst *MI, uint64_t Address,
         }
         case SPIRV::OpPredicatedLoadINTEL:
         case SPIRV::OpPredicatedStoreINTEL: {
-          const unsigned NumOps = MI->getNumOperands();
-          if (NumOps > NumFixedOps) {
-            OS << ' ';
-            printSymbolicOperand<OperandCategory::MemoryOperandOperand>(
-                MI, NumOps - 1, OS);
-            break;
-          }
+          if (MI->getNumOperands() > NumFixedOps)
+            printMemoryOperand(MI, NumFixedOps, OS);
           break;
         }
         default:
diff --git a/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.h b/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.h
index b195c3de1801d..4d9450376a0a4 100644
--- a/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.h
+++ b/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.h
@@ -43,6 +43,7 @@ class SPIRVInstPrinter : public MCInstPrinter {
                                  bool SkipImmediates = false);
   void printOpConstantVarOps(const MCInst *MI, unsigned StartIndex,
                              raw_ostream &O);
+  unsigned printMemoryOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O);
 
   void printExtension(const MCInst *MI, unsigned OpNo, raw_ostream &O);
   template <SPIRV::OperandCategory::OperandCategory category>
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/logical-memcpy.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/logical-memcpy.ll
index b8fe49a1ee481..007a32e244ac3 100644
--- a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/logical-memcpy.ll
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/logical-memcpy.ll
@@ -1,9 +1,11 @@
-; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-unknown-unknown %s -o - | FileCheck %s
-; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-unknown-unknown --spirv-ext=+SPV_INTEL_memory_access_aliasing %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-unknown --spirv-ext=+SPV_INTEL_memory_access_aliasing %s -o - -filetype=obj | spirv-val %}
 
 ; CHECK: OpName %[[dst_var:[0-9]+]] "dst"
 ; CHECK: OpName %[[src_var:[0-9]+]] "src"
 
+; CHECK: %[[#List:]] = OpAliasScopeListDeclINTEL
+
 ; CHECK: %[[f32:[0-9]+]] = OpTypeFloat 32
 ; CHECK: %[[structS:[0-9]+]] = OpTypeStruct %[[f32]] %[[f32]] %[[f32]] %[[f32]] %[[f32]]
 ; CHECK: %[[ptr_crosswkgrp_structS:[0-9]+]] = OpTypePointer CrossWorkgroup %[[structS]]
@@ -27,6 +29,28 @@ entry:
 ; CHECK: OpFunctionEnd
 }
 
+; Aliasing metadata on an OpCopyMemory pointer adds parameterized operands (an
+; alignment literal plus an alias-list ID) after the memory access mask; the
+; printer must consume exactly those operands rather than the fixed one it
+; previously special-cased for Aligned only.
+%struct.T = type <{ float, float, float, float, float }>
+@src2 = external dso_local addrspace(1) global %struct.T, align 4
+@dst2 = external dso_local addrspace(1) global %struct.T, align 4
+
+; CHECK: %[[#]] = OpFunction
+define void @copy_aliased() {
+entry:
+; CHECK: OpCopyMemory %[[#]] %[[#]] AliasScopeINTELMask %[[#List]]
+  call void @llvm.memcpy.p1.p1.i64(ptr addrspace(1) align 4 @dst2, ptr addrspace(1) align 4 @src2, i64 20, i1 false), !alias.scope !1
+  ret void
+}
+
+declare void @llvm.memcpy.p1.p1.i64(ptr addrspace(1), ptr addrspace(1), i64, i1)
+
 attributes #0 = { "hlsl.numthreads"="8,1,1" "hlsl.shader"="compute" }
 
+!1 = !{!2}
+!2 = distinct !{!2, !3, !"copy_aliased: %this"}
+!3 = distinct !{!3, !"copy_aliased"}
+
 

@aobolensk aobolensk requested review from MrSidims and jmmartinez July 14, 2026 05:19
@@ -1,9 +1,11 @@
; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-unknown-unknown %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-unknown %s -o - -filetype=obj | spirv-val %}
; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-unknown-unknown --spirv-ext=+SPV_INTEL_memory_access_aliasing %s -o - | FileCheck %s

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should be a separate test

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Separated

++NextOp;
}
return NextOp;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Iterating in one place and incrementing iterator in another is looking very fragile.

Lets imagine in the future in SPIR-V spec will be added another memory operand mask which makes an instruction accepting yet another literal operand. This memory operand was implemented with a bug and somehow we made it this far. In such case the loop block on L289 will be executed once more time re-processing all of the already processed masks, duplicating printed operands and making the iterator be out of bound.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Left only one place with the increment, unified handling for all cases

@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown

🐧 Linux x64 Test Results

  • 200203 tests passed
  • 5438 tests skipped

✅ The build succeeded and all tests passed.

@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown

🪟 Windows x64 Test Results

  • 138470 tests passed
  • 3541 tests skipped

✅ The build succeeded and all tests passed.

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.

3 participants