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

Member-wise constructor generation with visibility control #4854

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

Conversation

ArielG-NV
Copy link
Contributor

@ArielG-NV ArielG-NV commented Aug 15, 2024

Fixes #3406
Fixes #4461

PR with documentation: #4988

Changes (split up into major files):
source/slang/diff.meta.slang

  • Addition of TorchTensorType. This was added since Slang requires all TorchTensor objects to have a CUDAHost attribute. When auto-generating constructors Slang needs to be aware of this type to avoid generating invalid code for CUDAHost variable using functions (automatically add CUDAHost if TorchTensor is found).

source/slang/hlsl.meta.slang

  • (19542) A custom __init() was added to TextureFootprint to more correctly do what the struct requires (this also avoided at the time a weird compile failure that only the stdlib could cause).
  • (19965) Slang cannot propagate literal generics (Shape.dimensions) properly in our use case. We work around this by generating a simple copy of our literal generic and letting Slang optimize this value out to directly use Shape.dimensions.

source/slang/slang-ast-decl.h

  • This PR adds constructors to target private, internal, and public groups of members. If a Ctor is used for public and private and internal scopes we need to store this information so that when we look for InheritanceDecl ctor's, we know which "member-wise ctor" should be used given a visibility. ConstructorTags allows us to store this information.

source/slang/slang-check-conversion.cpp

  • Initializer lists call into constructors (and no longer uses related hacks unless UseLegacyLogic is true).
  • initialization-list logic for struct objects has been reordered and reformatted due to previous logical incompatibilities.
  • Flattened initializers for structs (c-style initializers) are allowed if and only if a struct is a C-Style-Struct (see user-guide for more details, docs\user-guide\02-conventional-features.md)

source/slang/slang-check-decl.cpp

  • we auto-generate member-wise constructors for varying visibilities of members (public, public-internal, public-internal-private).
  • Implements visibility and member-wise constructor rules described in Struct initializer-list with visibility control #3406.
  • Reorders and reformats how we auto-generate constructors. We needed to move generation of constructor "declarations" into SemanticsDeclConformancesVisitor.
  • If a TorchTensor typed variable is found we auto-add a CUDA-host attribute (host only function)
  • If we check an invoke and find a TreatAsDifferentiableExpr, we do not automatically error with "invalid no_diff use-case", this is incorrect behavior. We only error if TreatAsDifferentiableExpr is of flavor no_diff.

Other changes:

  • Addition of $ZeroInit to allow us 2 important functionalities: 1. Allows us to use {} inside a __init() 2. it allows us to zero initialize with {} for when a user does not provide a __init() (which is expected behavior).
  • Due to Resource variables used in a global initializer function do not hoist correctly #4874 we cannot assign functions to global's. As per discussion we will remove this failing functionality inside tests\compute\type-legalize-global-with-init.slang.
  • We deprecated legacy init-list syntax. This syntax was not removed, only deprecated

NOTE: This is a draft

Key Changes:
1. Initializer lists call into constructors (and no longer hack around per element)
2. we auto-generate member-wise constructors for varying visibility of members (public, public-internal, public-internal-private)
3. Implements visibility and member-wise constructor rules described in shader-slang#3406
4. Reorders and reformats how we auto-generate constructors along with adding support for member-wise constructors ('visitStruct' and 'visitAggTypeDecl').
    * This was changed since currently Slang (if no constructors are found) falls back to initializer list syntax for non initializer list constructor code. Since we add a non-default-ctor this fallback logic never happens causing failures now.
5. initialization-list logic for struct objects has been reordered and reformatted due to previous logical incompatibilities.
@ArielG-NV ArielG-NV added the pr: breaking change PRs with breaking changes label Aug 15, 2024
@ArielG-NV ArielG-NV marked this pull request as draft August 15, 2024 15:29
1. fix breaking tests which cannot be fixed by adding 'old style slang array init-list syntax' support, specifically for constructing a struct without an explicit '{}'
2. clean up tests
1. resolve generics which are associated to a struct instance during init list evaluation
2. fix autodiff test with incorrect init-list
…rrent limitations."

Revert because it will break code, instead just check more generally for 0, if we don't want the "everything does an 'init'" logic this can be changes later.
…iures

remove all null default-ctor's like before, instead though redesign the hacky overload resolution into ctor such that it works:
1. resolveInvoke properly culls useless overloads
2. This stops spirious generation of empty init's (side-effect if we generate a real ctor)
3. This stops lots of warnings since we don't have a ctor that init's nothing
1. allow a more formalized init-list logic for flattened init-lists's
2. fixing invalid tests
1. clean-up documentation tests 2. fix recursive type crash with uninitialized value checks 3. fully disallow synth object printing rather than partially for auto-documentation code.
@ArielG-NV
Copy link
Contributor Author

ArielG-NV commented Sep 5, 2024

Note: to repro the [ FAILED ] DiffMaterialTests.cpp:DiffPBRTDiffuse (D3D12) Falcor failure the following can be inputted into slangc

-target hlsl -entry testDiffPBRTDiffuse -stage compute ${PATH_TO_FALCOR}\Falcor\Source\Tools\FalcorTest\Tests\DiffRendering\Material\DiffMaterialTests.cs.slang -I ${PATH_TO_FALCOR}\Falcor\Source\Tools\FalcorTest\Tests\ -I C:\code\Falcor\Source\Falcor\Utils\ -I ${PATH_TO_FALCOR}\Falcor\Source\Falcor\ -DSCENE_SDF_GRID_IMPLEMENTATION_NDSDF=1 -DSCENE_SDF_GRID_IMPLEMENTATION_SVS=2 -DSCENE_SDF_GRID_IMPLEMENTATION_SBS=3 -DSCENE_SDF_GRID_IMPLEMENTATION_SVO=4 -DSCENE_SDF_NO_INTERSECTION_METHOD=0 -DSCENE_SDF_NO_VOXEL_SOLVER=1 -DSCENE_SDF_VOXEL_SPHERE_TRACING=2 -DSCENE_SDF_NO_GRADIENT_EVALUATION_METHOD=0 -DSCENE_SDF_GRADIENT_NUMERIC_DISCONTINUOUS=1 -DSCENE_SDF_GRADIENT_NUMERIC_CONTINUOUS=2 -DSCENE_SDF_GRID_COUNT=1 -DSCENE_SDF_GRID_MAX_LOD_COUNT=1 -DSCENE_SDF_GRID_IMPLEMENTATION=0 -DSCENE_SDF_VOXEL_INTERSECTION_METHOD=0 -DSCENE_SDF_GRADIENT_EVALUATION_METHOD=0 -DSCENE_SDF_SOLVER_MAX_ITERATION_COUNT=0 -DSCENE_SDF_OPTIMIZE_VISIBILITY_RAYS=0 -DSCENE_GEOMETRY_TYPES=0 -DMATERIAL_SYSTEM_TEXTURE_DESC_COUNT=1 -DMATERIAL_SYSTEM_SAMPLER_DESC_COUNT=1 -DMATERIAL_SYSTEM_BUFFER_DESC_COUNT=1 -DMATERIAL_SYSTEM_USE_LIGHT_PROFILE=0 -DMATERIAL_SYSTEM_TEXTURE_3D_DESC_COUNT=1 -DSCENE_GRID_COUNT=1 -DFALCOR_MATERIAL_INSTANCE_SIZE=1 -DFALCOR_NVAPI_AVAILABLE=0

Note: import Rendering.Materials.PBRT.PBRTDiffuseMaterial; must be added to the top of Source\Tools\FalcorTest\Tests\DiffRendering\Material\DiffMaterialTests.cs.slang to compile all of the likely problematic files.

1. fix auto-diff issues that cause incorrect `no_diff` error when Slang should not error
2. change when Slang applies to auto-gen parameters `no_diff` to be more correct/accurate
…o better report compile failiures with hlsl
@ArielG-NV
Copy link
Contributor Author

Note: Falcor is using a c-style-init-list with a non c-style-struct (TriangleHit). This is causing a PR failiure.

@ArielG-NV
Copy link
Contributor Author

Note: any commits after this comment will include "Legacy Slang init-list support" to avoid breaking legacy Slang code

@ArielG-NV ArielG-NV marked this pull request as ready for review September 6, 2024 20:07
Footprint footprint = {__queryFootprint$(CoarseOrFine)NVAPI(
Shape.dimensions,
shapeDim,
Copy link
Collaborator

Choose a reason for hiding this comment

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

It seems like this may be due to the lookup scope of the expr isn't correct hooked up when you create the ctor call expr?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is very likely


// `vector<T,N>` and `vector<T,U>` may not be equal types, but, we have no choice in this senario but to assume coerce logic
// can resolve such situations, otherwise we need to fail
if (isSameSimpleExprType(toType, fromExpr->type))
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 this not implied by line 110?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

if we have 2 aggregate types we return true


// if allowCStyleInitList, process any ctor which comes next using the most members possible. ioArgIndex may not be 0.
// if !allowCStyleInitList, process any ctor that exactly matched our argument count. ioArgIndex must start at 0.
if (!allowCStyleInitList && ctorParamCount != Index(argCount))
Copy link
Collaborator

Choose a reason for hiding this comment

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

What if __init takes a variadic parameter pack?

Copy link
Contributor Author

@ArielG-NV ArielG-NV Sep 6, 2024

Choose a reason for hiding this comment

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

This requires a test-case

I believe coerce logic will function correctly with type packs (meaning type packs will work as intended), but I never thought-of/had-time to make a test.

source/slang/slang-constructor-utility.cpp Show resolved Hide resolved
source/slang/slang-constructor-utility.cpp Show resolved Hide resolved
source/slang/slang-constructor-utility.cpp Show resolved Hide resolved
source/slang/slang-constructor-utility.cpp Show resolved Hide resolved
source/slang/slang-check-conversion.cpp Show resolved Hide resolved
source/slang/slang-check-impl.h Show resolved Hide resolved
/// init returning data. Instead we will call `$ZeroInit` through this logic below.
Expr* _tryToSpecialCaseOverloadDefaultConstructWithoutInit(SemanticsVisitor* visitor, SemanticsVisitor::OverloadResolveContext& context, Expr* expr, OverloadCandidate* bestCandidate)
{
if (context.argCount == 0)
Copy link
Collaborator

@csyonghe csyonghe Sep 6, 2024

Choose a reason for hiding this comment

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

should we make sure the canddiate we are calling is a ctor decl?

what are we trying to do here? If we have a ctor candidate that takes more than 0 parameters but we are trying to call it with 0 args, we form an initializerList coercion instead? but wouldn't that coercion fail again due to lack of ctor? Why do we need to do this?

If we have struct S { __init (x) } and called with S s = {};, it should be an error?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was a workaround we kept from the previous implementation (but changed to be less of a hack). The main idea is that some structs may be treated as if they have an __init despite not ever declaring an __init

The goal is that we will zeroInit these objects

source/slang/slang-constructor-utility.cpp Show resolved Hide resolved
@ArielG-NV
Copy link
Contributor Author

ArielG-NV commented Sep 6, 2024

I think @kaizhangNV will have to take over and finish review of this PR (from what I understand), I apologize for the inconvenience this may cause.

(I will try to answer questions if they come up though)

source/slang/slang-check-decl.cpp Show resolved Hide resolved
}

template<typename T>
bool _containsTargetType(ASTBuilder* m_astBuilder, NodeBase* target, HashSet<NodeBase*>& visitedNodes)
Copy link
Collaborator

Choose a reason for hiding this comment

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

this is very heavy weight recursion, why do we need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We need this or a cached version of some sort (I agree this is quite an expensive function) to detect if we need our ctor to be tagged with "CudaHost" due to CudaHost only type being found.

Copy link
Contributor Author

@ArielG-NV ArielG-NV Sep 7, 2024

Choose a reason for hiding this comment

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

https://github.com/shader-slang/slang/pull/4854/files/c0e59e84f30557389d8dd895198c068c219fb6f7#r1747817868

Note: I agree, a legalization pass for 1 target is a better idea than this expensive recursive routine.

}

// pre-calculate any requirements of a CudaHostAttribute
HashSet<VarDeclBase*> requiresCudaHostModifier;
Copy link
Collaborator

Choose a reason for hiding this comment

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

It doesn't seem to worth this much complexity for such a corner case. We need to get rid of all the TorchTensorType` logic here, and just insert the cuda host decoration in a separate legalization pass when compiling to pytorch.

}

if (structDeclInfo.defaultCtor)
// Compiler generated ctor may be destroyed
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we need to remove a ctor? Can we just not generate it in the first place?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this ctor-deletion routine can just be removed completely, you are correct.

We should no longer be creating __init functions which need to be deleted in our bodyVisitor.

This must hold true since otherwise we would have a mismatch of conformances from the conformance AST-Pass and actual conformances (since deleting a ctor breaks conformances).

Copy link
Contributor

Choose a reason for hiding this comment

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

After reading the code carefully, I found that we cannot avoid removing the ctor based on current change.

This change create a empty default ctor during SemanticsDeclConformancesVisitor::visitAggTypeDecl,
and it will fill the body of this default ctor in SemanticsDeclBodyVisitor::visitAggTypeDecl.

But there're some situations where it will not fill the body of the ctor, so if that is the case, we will remove the empty default ctor. (Though I still don't quite understand why we have to remove it, removing it will cause lots of errors).

We're actually able to detect those situations during creating the ctor, however the logic to check those situations are part of SemanticsDeclBodyVisitor::visitAggTypeDecl, and there is not a very good way to re-use the results of this logic.

So I'd say let's keep it, as cost of removing it could be smaller than "detecting and not create it".

structDecl->invalidateMemberDictionary();
structDecl->buildMemberDictionary();
}
}

// Fill in our $ZeroInit
if (auto zeroInitListFunc = findZeroInitListFunc(structDecl))
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 shouldn't need this at all because our IR already knows how to zerzo init a thing.
We can simply do:

this = DefaultConstruct(This);

at the begining of the ctor, where DefaultConstruct(T) lowers to kIROp_DefaultConstruct(T)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We could add a flag to DefaultConstructExpr to remove the need for $ZeroInit.

To elaborate, DefaultConstructExpr does not handle initExpr on a struct member (from what I remember).

We need a flag that we can set to tell Slang when lowering a DefaultConstructExpr that it should take into account initExpr of varDecl's.

@csyonghe
Copy link
Collaborator

csyonghe commented Sep 6, 2024

@ArielG-NV there is no expectation that you will address any of these.
However if you can put comments on what you think are / are not possible regarding my thoughts, that will be very helpful for @kaizhangNV.

@ArielG-NV
Copy link
Contributor Author

I will leave a 👍 on tasks that should be addressed

@csyonghe
Copy link
Collaborator

csyonghe commented Sep 7, 2024

@ArielG-NV do you have a non-NV github account that we can reach you?

@ArielG-NV
Copy link
Contributor Author

My non-NV GitHub would be @16-Bit-Dog

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pr: breaking change PRs with breaking changes
Projects
None yet
3 participants