From 7758625d3fea67e55e98e7e4103d56c9918365be Mon Sep 17 00:00:00 2001 From: ArielG-NV <159081215+ArielG-NV@users.noreply.github.com> Date: Fri, 29 Aug 2025 15:52:34 -0700 Subject: [CBP] Pointer frontend changes + groupshared pointer support (#7848) Resolves #7628 Resolves: #8197 Primary Goals: 1. Add `Access` to pointer 2. AddressSpace::GroupShared support for pointers (SPIR-V) 3. Add `__getAddress()` to replace `&` * `&` is not updated to `require(cpu)` since slangpy uses `&`. This means we must: (1) merge PR; (2) replace `&` with `__getAddress()`; (3) add `require(cpu)` to `&` Changes: * Added to `Ptr` the `Access` generic argument & logic (for `Access::Read`). * Moved the generic argument `AddressSpace` from `Ptr` to the end of the type. * Added pointer casting support between any `Ptr` as long as the `AddressSpace` is the same * Disallow globallycoherent T* and coherent T* * Disallow const T*, T const*, and const T* * Fixed .natvis display of `ConstantValue` `ValOperandNode` * Support generic resolution of type-casted integers * Added `VariablePointer` emitting for spirv + other minor logic needed for groupshared pointers Breaking Changes: * Anyone using the `AddressSpace` of `Ptr` will now have to account for the `Access` argument * we disallow various syntax paired with `Ptr` and `T*` --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com> --- .../pointer/const-ptr-variations.slang | 40 +++++++++++ .../pointer/get-address-validation.slang | 82 ++++++++++++++++++++++ .../pointer/globallycoherent-ptr.slang | 20 ++++++ .../pointer/groupshared-ptr-of-device.slang | 28 ++++++++ .../pointer-access/pointer-access-frontend.slang | 14 ++++ .../pointer-access/read-only-pointer-1.slang | 41 +++++++++++ .../pointer-access/read-only-pointer-2.slang | 19 +++++ .../pointer-casting/pointer-casting-rules.slang | 51 ++++++++++++++ .../pointer/pointer-self-reference.slang | 10 +-- .../pointer/ptr-to-groupshared.slang | 30 ++++++++ 10 files changed, 331 insertions(+), 4 deletions(-) create mode 100644 tests/language-feature/pointer/const-ptr-variations.slang create mode 100644 tests/language-feature/pointer/get-address-validation.slang create mode 100644 tests/language-feature/pointer/globallycoherent-ptr.slang create mode 100644 tests/language-feature/pointer/groupshared-ptr-of-device.slang create mode 100644 tests/language-feature/pointer/pointer-access/pointer-access-frontend.slang create mode 100644 tests/language-feature/pointer/pointer-access/read-only-pointer-1.slang create mode 100644 tests/language-feature/pointer/pointer-access/read-only-pointer-2.slang create mode 100644 tests/language-feature/pointer/pointer-casting/pointer-casting-rules.slang create mode 100644 tests/language-feature/pointer/ptr-to-groupshared.slang (limited to 'tests/language-feature/pointer') diff --git a/tests/language-feature/pointer/const-ptr-variations.slang b/tests/language-feature/pointer/const-ptr-variations.slang new file mode 100644 index 000000000..a2619d6c4 --- /dev/null +++ b/tests/language-feature/pointer/const-ptr-variations.slang @@ -0,0 +1,40 @@ +//TEST:SIMPLE(filecheck=CHECK_1):-stage compute -entry computeMain -target spirv -DT1 +//TEST:SIMPLE(filecheck=CHECK_2):-stage compute -entry computeMain -target spirv -DT2 +//TEST:SIMPLE(filecheck=CHECK_3):-stage compute -entry computeMain -target spirv -DT3 +//TEST:SIMPLE(filecheck=CHECK_4):-stage compute -entry computeMain -target spirv -DT4 + +// Tests for invalid use of `const` with Ptr/T* +// Due to bad syntax breaking the parser, it is more robust to use disjoint tests with +// #define's. +cbuffer Globals +{ + int* ptr; +} + +[numthreads(1, 1, 1)] +void computeMain(int id : SV_DispatchThreadID) +{ + // disallowed syntax with modifier `const` +#ifdef T1 + // CHECK_1: ([[# @LINE+1]]): error + int const* ptr1 = ptr; +#endif + +#ifdef T2 + // CHECK_2: ([[# @LINE+1]]): error + int* const ptr2 = ptr; +#endif + +#ifdef T3 + // CHECK_3: ([[# @LINE+1]]): error 20017 + const int* ptr3 = ptr; + // CHECK_3: ([[# @LINE+1]]): error 20018 + Ptr ptr4 = ptr; +#endif + +#ifdef T4 + // CHECK_4: OpEntryPoint + // CHECK_4-NOT: error + const Ptr ptr5 = ptr; +#endif +} \ No newline at end of file diff --git a/tests/language-feature/pointer/get-address-validation.slang b/tests/language-feature/pointer/get-address-validation.slang new file mode 100644 index 000000000..3931c13a2 --- /dev/null +++ b/tests/language-feature/pointer/get-address-validation.slang @@ -0,0 +1,82 @@ +//TEST:SIMPLE(filecheck=CHECK):-stage compute -entry computeMain -target spirv + +// Tests for invalid/valid use of `__getAddress` + +struct DeviceStruct +{ + int data1; + int data2; +} + +struct StructPtrInStruct +{ + DeviceStruct* ptr; +} + +uniform int* bufferUserPointer; +RWStructuredBuffer bufferStorage; +groupshared int bufferGroupShared[100]; +uniform DeviceStruct* bufferUserPointerStruct; +uniform int2* bufferUserPointerVector; + +int* output; + +typealias GroupSharedPtr = Ptr; + +GroupSharedPtr paramGroupShared(out groupshared T[100] ptr) +{ + // CHECK: ([[# @LINE+1]]): error 30019 + T* ptr1 = __getAddress(ptr[5]); + + // CHECK-NOT: ([[# @LINE+1]]): error + GroupSharedPtr ptr2 = __getAddress(ptr[5]); + + return ptr2; +} + +[numthreads(1, 1, 1)] +void computeMain(int id : SV_DispatchThreadID) +{ + // CHECK: ([[# @LINE+1]]): error 31160 + int* ptr1 = __getAddress(bufferStorage[id.x]); + + // CHECK ([[# @LINE+1]]): error + int[100]* ptr2 = __getAddress(bufferGroupShared); + + // CHECK: ([[# @LINE+1]]): error + int* ptr3 = __getAddress(bufferGroupShared[id.x]); + + // CHECK-NOT: ([[# @LINE+1]]): error + int* ptr4 = __getAddress(bufferUserPointer[id.x]); + + // CHECK-NOT: ([[# @LINE+1]]): error + GroupSharedPtr ptr5 = __getAddress(bufferGroupShared); + + // CHECK-NOT: ([[# @LINE+1]]): error + GroupSharedPtr ptr6 = __getAddress(bufferGroupShared[id.x]); + + // CHECK-NOT: ([[# @LINE+1]]): error + GroupSharedPtr ptr7 = paramGroupShared(bufferGroupShared); + + // CHECK-NOT: ([[# @LINE+1]]): error + int* ptr8 = __getAddress(bufferUserPointerStruct.data1); + + StructPtrInStruct structPtrInStruct; + structPtrInStruct.ptr = bufferUserPointerStruct; + // CHECK-NOT: ([[# @LINE+1]]): error + int* ptr9 = __getAddress(structPtrInStruct.ptr[id.x].data1); + + // CHECK-NOT: ([[# @LINE+1]]): error + int* ptr10 = __getAddress(bufferUserPointerVector[0].x); + + output[id] = ptr1[id]; + output[id] = ptr2[id][0]; + output[id] = ptr3[id]; + output[id] = ptr4[id]; + output[id] = ptr5[id]; + output[id] = ptr6[id]; + output[id] = ptr7[id]; + output[id] = ptr8[id]; + output[id] = ptr9[id]; + output[id] = ptr10[id]; +} diff --git a/tests/language-feature/pointer/globallycoherent-ptr.slang b/tests/language-feature/pointer/globallycoherent-ptr.slang new file mode 100644 index 000000000..4909537d7 --- /dev/null +++ b/tests/language-feature/pointer/globallycoherent-ptr.slang @@ -0,0 +1,20 @@ +//TEST:SIMPLE(filecheck=CHECK):-stage compute -entry computeMain -target spirv + +// Tests for invalid use of `globallycoherent` with Ptr/T* + +cbuffer Globals +{ + // CHECK: ([[# @LINE+1]]): error 30078 + globallycoherent Ptr ptr1; + // CHECK: ([[# @LINE+1]]): error 30078 + globallycoherent int* ptr2; + // CHECK: ([[# @LINE+1]]): error 30078 + coherent Ptr ptr3; + // CHECK: ([[# @LINE+1]]): error 30078 + coherent int* ptr4; +} + +[numthreads(1, 1, 1)] +void computeMain(int id : SV_DispatchThreadID) +{ +} diff --git a/tests/language-feature/pointer/groupshared-ptr-of-device.slang b/tests/language-feature/pointer/groupshared-ptr-of-device.slang new file mode 100644 index 000000000..31703819e --- /dev/null +++ b/tests/language-feature/pointer/groupshared-ptr-of-device.slang @@ -0,0 +1,28 @@ +//TEST:SIMPLE(filecheck=SPIRV):-stage compute -entry computeMain -target spirv -capability vk_mem_model+sm_6_0+spvGroupNonUniformBallot +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-vk -output-using-type -emit-spirv-directly -capability vk_mem_model+sm_6_0+spvGroupNonUniformBallot + +// Tests if we pass-through and handle pointers via groupshared-memory correctly. +// Ensure SPIRV emits coherent operations here +// SPIRV: OpEntryPoint +// SPIRV-NOT: error + +// CHECK: 1 +// CHECK-NEXT: 0 +// CHECK-NEXT: 2 +// CHECK-NEXT: 0 +// CHECK-NEXT: 3 + +//TEST_INPUT:ubuffer(data=[0 0 0 0 0], stride=4):out,name=outputBuffer +uniform int* outputBuffer; + +groupshared int* sharedPtr[3]; + +[numthreads(3, 1, 1)] +void computeMain(uint3 group_thread_id: SV_GroupThreadID) +{ + sharedPtr[group_thread_id.x] = outputBuffer + group_thread_id.x; + sharedPtr[group_thread_id.x] = sharedPtr[group_thread_id.x]+group_thread_id.x; + GroupMemoryBarrierWithGroupSync(); + + *sharedPtr[group_thread_id.x] = group_thread_id.x+1; +} \ No newline at end of file diff --git a/tests/language-feature/pointer/pointer-access/pointer-access-frontend.slang b/tests/language-feature/pointer/pointer-access/pointer-access-frontend.slang new file mode 100644 index 000000000..98a2a076d --- /dev/null +++ b/tests/language-feature/pointer/pointer-access/pointer-access-frontend.slang @@ -0,0 +1,14 @@ +//TEST:SIMPLE(filecheck=CHECK):-stage compute -entry computeMain -target spirv + +//CHECK: OpEntryPoint +//CHECK-NOT: error + +int* processMemory; +int* output; + +[numthreads(1, 1, 1)] +void computeMain(int id : SV_DispatchThreadID) +{ + Ptr ptr1 = processMemory + id.x + 5; + Ptr ptr2 = processMemory + id.x + 4; +} \ No newline at end of file diff --git a/tests/language-feature/pointer/pointer-access/read-only-pointer-1.slang b/tests/language-feature/pointer/pointer-access/read-only-pointer-1.slang new file mode 100644 index 000000000..e7f1ad534 --- /dev/null +++ b/tests/language-feature/pointer/pointer-access/read-only-pointer-1.slang @@ -0,0 +1,41 @@ +//TEST:SIMPLE(filecheck=CHECK):-stage compute -entry computeMain -target spirv + +// Writing with a read-only pointer should be an error + +int* processMemory; +RWStructuredBuffer output; + +typealias ReadPtr = Ptr; + +void writeToReadOnlyPointer(ReadPtr ptr) +{ + // CHECK: ([[# @LINE+1]]): error 30011 + ptr[0] = 1; +} + +void writeToReadOnlyPointerOut(out int ptrVal) +{ + ptrVal = 1; +} + +[numthreads(1, 1, 1)] +void computeMain(int id : SV_DispatchThreadID) +{ + // CHECK-NOT: ([[# @LINE+1]]): error + ReadPtr ptr1 = ReadPtr(processMemory + id.x); + + // CHECK: ([[# @LINE+1]]): error 30011 + ptr1[id + 1] = 1; + // CHECK: ([[# @LINE+1]]): error 30011 + *ptr1 = 1; + + writeToReadOnlyPointer(ptr1); + + // CHECK: ([[# @LINE+1]]): error 30047 + writeToReadOnlyPointerOut(ptr1[1]); + + // CHECK: ([[# @LINE+1]]): error 30047 + writeToReadOnlyPointerOut(*(ptr1+2)); + + output[id] = ptr1[id]; +} diff --git a/tests/language-feature/pointer/pointer-access/read-only-pointer-2.slang b/tests/language-feature/pointer/pointer-access/read-only-pointer-2.slang new file mode 100644 index 000000000..c5288caba --- /dev/null +++ b/tests/language-feature/pointer/pointer-access/read-only-pointer-2.slang @@ -0,0 +1,19 @@ +//TEST:SIMPLE(filecheck=CHECK):-stage compute -entry computeMain -target spirv + +// Tests valid use of read-only pointer + +// CHECK: OpEntryPoint +// CHECK-NOT: error + +int* processMemory; +int* output; + +typealias ReadPtr = Ptr; + +[numthreads(1, 1, 1)] +void computeMain(int id : SV_DispatchThreadID) +{ + ReadPtr ptr1 = ReadPtr(processMemory + id.x); + + output[id] = ptr1[id]; +} diff --git a/tests/language-feature/pointer/pointer-casting/pointer-casting-rules.slang b/tests/language-feature/pointer/pointer-casting/pointer-casting-rules.slang new file mode 100644 index 000000000..d0a016fe5 --- /dev/null +++ b/tests/language-feature/pointer/pointer-casting/pointer-casting-rules.slang @@ -0,0 +1,51 @@ +//TEST:SIMPLE(filecheck=CHECK):-stage compute -entry computeMain -target spirv + +// Tests pointer casting rules: Only explicit casting is allowed between pointer types. +// All implicit conversions between pointer types should fail. +int* processMemory; +RWStructuredBuffer output; + +[numthreads(1, 1, 1)] +void computeMain(int id : SV_DispatchThreadID) +{ + // regular address-of + // CHECK-NOT: ([[# @LINE+1]]): error + Ptr rwPtr = processMemory + id.x; + // copying a pointer of T* syntax + // CHECK-NOT: ([[# @LINE+1]]): error + Ptr copiedPtrOfLegacySyntax = processMemory; + // casting to Read ptr + // CHECK-NOT: ([[# @LINE+1]]): error + Ptr rPtr = Ptr(processMemory + id.x); + + // casting to RW ptr from a R ptr + // CHECK-NOT: ([[# @LINE+1]]): error + Ptr p1 = Ptr(rPtr); + // casting to R ptr from a RW ptr + // CHECK-NOT: ([[# @LINE+1]]): error + Ptr p2 = Ptr(rwPtr); + // casting to ptr of different type + // CHECK-NOT: ([[# @LINE+1]]): error + Ptr p3 = Ptr(rPtr); + + // Cannot implicit cast ptr's + // CHECK: ([[# @LINE+1]]): error 30019 + Ptr p4 = rPtr; + // cannot implcitly cast between different access qualifiers + // CHECK: ([[# @LINE+1]]): error 30019 + Ptr p5 = Ptr(processMemory + id.x); + // cannot implcitly cast between different access qualifiers + // CHECK: ([[# @LINE+1]]): error 30019 + Ptr p6 = Ptr(processMemory + id.x); + + // TODO: Enable this when we allow user-defined group-shared address space, Issue #8173. + // Cannot cast between different address spaces. + // CHECK: ([[# @LINE+1]]): error + Ptr p7 = Ptr(rwPtr); + // CHECK: ([[# @LINE+1]]): error + Ptr p8 = Ptr(p1); + // CHECK: ([[# @LINE+1]]): error + Ptr p9 = rwPtr; + + output[id] = *rwPtr; +} diff --git a/tests/language-feature/pointer/pointer-self-reference.slang b/tests/language-feature/pointer/pointer-self-reference.slang index e78b70db0..75ff4e7a9 100644 --- a/tests/language-feature/pointer/pointer-self-reference.slang +++ b/tests/language-feature/pointer/pointer-self-reference.slang @@ -1,6 +1,8 @@ // pointer-self-reference.slang -//TEST(compute):COMPARE_COMPUTE_EX:-cpu -compute -output-using-type -shaderobj +// We are disabling this test because '&' is intentionally not supported. +// Design for pointers in Slang are not yet finalized. +//DISABLE_TEST(compute):COMPARE_COMPUTE_EX:-cpu -compute -output-using-type -shaderobj //TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer RWStructuredBuffer outputBuffer; @@ -18,13 +20,13 @@ void computeMain(int3 dispatchThreadID: SV_DispatchThreadID) Thing things[2]; - things[0].next = &things[1]; + things[0].next = __getAddress(things[1]); things[0].value = 27; - things[1].next = &things[0]; + things[1].next = __getAddress(things[0]); things[1].value = idx * idx; - Ptr cur = &things[0]; + Ptr cur = __getAddress(things[0]); for (int i = 0; cur && i < idx; ++i) { diff --git a/tests/language-feature/pointer/ptr-to-groupshared.slang b/tests/language-feature/pointer/ptr-to-groupshared.slang new file mode 100644 index 000000000..3ad4c5e0b --- /dev/null +++ b/tests/language-feature/pointer/ptr-to-groupshared.slang @@ -0,0 +1,30 @@ +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-vk -output-using-type -emit-spirv-directly + +// Tests if we handle passing groupshared address-space pointers correctly to a function +// when that data-type needs legalization (Data -> Data_natural due to `lower-buffer-element-type`). +// CHECK: 1 +// CHECK-NEXT: 2 +// CHECK-NEXT: 0 + +struct Data +{ + int value1; + int value2; +} + +//TEST_INPUT:ubuffer(data=[0 0 0], stride=4):out,name=outputBuffer +uniform int* outputBuffer; +groupshared Data shared; + +void foo(Ptr ptr) +{ + outputBuffer[0] = ptr.value1; + outputBuffer[1] = ptr.value2; +} + +[numthreads(3, 1, 1)] +void computeMain(uint3 group_thread_id: SV_GroupThreadID) +{ + shared = Data(1, 2); + foo(__getAddress(shared)); +} \ No newline at end of file -- cgit v1.2.3