Thanks to visit codestin.com
Credit goes to github.com

Skip to content

support specialization constant sized array #6871

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

Open
wants to merge 18 commits into
base: master
Choose a base branch
from

Conversation

kaizhangNV
Copy link
Contributor

@kaizhangNV kaizhangNV commented Apr 21, 2025

Goal of this PR
We want to support an array whose size can be specialization constant for shared/global variable e.g.

layout (constant_id = 0) const uint BLOCK_SIZE = 64;
shared float buf_a[(BLOCK_SIZE + 5) * 4];

Overview of the solution:

  • During IndexExpr check, we will loose the restriction to allow SpecConst passing, but the size parameter will not be a constant value because it cannot be folded into a constant, instead it will be an expression that will result in SpecConst value.
  • During IR lowering, we will still lower the array to sized array, but the size will not be an int value, instead we will lower it to a global constant, the initial value will be the IR inst lowering from the specConst expression from above step, and we will decorate this global constant variable with kIROp_SpecializationConstantOpDecoration.
  • During spirv emit stage, we will allow emit global constant variable only when it has the decoration of kIROp_SpecializationConstantOpDecoration.
  • We have to implement new logic to emit OpSpecConstantOp, the existing emit logic doesn't support emitting OpSpecConstantOp, especially this op can embed arithmetic operation at global scope, where we can only emit arithmetic instruct at local. But there are only few instructs we need to support.

Overview of the solution:
Note: This PR is not aimed to support all possible opcodes supported by OpSpecConstantOp, only those 32-bit integer opcode, because only those types can be used for array size. For those untypical usage, we can leave in the future to extend, current emit struct should be easy to extend.

@kaizhangNV kaizhangNV requested a review from a team as a code owner April 21, 2025 17:34
@kaizhangNV kaizhangNV marked this pull request as draft April 21, 2025 17:34
@kaizhangNV kaizhangNV changed the title support specialization constant sized array WIP: support specialization constant sized array Apr 21, 2025
@kaizhangNV kaizhangNV added the pr: non-breaking PRs without breaking changes label Apr 21, 2025
@csyonghe
Copy link
Collaborator

My thinking is that we should lower such array as a sized array. Why is that not possible?

getTypeTags function should report such array types as unsized, and that should be everything we need...

@csyonghe
Copy link
Collaborator

The size of the array should be folded into a GenericParamIntVal which wraps a declref to the spec const var, similar to how we handle linktime array sizes.

The GenericParamIntVal used to mean the IntVal is from a generic int parameter, however, that later got extended to also represent int values from a link time constant. It really should have been renamed to DeclRefIntVal.

@kaizhangNV
Copy link
Contributor Author

My thinking is that we should lower such array as a sized array. Why is that not possible?

It's not impossible. I made this choice is because by lower to unsized array could potentially avoid any potential checks during type checking and IR legalization pass, so that could make this PR simpler, while the two decisions (unsized array vs sized array) should be equally feasible. But maybe I'm overthinking about the cost.

@csyonghe
Copy link
Collaborator

I don't think there are any checks after IR lowering, and we should be fine there.

@kaizhangNV
Copy link
Contributor Author

OK, I can change it to sized array, it should be very small change.

But the problem still exists, take this example in the description

<arraySize> = (BLOCK_SIZE + 5) * 4

we want to emit this to something like this:

%1 = OpSpecConstantOp Add BLOCK_SIZE 5
%2 = OpSpecConstantOp MIUL %1 4

our currently emit logic won't let me do that. So I guess I will have to implement the logic in emit-spirv.

@csyonghe
Copy link
Collaborator

Yeah, we will need to find a way to emit this, independent from how we represent this in the type system or IR.

@kaizhangNV kaizhangNV force-pushed the spec_const_arrSize branch 3 times, most recently from 8bf2a3d to 9c8ea34 Compare April 23, 2025 21:07
@kaizhangNV kaizhangNV force-pushed the spec_const_arrSize branch from 9fe0c62 to 7ecbadd Compare May 7, 2025 17:07
@kaizhangNV kaizhangNV marked this pull request as ready for review May 7, 2025 20:17
@kaizhangNV kaizhangNV changed the title WIP: support specialization constant sized array support specialization constant sized array May 7, 2025
Copy link
Collaborator

@csyonghe csyonghe left a comment

Choose a reason for hiding this comment

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

I don't think you need to make any changes to ir lowering or mangling at all, if we reuse/rename the existing GenericParamIntVal to point to speicalization constant decls.

We just need to insert the right spirv legalization logic to turn the intermediate expressions (e.g. opAdd etc.) into its own specConst, before it is used in array sizes.

if (elementCount->getType() != getIntType())
{
// Canonicalize constant elementCount to int.
if (auto elementCountConstantInt = as<ConstantIntVal>(elementCount))
{
elementCount = getIntVal(getIntType(), elementCountConstantInt->getValue());
}
else if (auto elementCountSpecConstInt = as<SpecializationConstantIntVal>(elementCount))
{
elementCount = getSpecConstIntVal(getIntType(), elementCountSpecConstInt->getValue());
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think we need this case. The else branch should handle the specializeation constant case just fine.

@@ -524,6 +529,10 @@ class ASTBuilder : public RefObject
PtrTypeBase* getPtrType(Type* valueType, AddressSpace addrSpace, char const* ptrTypeName);

ArrayExpressionType* getArrayType(Type* elementType, IntVal* elementCount);
ArrayExpressionType* getArrayType(
Copy link
Collaborator

Choose a reason for hiding this comment

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

we shouldn't need this.

@@ -386,6 +386,11 @@ class ASTBuilder : public RefObject
return getOrCreate<ConstantIntVal>(type, value);
}

SpecializationConstantIntVal* getSpecConstIntVal(Type* type, Val* specConstVal)
Copy link
Collaborator

Choose a reason for hiding this comment

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

as we discussed, we just need getDeclRefIntVal.

else
{
allConst = false;
}
}

if (allConst && hasSpecializationConstant)
{
// If the expression contains a specialization constant, we are not able to constant fold
Copy link
Collaborator

Choose a reason for hiding this comment

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

If we don't add the hasSpecializationConstant change here, and just have everything processed the same way as if a specialization constant is a generic value parameter, would anything go wrong?

I feel like things will run just fine if we undo all the changes to this function.

@@ -1974,7 +2006,9 @@ IntVal* SemanticsVisitor::tryConstantFoldDeclRef(
// if they're marked `const`.
if (decl->hasModifier<SpecializationConstantAttribute>() ||
decl->hasModifier<VkConstantIdAttribute>())
return nullptr;
{
Copy link
Collaborator

Choose a reason for hiding this comment

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

also need kind == ConstantFoldingKind::SpecializationConstantTime

if (auto specConstElementCount = as<SpecializationConstantIntVal>(elementCount))
{
// We need to check if the specialization constant has valid specConst expression
if (!specConstElementCount->getValue())
Copy link
Collaborator

Choose a reason for hiding this comment

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

How can we ever run into this case? SpecializationConstantIntVal either exists and points to a valid var decl, or it doesn't. SpecializationConstantIntVal should be holding a declref to the var decl.

Val* getValue()
{
Val* val = getOperand(1);
if (!as<FuncCallIntVal>(val) && !as<DeclRefBase>(val))
Copy link
Collaborator

Choose a reason for hiding this comment

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

SpecializationConstantIntVal should just be wrapping a declref, val here shouldn't be a FuncCallIntVal.

@@ -492,6 +492,8 @@ struct SharedIRGenContext
Dictionary<SourceFile*, IRInst*> mapSourceFileToDebugSourceInst;
Dictionary<String, IRInst*> mapSourcePathToDebugSourceInst;

Dictionary<SpecializationConstantIntVal*, IRInst*> mapSpecConstValToIRInst;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we don't need this mapping, if we use the existing GenericParamIntVal to refer to specialization constants.

@@ -2056,7 +2074,38 @@ struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, Lower
auto elementType = lowerType(context, type->getElementType());
if (!type->isUnsized())
{
auto elementCount = lowerSimpleVal(context, type->getElementCount());
IRInst* elementCount = nullptr;
if (auto specConstIntVal = as<SpecializationConstantIntVal>(type->getElementCount()))
Copy link
Collaborator

Choose a reason for hiding this comment

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

This isn't needed. The existing lowering logic for GenericParamIntVal should just work for specialization constants.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pr: non-breaking PRs without breaking changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants