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> --- source/slang/core.meta.slang | 180 ++++++++++++++++++++++++++++--------------- 1 file changed, 117 insertions(+), 63 deletions(-) (limited to 'source/slang/core.meta.slang') diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang index 3306403f5..5d2a80c29 100644 --- a/source/slang/core.meta.slang +++ b/source/slang/core.meta.slang @@ -1278,18 +1278,52 @@ struct __none_t { }; +// @hidden: this type is a BaseType since we want it to work with +// `registerBuiltinDecl` +__builtin_type($((int)BaseType::AddressSpace)) +enum AddressSpace : uint64_t +{ + Device = $((uint64_t)AddressSpace::UserPointer), + GroupShared = $((uint64_t)AddressSpace::GroupShared), +}; + +// @hidden: this type is a BaseType since we want it to work with +// `registerBuiltinDecl` +__builtin_type($((int)BaseType::MemoryScope)) +enum MemoryScope : int32_t +{ + CrossDevice = $((int32_t)MemoryScope::CrossDevice), + Device = $((int32_t)MemoryScope::Device), + Workgroup = $((int32_t)MemoryScope::Workgroup), + Subgroup = $((int32_t)MemoryScope::Subgroup), + Invocation = $((int32_t)MemoryScope::Invocation), + QueueFamily = $((int32_t)MemoryScope::QueueFamily), +} + +// @hidden: this type is a BaseType since we want it to work with +// `registerBuiltinDecl` +__builtin_type($((int)BaseType::AccessQualifier)) +enum Access : uint64_t +{ + ReadWrite = $((uint64_t)AccessQualifier::ReadWrite), + Read = $((uint64_t)AccessQualifier::Read), +} + //@public: /// Represents a pointer type. /// @param T The type of the value pointed to. /// @remarks `T* val` is equivalent to `Ptr val`. -__generic __magic_type(PtrType) __intrinsic_type($(kIROp_PtrType)) -struct Ptr +struct Ptr< + T, + Access access = Access::ReadWrite, + AddressSpace addrSpace = AddressSpace::Device> { - __generic + // A user is allowed to explicitly cast between any pointer type of + // the same address space __intrinsic_op($(kIROp_BitCast)) - __init(Ptr ptr); + __init(Ptr ptr); __intrinsic_op($(kIROp_CastIntToPtr)) __init(uint64_t val); @@ -1297,16 +1331,30 @@ struct Ptr __intrinsic_op($(kIROp_CastIntToPtr)) __init(int64_t val); + // By default, getter is not an L value __generic __subscript(TInt index) -> T { - // If a 'Ptr[index]' is referred to by a '__ref', call 'kIROp_GetOffsetPtr(index)' __intrinsic_op($(kIROp_GetOffsetPtr)) [nonmutating] ref; } }; +extension Ptr +{ + // We have a `ref` accessor if we are ReadWrite. This means only `ReadWrite` + // can be used as an L-value. + __generic + __subscript(TInt index) -> Ref + { + // If a 'Ptr[index]' is referred to by a '__ref', call 'kIROp_GetOffsetPtr(index)' + __intrinsic_op($(kIROp_GetOffsetPtr)) + [nonmutating] + ref; + } +} + //@hidden: __intrinsic_op($(kIROp_AlignedAttr)) void __align_attr(int alignment); @@ -1348,50 +1396,64 @@ void storeAligned(T* ptr, T value) __store_aligned(ptr, value, __align_attr(alignment)); } +${{{ + StringBuilder ptrTypeParameterListBuilder; + ptrTypeParameterListBuilder << "T, Access access, AddressSpace addrSpace"; + String ptrTypeParameterList = ptrTypeParameterListBuilder.toString(); + + StringBuilder ptrArgListBuilder; + ptrArgListBuilder << "T, access, addrSpace"; + String ptrArgList = ptrArgListBuilder.toString(); + + StringBuilder fullPtrTypeBuilder; + fullPtrTypeBuilder << "Ptr<" << ptrArgList << ">"; + String fullPtrType = fullPtrTypeBuilder.toString(); + +}}} //@hidden: __intrinsic_op($(kIROp_Load)) -T __load(Ptr ptr); +T __load<$(ptrTypeParameterList)>($(fullPtrType) ptr); __intrinsic_op($(kIROp_Store)) -void __store(Ptr ptr, T val); +void __store<$(ptrTypeParameterList)>($(fullPtrType) ptr, T val); __intrinsic_op($(kIROp_GetElementPtr)) -Ptr __getElementPtr(Ptr ptr, TIndex index); +$(fullPtrType) __getElementPtr<$(ptrTypeParameterList), TIndex : __BuiltinIntegerType>($(fullPtrType) ptr, TIndex index); __intrinsic_op($(kIROp_GetOffsetPtr)) -Ptr __getOffsetPtr(Ptr ptr, TIndex index); +$(fullPtrType) __getOffsetPtr<$(ptrTypeParameterList), TIndex : __BuiltinIntegerType>($(fullPtrType) ptr, TIndex index); -__generic +__generic<$(ptrTypeParameterList)> __intrinsic_op($(kIROp_Less)) -bool operator <(Ptr p1, Ptr p2); +bool operator <($(fullPtrType) p1, $(fullPtrType) p2); -__generic +__generic<$(ptrTypeParameterList)> __intrinsic_op($(kIROp_Leq)) -bool operator <=(Ptr p1, Ptr p2); +bool operator <=($(fullPtrType) p1, $(fullPtrType) p2); -__generic +__generic<$(ptrTypeParameterList)> __intrinsic_op($(kIROp_Greater)) -bool operator>(Ptr p1, Ptr p2); +bool operator>($(fullPtrType) p1, $(fullPtrType) p2); -__generic +__generic<$(ptrTypeParameterList)> __intrinsic_op($(kIROp_Geq)) -bool operator >=(Ptr p1, Ptr p2); +bool operator >=($(fullPtrType) p1, $(fullPtrType) p2); -__generic +__generic<$(ptrTypeParameterList)> __intrinsic_op($(kIROp_Neq)) -bool operator !=(Ptr p1, Ptr p2); +bool operator !=($(fullPtrType) p1, $(fullPtrType) p2); -__generic +__generic<$(ptrTypeParameterList)> __intrinsic_op($(kIROp_Eql)) -bool operator ==(Ptr p1, Ptr p2); +bool operator ==($(fullPtrType) p1, $(fullPtrType) p2); //@public: extension bool : IRangedValue { - __generic + __generic<$(ptrTypeParameterList)> __implicit_conversion($(kConversionCost_PtrToBool)) __intrinsic_op($(kIROp_CastPtrToBool)) - __init(Ptr ptr); + __init($(fullPtrType) ptr); __generic __implicit_conversion($(kConversionCost_IntegerTruncate)) @@ -1407,9 +1469,9 @@ extension bool : IRangedValue extension uint64_t : IRangedValue { - __generic + __generic<$(ptrTypeParameterList)> __intrinsic_op($(kIROp_CastPtrToInt)) - __init(Ptr ptr); + __init($(fullPtrType) ptr); static const uint64_t maxValue = 0xFFFFFFFFFFFFFFFFULL; static const uint64_t minValue = 0; @@ -1417,9 +1479,9 @@ extension uint64_t : IRangedValue extension int64_t : IRangedValue { - __generic + __generic<$(ptrTypeParameterList)> __intrinsic_op($(kIROp_CastPtrToInt)) - __init(Ptr ptr); + __init($(fullPtrType) ptr); static const int64_t maxValue = 0x7FFFFFFFFFFFFFFFLL; static const int64_t minValue = -0x8000000000000000LL; @@ -1427,9 +1489,9 @@ extension int64_t : IRangedValue extension intptr_t : IRangedValue { - __generic + __generic<$(ptrTypeParameterList)> __intrinsic_op($(kIROp_CastPtrToInt)) - __init(Ptr ptr); + __init($(fullPtrType) ptr); static const intptr_t maxValue = $(SLANG_PROCESSOR_X86_64?"0x7FFFFFFFFFFFFFFFz":"0x7FFFFFFFz"); static const intptr_t minValue = $(SLANG_PROCESSOR_X86_64?"0x8000000000000000z":"0x80000000z"); static const int size = $(SLANG_PROCESSOR_X86_64?"8":"4"); @@ -1437,9 +1499,9 @@ extension intptr_t : IRangedValue extension uintptr_t : IRangedValue { - __generic + __generic<$(ptrTypeParameterList)> __intrinsic_op($(kIROp_CastPtrToInt)) - __init(Ptr ptr); + __init($(fullPtrType) ptr); static const uintptr_t maxValue = $(SLANG_PROCESSOR_X86_64?"0xFFFFFFFFFFFFFFFFz":"0xFFFFFFFFz"); static const uintptr_t minValue = 0z; static const int size = $(SLANG_PROCESSOR_X86_64?"8":"4"); @@ -1470,7 +1532,9 @@ __intrinsic_type($(kIROp_ConstRefType)) struct ConstRef {}; -typealias __Addr = Ptr; +// __Addr is AddressSpace::Generic since Slang will specalize & validate the address-space +// internally to a concrete address-space. +typealias __Addr = Ptr; //@public: @@ -1828,16 +1892,16 @@ struct NativeString __init() { this = NativeString(""); } }; -extension Ptr +extension Ptr { __implicit_conversion($(kConversionCost_PtrToVoidPtr)) [__unsafeForceInlineEarly] - __init(NativeString nativeStr) { this = nativeStr.getBuffer(); } + __init(NativeString nativeStr) { this = Ptr(nativeStr.getBuffer()); } - __generic + __generic<$(ptrTypeParameterList)> __intrinsic_op($(kIROp_BitCast)) __implicit_conversion($(kConversionCost_PtrToVoidPtr)) - __init(Ptr ptr); + __init($(fullPtrType) ptr); __generic __intrinsic_op($(kIROp_BitCast)) @@ -2607,29 +2671,31 @@ for (auto op : intrinsicUnaryOps) }}}} -__generic +// Only ReadWrite is an L-value. +__generic __intrinsic_op(0) -[require(cpp_cuda_spirv)] -__prefix Ref operator*(Ptr value); +__prefix Ref operator*(Ptr value); -__generic +// Unknown access qualifier or Access::Read access qualifier is a promise +// that the pointer is not going to be used as an L-value. +__generic<$(ptrTypeParameterList)> __intrinsic_op(0) -[KnownBuiltin($( (int)KnownBuiltinDeclName::OperatorAddressOf))] -[require(cpp_cuda_spirv)] -__prefix Ptr operator&(__ref T value); +__prefix ConstRef operator*($(fullPtrType) value); +// TODO: [require(cpu)]. This cannot be done yet since this change breaks slangpy __generic __intrinsic_op(0) +[KnownBuiltin( $((int)KnownBuiltinDeclName::OperatorAddressOf))] [require(cpp_cuda_spirv)] -__Addr __get_addr( __ref T value); +__prefix Ptr operator&(__ref T value); -__generic +__generic<$(ptrTypeParameterList), TInt : __BuiltinIntegerType> __intrinsic_op($(kIROp_GetOffsetPtr)) -Ptr operator+(Ptr value, int64_t offset); +$(fullPtrType) operator+($(fullPtrType) value, TInt offset); -__generic +__generic<$(ptrTypeParameterList), TInt : __BuiltinIntegerType> [__unsafeForceInlineEarly] -Ptr operator -(Ptr value, int64_t offset) +$(fullPtrType) operator-($(fullPtrType) value, TInt offset) { return __getOffsetPtr(value, -offset); } @@ -2694,9 +2760,9 @@ matrix operator$(op.name)(in out matrix value) {$(fixity.bodyPrefix) value = value $(op.binOp) __builtin_cast(1); return $(fixity.returnVal); } $(fixity.qual) -__generic +__generic<$(ptrTypeParameterList)> [__unsafeForceInlineEarly] -Ptr operator$(op.name)(in out Ptr value) +$(fullPtrType) operator$(op.name)(in out $(fullPtrType) value) {$(fixity.bodyPrefix) value = value $(op.binOp) 1; return $(fixity.returnVal); } ${{{{ @@ -3556,18 +3622,6 @@ enum MemoryOrder SeqCst = $(kIRMemoryOrder_SeqCst), } -// https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_scope_id -enum MemoryScope -{ - CrossDevice = 0, - Device = 1, - Workgroup = 2, - Subgroup = 3, - Invocation = 4, - QueueFamily = 5, - ShaderCallKHR = 6, -}; - /// Represents types that can be used in any atomic operations. /// Implemented by builtin scalar types: `int`, `uint`, `int64_t`, `uint64_t`, `int8_t`, `uint8_t`, `int16_t`, `uint16_t`, `float`, `double` and `half`. [sealed] interface IAtomicable {} @@ -4307,7 +4361,7 @@ __attributeTarget(FuncDecl) attribute_syntax [RequireFullQuads] : RequireFullQuadsAttribute; __generic -typealias NodePayloadPtr = Ptr; +typealias NodePayloadPtr = Ptr; __attributeTarget(StructDecl) attribute_syntax [raypayload] : RayPayloadAttribute; -- cgit v1.2.3