diff options
| author | ArielG-NV <159081215+ArielG-NV@users.noreply.github.com> | 2024-06-12 12:46:24 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-06-12 09:46:24 -0700 |
| commit | b7e824347a5de25cc013af30e43bd405b8b5698f (patch) | |
| tree | 74f477e883add12978d1586c89814364d8a1f275 | |
| parent | ccc26c2d22d471ae649bf16f37ed1cd6cfbddd1b (diff) | |
Add slangc flag to `-zero-initialize` all variables (#3987)
* Default (zero'd) values with `-zero-initialize` flag
Adds `-zero-initialize` flag to set values to a __default() expression if they are missing a initExpr.
* address review and ensure __default calls ctor + zero's fields.
1. We must keep zero-initialize in SemanticsDeclHeaderVisitor. This is done because else a ctor will be initialized before we can set struct fields to `__default`.
2. IRDefaultCtorDecoration was added to track default ctor's with parent struct.
3. ParentAggTypeModifier was added to track ChildOfStruct->IRType for sharing data such as with functions. This is required to ensure we associate a lowered function with a lowered struct type
* Removed decoration to track defaultCtor in favor of field.
This was done since decorations are checked for IR objects, storing auxillary info does not work here as a result if usable object.
* address some review comments
Since `IDefaultInitializable` is taking a considerabley larger amount of time than anticipated I am pushing some of the other fixes requested. I did not remove the "IRStruct storing a default Ctor" hack yet.
mostly renamed/adjusted tests to work as intended
added test to ensure we don't synthisize a junk `= 0` when not in `zero initialize` mode
removed member in favor of sharedContext+dictionary.
* a working but incorrect impl
* default init without any IR hacks (fully working aside from generic/containored-types)
* Finish zero init code
1. IDefaultInitializer interface was added. If conforming, your type may be zero-initialized. To Conform a `__init()` is required
2. `[OnlyAutoInitIfForced]` was added. This attribute states that a default initializer should only be implicitly called if forced by the compiler (`zero-initialize` for example). This allows types which implicitly/explicitly conform to IDefaultInitialize to have optional auto-init behavior (which is Slang's default for user structs) to be disabled.
* note about `[OnlyAutoInitIfForced]`. This is required for std-lib to not automatically resolve init-expressions for std-lib, but it has the added benifit of allowing user made structs/classes to control the default behavior of initializing
* fix ErrType assumption
* testing why dx12 fails local but passes CI
* push vector changes to generic test
* push syntax adjustment, still figuring out what is wrong with cuda.
* remove debug changes & adjust style
* fix field-init expressions with structs initializers
don't init a static in a ctor. This would be illegal code and wrong code (init list in lower-to-ir)
* minor adjustments temporarily while the rest of the issue is discussed
* fix
* implement IDefaultInitializable
* remove a unneeded whitespace change
* fix type checking error
should be checking if a valid type is `Type`, not `BasicExpressionType`
* needs to be DeclRefType, not Type
* fix langguage server error
* change findinheritance for correctness + cleanup
* remove return false
verified the issue was `findInheritance`
* push attempt at language server fix
* still trying to fix inheritance
* added extension support, remove redundant code
Did not address all review comments yet, want to see if CI also passes my changes
* undo a change which caused CI to fail
* change logic + DefaultConstructExpr
setup code to use defaultConstructExpr when possible to construct a default without overhead of invoke/related
also changed code so parent's defaultInitializable propegates to derived member
* 1. fix error in `isSubtype` 2. add flag to isSubtype
`subtypeInheritanceIsNotFullyResolved` was added since we may not be done the lookup stage but still require `isSubtype` checking to verify usage of inheritance while working with inheritance. In This case we will just skip `ensureLookup` and "caching" (since we don't have a cache invalidation system, nor need)
* fix bug in logic + add test to better catch the bug
* address comment + isSubTypeOption + wrapper type test,
* fix wrong code adjustment
I checked on the CI and realized I caused a failure, mistake was made not negating some code
* syntax, class naming capital
* remove stdlib default initialize changes, replace with `__default()` for init
* remove redundant code + fix defaultConstruct emitting
previously defaultConstruct emitting was crashing due to having generics unresolved. By not resolving the default construct immediately, everything works.
* remove a coment
* add test to ensure static variables dont `init` inside a struct's `__init`
* fix Ptr members breaking struct use
* address review and add -zero-initialize test
`-zero-initialize` test was added to be sure debug pointers are not broken with default init values
---------
Co-authored-by: Yong He <yonghe@outlook.com>
34 files changed, 966 insertions, 88 deletions
@@ -841,6 +841,7 @@ extern "C" Language, MatrixLayoutColumn, // bool MatrixLayoutRow, // bool + ZeroInitialize, // bool IgnoreCapabilities, // bool ModuleName, // stringValue0: module name. Output, diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang index fcb08984b..3f396b864 100644 --- a/source/slang/core.meta.slang +++ b/source/slang/core.meta.slang @@ -74,6 +74,13 @@ syntax snorm : SNormModifier; /// syntax __extern_cpp : ExternCppModifier; +__magic_type(DefaultInitializableType) +interface IDefaultInitializableType +{ + __builtin_requirement($( (int)BuiltinRequirementKind::DefaultInitializableConstructor)) + __init(); +} + interface IComparable { __builtin_requirement($( (int)BuiltinRequirementKind::Equals)) @@ -187,7 +194,9 @@ interface IFloat : IArithmetic, IDifferentiable /// A type that can be used as an operand for builtins [sealed] [builtin] -interface __BuiltinType {} +interface __BuiltinType +{ +} /// A type that can be used for arithmetic operations [sealed] @@ -385,20 +394,6 @@ interface IArray<T> } } -__generic<T, let N:int> -__magic_type(ArrayExpressionType) -struct Array : IArray<T> -{ - [ForceInline] - int getCount() { return N; } - - __subscript(int index) -> T - { - __intrinsic_op($(kIROp_GetElement)) - get; - } -} - // The "comma operator" is effectively just a generic function that returns its second // argument. The left-to-right evaluation order guaranteed by Slang then ensures that // `left` is evaluated before `right`. @@ -1037,6 +1032,20 @@ extension int16_t : IRangedValue static const int16_t minValue = -32768; } +__generic<T, let N:int> +__magic_type(ArrayExpressionType) +struct Array : IArray<T> +{ + [ForceInline] + int getCount() { return N; } + + __subscript(int index) -> T + { + __intrinsic_op($(kIROp_GetElement)) + get; + } +} + /// An `N` component vector with elements of type `T`. __generic<T = float, let N : int = 4> __magic_type(VectorExpressionType) diff --git a/source/slang/slang-ast-builder.cpp b/source/slang/slang-ast-builder.cpp index 0c30366e8..a672c1b7e 100644 --- a/source/slang/slang-ast-builder.cpp +++ b/source/slang/slang-ast-builder.cpp @@ -419,6 +419,16 @@ bool ASTBuilder::isDifferentiableInterfaceAvailable() return (m_sharedASTBuilder->tryFindMagicDecl("DifferentiableType") != nullptr); } +DeclRef<InterfaceDecl> ASTBuilder::getDefaultInitializableTypeInterfaceDecl() +{ + DeclRef<InterfaceDecl> declRef = DeclRef<InterfaceDecl>(getBuiltinDeclRef("DefaultInitializableType", nullptr)); + return declRef; +} +Type* ASTBuilder::getDefaultInitializableType() +{ + return DeclRefType::create(m_sharedASTBuilder->m_astBuilder, getDefaultInitializableTypeInterfaceDecl()); +} + MeshOutputType* ASTBuilder::getMeshOutputTypeFromModifier( HLSLMeshShaderOutputModifier* modifier, Type* elementType, diff --git a/source/slang/slang-ast-builder.h b/source/slang/slang-ast-builder.h index 1c6637c31..029c24216 100644 --- a/source/slang/slang-ast-builder.h +++ b/source/slang/slang-ast-builder.h @@ -485,6 +485,9 @@ public: bool isDifferentiableInterfaceAvailable(); + DeclRef<InterfaceDecl> getDefaultInitializableTypeInterfaceDecl(); + Type* getDefaultInitializableType(); + MeshOutputType* getMeshOutputTypeFromModifier( HLSLMeshShaderOutputModifier* modifier, Type* elementType, diff --git a/source/slang/slang-ast-expr.h b/source/slang/slang-ast-expr.h index cf873bf0d..9ed725e1a 100644 --- a/source/slang/slang-ast-expr.h +++ b/source/slang/slang-ast-expr.h @@ -40,6 +40,11 @@ class VarExpr : public DeclRefExpr SLANG_AST_CLASS(VarExpr) }; +class DefaultConstructExpr : public Expr +{ + SLANG_AST_CLASS(DefaultConstructExpr) +}; + // An expression that references an overloaded set of declarations // having the same name. class OverloadedExpr : public Expr diff --git a/source/slang/slang-ast-support-types.h b/source/slang/slang-ast-support-types.h index 2d238d717..648baa820 100644 --- a/source/slang/slang-ast-support-types.h +++ b/source/slang/slang-ast-support-types.h @@ -466,6 +466,7 @@ namespace Slang /// functions, so it belongs in the last phase of checking. /// DefinitionChecked, + DefaultConstructorReadyForUse = DefinitionChecked, /// The capabilities required by the decl is infered and validated. /// @@ -1601,6 +1602,8 @@ namespace Slang /// The kind of a builtin interface requirement that can be automatically synthesized. enum class BuiltinRequirementKind { + DefaultInitializableConstructor, ///< The `IDefaultInitializable.__init()` method + DifferentialType, ///< The `IDifferentiable.Differential` associated type requirement DZeroFunc, ///< The `IDifferentiable.dzero` function requirement DAddFunc, ///< The `IDifferentiable.dadd` function requirement diff --git a/source/slang/slang-ast-type.h b/source/slang/slang-ast-type.h index 74fae139c..b007ad854 100644 --- a/source/slang/slang-ast-type.h +++ b/source/slang/slang-ast-type.h @@ -450,6 +450,11 @@ class DifferentiableType : public BuiltinType SLANG_AST_CLASS(DifferentiableType) }; +class DefaultInitializableType : public BuiltinType +{ + SLANG_AST_CLASS(DefaultInitializableType); +}; + // A vector type, e.g., `vector<T,N>` class VectorExpressionType : public ArithmeticExpressionType { diff --git a/source/slang/slang-check-conformance.cpp b/source/slang/slang-check-conformance.cpp index 59547ef0a..0ff4bfed4 100644 --- a/source/slang/slang-check-conformance.cpp +++ b/source/slang/slang-check-conformance.cpp @@ -51,19 +51,26 @@ namespace Slang SubtypeWitness* SemanticsVisitor::isSubtype( Type* subType, - Type* superType) + Type* superType, + IsSubTypeOptions isSubTypeOptions + ) { SubtypeWitness* result = nullptr; if (getShared()->tryGetSubtypeWitnessFromCache(subType, superType, result)) return result; - result = checkAndConstructSubtypeWitness(subType, superType); + result = checkAndConstructSubtypeWitness(subType, superType, isSubTypeOptions); + + if(int(isSubTypeOptions) & int(IsSubTypeOptions::NotReadyForLookup)) + return result; + getShared()->cacheSubtypeWitness(subType, superType, result); return result; } SubtypeWitness* SemanticsVisitor::checkAndConstructSubtypeWitness( Type* subType, - Type* superType) + Type* superType, + IsSubTypeOptions isSubTypeOptions) { // TODO: The Slang codebase is currently being quite slippery by conflating // multiple concepts, all under the banner of a "subtype" test: @@ -105,11 +112,14 @@ namespace Slang // tangling convertibility into it. // First, make sure both sub type and super type decl are ready for lookup. - if (auto subDeclRefType = as<DeclRefType>(subType)) + if ( !(int(isSubTypeOptions) & int(IsSubTypeOptions::NotReadyForLookup)) ) { - ensureDecl(subDeclRefType->getDeclRef().getDecl(), DeclCheckState::ReadyForLookup); + if (auto subDeclRefType = as<DeclRefType>(subType)) + { + ensureDecl(subDeclRefType->getDeclRef().getDecl(), DeclCheckState::ReadyForLookup); + } } - if (auto superDeclRefType = as<DeclRefType>(subType)) + if (auto superDeclRefType = as<DeclRefType>(superType)) { ensureDecl(superDeclRefType->getDeclRef().getDecl(), DeclCheckState::ReadyForLookup); } @@ -189,10 +199,10 @@ namespace Slang // We therefore simply recursively test both `T <: L` // and `T <: R`. // - auto leftWitness = isSubtype(subType, conjunctionSuperType->getLeft()); + auto leftWitness = isSubtype(subType, conjunctionSuperType->getLeft(), IsSubTypeOptions::None); if (!leftWitness) return nullptr; // - auto rightWitness = isSubtype(subType, conjunctionSuperType->getRight()); + auto rightWitness = isSubtype(subType, conjunctionSuperType->getRight(), IsSubTypeOptions::None); if (!rightWitness) return nullptr; // If both of the sub-relationships hold, we can construct @@ -238,7 +248,7 @@ namespace Slang bool SemanticsVisitor::isTypeDifferentiable(Type* type) { - return isSubtype(type, m_astBuilder->getDiffInterfaceType()); + return isSubtype(type, m_astBuilder->getDiffInterfaceType(), IsSubTypeOptions::None); } bool SemanticsVisitor::doesTypeHaveTag(Type* type, TypeTag tag) @@ -319,7 +329,7 @@ namespace Slang Type* type, Type* interfaceType) { - return isSubtype(type, interfaceType); + return isSubtype(type, interfaceType, IsSubTypeOptions::None); } TypeEqualityWitness* SemanticsVisitor::createTypeEqualityWitness( diff --git a/source/slang/slang-check-constraint.cpp b/source/slang/slang-check-constraint.cpp index 6a9b0adfa..23f7354d9 100644 --- a/source/slang/slang-check-constraint.cpp +++ b/source/slang/slang-check-constraint.cpp @@ -80,7 +80,7 @@ namespace Slang Type* interfaceType) { // The most basic test here should be: does the type declare conformance to the trait. - if(isSubtype(type, interfaceType)) + if(isSubtype(type, interfaceType, IsSubTypeOptions::None)) return type; // Just because `type` doesn't conform to the given `interfaceDeclRef`, that @@ -120,7 +120,7 @@ namespace Slang continue; // We only want to consider types that implement the target interface. - if(!isSubtype(candidateType, interfaceType)) + if(!isSubtype(candidateType, interfaceType, IsSubTypeOptions::None)) continue; // We only want to consider types where we can implicitly convert from `type` @@ -484,7 +484,7 @@ namespace Slang } // Search for a witness that shows the constraint is satisfied. - auto subTypeWitness = isSubtype(sub, sup); + auto subTypeWitness = isSubtype(sub, sup, IsSubTypeOptions::None); if(subTypeWitness) { // We found a witness, so it will become an (implicit) argument. diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index bf61a6c2e..f8f6d2dcb 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -1870,7 +1870,7 @@ namespace Slang DeclRefType::create(m_astBuilder, structDecl), structDecl->ownedScope, LookupMask::Function, - LookupOptions::IgnoreInheritance); + (LookupOptions)((Index)LookupOptions::IgnoreInheritance | (Index)LookupOptions::NoDeref)); if (!ctorLookupResult.isValid()) return ctorList; @@ -1948,8 +1948,46 @@ namespace Slang checkVisibility(classDecl); } + static Expr* constructDefaultInitExprForVar(SemanticsVisitor* visitor, VarDeclBase* varDecl) + { + if (!varDecl->type || !varDecl->type.type) + return nullptr; + + ConstructorDecl* defaultCtor = nullptr; + auto declRefType = as<DeclRefType>(varDecl->type.type); + if (declRefType) + { + if (auto structDecl = as<StructDecl>(declRefType->getDeclRef().getDecl())) + { + defaultCtor = _getDefaultCtor(structDecl); + } + } + + if (defaultCtor) + { + auto* invoke = visitor->getASTBuilder()->create<InvokeExpr>(); + auto member = visitor->getASTBuilder()->getMemberDeclRef(declRefType->getDeclRef(), defaultCtor); + invoke->functionExpr = visitor->ConstructDeclRefExpr(member, nullptr, defaultCtor->loc, nullptr); + return invoke; + } + else + { + auto* defaultCall = visitor->getASTBuilder()->create<DefaultConstructExpr>(); + defaultCall->type = QualType(varDecl->type); + return defaultCall; + } + } void SemanticsDeclBodyVisitor::checkVarDeclCommon(VarDeclBase* varDecl) { + // if zero initialize is true, set everything to a default + if (getOptionSet().hasOption(CompilerOptionName::ZeroInitialize) + && !varDecl->initExpr + && as<VarDecl>(varDecl) + ) + { + varDecl->initExpr = constructDefaultInitExprForVar(this, varDecl); + } + if (auto initExpr = varDecl->initExpr) { // Disable the short-circuiting for static const variable init expression @@ -4120,8 +4158,9 @@ namespace Slang } } + bool isDefaultInitializableType = requiredMemberDeclRef.getParent() == getASTBuilder()->getDefaultInitializableTypeInterfaceDecl(); bool isInWrapperType = isWrapperTypeDecl(context->parentDecl); - if (!isInWrapperType) + if (!isInWrapperType && !isDefaultInitializableType) { return false; } @@ -4136,6 +4175,10 @@ namespace Slang auto ctorName = getName("$init"); ctorDecl->nameAndLoc.name = ctorName; ctorDecl->nameAndLoc.loc = ctorDecl->loc; + + auto seqStmt = m_astBuilder->create<SeqStmt>(); + ctorDecl->body = seqStmt; + ctorDecl->returnType.type = DeclRefType::create(m_astBuilder, makeDeclRef(context->parentDecl)); List<Expr*> synArgs; addRequiredParamsToSynthesizedDecl(requiredMemberDeclRef, ctorDecl, synArgs); @@ -4143,52 +4186,55 @@ namespace Slang ThisExpr* synThis = nullptr; addModifiersToSynthesizedDecl(context, requiredMemberDeclRef, ctorDecl, synThis); - auto seqStmt = m_astBuilder->create<SeqStmt>(); - ctorDecl->body = seqStmt; - ctorDecl->returnType.type = DeclRefType::create(m_astBuilder, makeDeclRef(context->parentDecl)); - SemanticsDeclBodyVisitor bodyVisitor(withParentFunc(ctorDecl)); - bodyVisitor.maybeRegisterDifferentiableType(m_astBuilder, context->conformingType); - - for (auto member : context->parentDecl->members) + if (isInWrapperType) { - if (auto varDecl = as<VarDeclBase>(member)) + SemanticsDeclBodyVisitor bodyVisitor(withParentFunc(ctorDecl)); + bodyVisitor.maybeRegisterDifferentiableType(m_astBuilder, context->conformingType); + + for (auto member : context->parentDecl->members) { - auto varExpr = m_astBuilder->create<VarExpr>(); - varExpr->scope = ctorDecl->ownedScope; - varExpr->name = varDecl->getName(); - auto checkedVarExpr = CheckTerm(varExpr); - if (!checkedVarExpr) - return false; - if (as<ErrorType>(checkedVarExpr->type.type)) - return false; - auto assign = m_astBuilder->create<AssignExpr>(); - assign->left = checkedVarExpr; - auto temp = m_astBuilder->create<InvokeExpr>(); - auto lookupResult = lookUpMember( - m_astBuilder, - this, - ctorName, - varDecl->type.type, - ctorDecl->ownedScope, - LookupMask::Function, - LookupOptions::IgnoreBaseInterfaces); - temp->functionExpr = createLookupResultExpr(ctorName, lookupResult, nullptr, context->parentDecl->loc, nullptr); - temp->arguments.addRange(synArgs); - auto resolvedVar = ResolveInvoke(temp); - if (!resolvedVar) - return false; - assign->right = resolvedVar; - assign->type = m_astBuilder->getVoidType(); - bodyVisitor.maybeRegisterDifferentiableType(m_astBuilder, varDecl->type.type); + if (auto varDecl = as<VarDeclBase>(member)) + { + auto varExpr = m_astBuilder->create<VarExpr>(); + varExpr->scope = ctorDecl->ownedScope; + varExpr->name = varDecl->getName(); + auto checkedVarExpr = CheckTerm(varExpr); + if (!checkedVarExpr) + return false; + if (as<ErrorType>(checkedVarExpr->type.type)) + return false; + auto assign = m_astBuilder->create<AssignExpr>(); + assign->left = checkedVarExpr; + auto temp = m_astBuilder->create<InvokeExpr>(); + auto lookupResult = lookUpMember( + m_astBuilder, + this, + ctorName, + varDecl->type.type, + ctorDecl->ownedScope, + LookupMask::Function, + LookupOptions::IgnoreBaseInterfaces); + temp->functionExpr = createLookupResultExpr(ctorName, lookupResult, nullptr, context->parentDecl->loc, nullptr); + temp->arguments.addRange(synArgs); + auto resolvedVar = ResolveInvoke(temp); + if (!resolvedVar) + return false; + assign->right = resolvedVar; + assign->type = m_astBuilder->getVoidType(); + bodyVisitor.maybeRegisterDifferentiableType(m_astBuilder, varDecl->type.type); - auto stmt = m_astBuilder->create<ExpressionStmt>(); - stmt->expression = assign; - seqStmt->stmts.add(stmt); - break; + auto stmt = m_astBuilder->create<ExpressionStmt>(); + stmt->expression = assign; + seqStmt->stmts.add(stmt); + break; + } } } - - _addMethodWitness(witnessTable, requiredMemberDeclRef, makeDeclRef(ctorDecl)); + if (isDefaultInitializableType) + context->parentDecl->addMember(ctorDecl); + else + _addMethodWitness(witnessTable, requiredMemberDeclRef, makeDeclRef(ctorDecl)); + return true; } @@ -6057,6 +6103,21 @@ namespace Slang continue; } + if (this->getOptionSet().getBoolOption(CompilerOptionName::ZeroInitialize) && !isFromStdLib(decl)) + { + // Force add IDefaultInitializableType to any struct missing (transitively) `IDefaultInitializableType`. + auto* defaultInitializableType = m_astBuilder->getDefaultInitializableType(); + if(!isSubtype(DeclRefType::create(m_astBuilder, decl), defaultInitializableType, IsSubTypeOptions::NotReadyForLookup)) + { + InheritanceDecl* conformanceDecl = m_astBuilder->create<InheritanceDecl>(); + conformanceDecl->parentDecl = decl; + conformanceDecl->loc = decl->loc; + conformanceDecl->base.type = defaultInitializableType; + conformanceDecl->nameAndLoc.name = getName("$inheritance"); + decl->members.add(conformanceDecl); + } + } + // TODO: At this point we have the `baseDeclRef` // and could use it to perform further validity checks, // and/or to build up a more refined representation of @@ -7491,6 +7552,14 @@ namespace Slang // expression. This would be a senario we need to // put the `ExpressionStmt` inside a `SeqStmt`. auto stmt = as<BlockStmt>(decl->body); + if (!stmt) + { + auto tmpExpr = decl->body; + auto blockStmt = m_astBuilder->create<BlockStmt>(); + blockStmt->body = tmpExpr; + decl->body = blockStmt; + stmt = blockStmt; + } if (!as<SeqStmt>(stmt->body)) { auto tmpExpr = stmt->body; @@ -7542,6 +7611,21 @@ namespace Slang } DeclAndCtorInfo structDeclInfo = DeclAndCtorInfo(m_astBuilder, this, structDecl, false); + // ensure all varDecl members are processed up to SemanticsBodyVisitor so we can be sure that if init expressions + // of members are to be synthisised, they are. + bool isDefaultInitializableType = isSubtype(DeclRefType::create(m_astBuilder, structDecl), m_astBuilder->getDefaultInitializableType(), IsSubTypeOptions::None); + for (auto m : structDecl->members) + { + auto varDeclBase = as<VarDeclBase>(m); + if (!varDeclBase) + continue; + ensureDecl(m->getDefaultDeclRef(), DeclCheckState::DefaultConstructorReadyForUse); + if (!isDefaultInitializableType + || varDeclBase->initExpr) + continue; + varDeclBase->initExpr = constructDefaultInitExprForVar(this, varDeclBase); + } + Index insertOffset = 0; Dictionary<Decl*, Expr*> cachedDeclToCheckedVar; for (auto ctor : structDeclInfo.ctorList) @@ -7596,8 +7680,11 @@ namespace Slang for (auto& m : structDecl->members) { auto varDeclBase = as<VarDeclBase>(m); + + // Static variables are initialized at start of runtime, not inside a constructor if (!varDeclBase - || !varDeclBase->initExpr) + || !varDeclBase->initExpr + || varDeclBase->hasModifier<HLSLStaticModifier>()) continue; MemberExpr* memberExpr = m_astBuilder->create<MemberExpr>(); diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp index f7ad5bdbf..d0b82c6bd 100644 --- a/source/slang/slang-check-expr.cpp +++ b/source/slang/slang-check-expr.cpp @@ -2733,7 +2733,7 @@ namespace Slang // Get a reference to the builtin 'IDifferentiable' interface auto differentiableInterface = getASTBuilder()->getDifferentiableInterfaceType(); - auto conformanceWitness = as<Witness>(isSubtype(primalType, differentiableInterface)); + auto conformanceWitness = as<Witness>(isSubtype(primalType, differentiableInterface, IsSubTypeOptions::None)); // Check if the provided type inherits from IDifferentiable. // If not, return the original type. if (conformanceWitness) @@ -3116,6 +3116,12 @@ namespace Slang return expr; } + Expr* SemanticsExprVisitor::visitDefaultConstructExpr(DefaultConstructExpr* expr) + { + return expr; + } + + static bool _isSizeOfType(Type* type) { if (!type) @@ -3337,7 +3343,7 @@ namespace Slang valueType = typeType->getType(); // If value is a subtype of `type`, then this expr is always true. - if(isSubtype(valueType, expr->typeExpr.type)) + if(isSubtype(valueType, expr->typeExpr.type, IsSubTypeOptions::None)) { // Instead of returning a BoolLiteralExpr, we use a field to indicate this scenario, // so that the language server can still see the original syntax tree. diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h index d7a4827fb..232cb623c 100644 --- a/source/slang/slang-check-impl.h +++ b/source/slang/slang-check-impl.h @@ -19,6 +19,13 @@ namespace Slang return sink->diagnose(pos, info, args...); } + enum class IsSubTypeOptions + { + None = 0, + /// Type may not be finished 'DeclCheckState::ReadyForLookup` + NotReadyForLookup = 1 << 0, + }; + /// Should the given `decl` be treated as a static rather than instance declaration? bool isEffectivelyStatic( Decl* decl); @@ -2105,9 +2112,15 @@ namespace Slang /// SubtypeWitness* isSubtype( Type* subType, - Type* superType); + Type* superType, + IsSubTypeOptions isSubTypeOptions + ); - SubtypeWitness* checkAndConstructSubtypeWitness(Type* subType, Type* superType); + SubtypeWitness* checkAndConstructSubtypeWitness( + Type* subType, + Type* superType, + IsSubTypeOptions isSubTypeOptions + ); bool isValidGenericConstraintType(Type* type); @@ -2127,7 +2140,7 @@ namespace Slang Type* subType, Type* superType) { - return isSubtype(subType, superType); + return isSubtype(subType, superType, IsSubTypeOptions::None); } /// Check whether `type` conforms to `interfaceDeclRef`, @@ -2734,6 +2747,8 @@ namespace Slang Expr* visitGetArrayLengthExpr(GetArrayLengthExpr* expr); + Expr* visitDefaultConstructExpr(DefaultConstructExpr* expr); + Expr* visitSPIRVAsmExpr(SPIRVAsmExpr*); /// Perform semantic checking on a `modifier` that is being applied to the given `type` diff --git a/source/slang/slang-check-inheritance.cpp b/source/slang/slang-check-inheritance.cpp index f1dbaef5c..7320d0463 100644 --- a/source/slang/slang-check-inheritance.cpp +++ b/source/slang/slang-check-inheritance.cpp @@ -417,7 +417,7 @@ namespace Slang break; auto rightBaseType = rightBase->origin.type; - if (visitor.isSubtype(leftBaseType, rightBaseType)) + if (visitor.isSubtype(leftBaseType, rightBaseType, IsSubTypeOptions::None)) { // If a type earlier in the list of bases is a subtype of // one later in the list, then the ordering is consistent @@ -427,7 +427,7 @@ namespace Slang // // TODO: decide whether to diagnose this case. } - else if (visitor.isSubtype(rightBaseType, leftBaseType)) + else if (visitor.isSubtype(rightBaseType, leftBaseType, IsSubTypeOptions::None)) { // If a type later in the list is a subtype of a type earlier // in the list, then the declared list of bases is inconsistent diff --git a/source/slang/slang-check-overload.cpp b/source/slang/slang-check-overload.cpp index 367c200cc..d37c6e469 100644 --- a/source/slang/slang-check-overload.cpp +++ b/source/slang/slang-check-overload.cpp @@ -711,7 +711,7 @@ namespace Slang { if(context.mode != OverloadResolveContext::Mode::JustTrying) { - subTypeWitness = isSubtype(sub, sup); + subTypeWitness = isSubtype(sub, sup, IsSubTypeOptions::None); getSink()->diagnose(context.loc, Diagnostics::typeArgumentDoesNotConformToInterface, sub, sup); } return false; @@ -1024,9 +1024,9 @@ namespace Slang if (!leftType->equals(rightType)) { - if (isSubtype(leftType, rightType)) + if (isSubtype(leftType, rightType, IsSubTypeOptions::None)) return -1; - if (isSubtype(rightType, leftType)) + if (isSubtype(rightType, leftType, IsSubTypeOptions::None)) return 1; } } diff --git a/source/slang/slang-check-shader.cpp b/source/slang/slang-check-shader.cpp index 43f8b55e8..2ebc9d3a4 100644 --- a/source/slang/slang-check-shader.cpp +++ b/source/slang/slang-check-shader.cpp @@ -1079,7 +1079,7 @@ namespace Slang auto interfaceType = getSup(getLinkage()->getASTBuilder(), DeclRef<GenericTypeConstraintDecl>(constraintDecl)); // Use our semantic-checking logic to search for a witness to the required conformance - auto witness = visitor.isSubtype(argType, interfaceType); + auto witness = visitor.isSubtype(argType, interfaceType, IsSubTypeOptions::None); if(!witness) { // If no witness was found, then we will be unable to satisfy @@ -1111,7 +1111,7 @@ namespace Slang argType = getLinkage()->getASTBuilder()->getErrorType(); } - auto witness = visitor.isSubtype(argType, interfaceType); + auto witness = visitor.isSubtype(argType, interfaceType, IsSubTypeOptions::None); if (!witness) { // If no witness was found, then we will be unable to satisfy @@ -1262,7 +1262,7 @@ namespace Slang } auto sup = getSup(astBuilder, constraintDeclRef); - auto subTypeWitness = visitor.isSubtype(sub, sup); + auto subTypeWitness = visitor.isSubtype(sub, sup, IsSubTypeOptions::None); if(subTypeWitness) { genericArgs.add(subTypeWitness); @@ -1302,7 +1302,7 @@ namespace Slang auto paramType = as<Type>(param.object); auto argType = as<Type>(specializationArg.val); - auto witness = visitor.isSubtype(argType, paramType); + auto witness = visitor.isSubtype(argType, paramType, IsSubTypeOptions::None); if (!witness) { // If no witness was found, then we will be unable to satisfy @@ -1460,7 +1460,7 @@ namespace Slang ExpandedSpecializationArg arg; arg.val = argType; - arg.witness = visitor.isSubtype(argType, paramType); + arg.witness = visitor.isSubtype(argType, paramType, IsSubTypeOptions::None); specializationArgs.add(arg); } diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index bfc4c444d..4a6701acc 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -4444,6 +4444,11 @@ struct ExprLoweringVisitorBase : public ExprVisitor<Derived, LoweredValInfo> return LoweredValInfo::simple(getBuilder()->emitDefaultConstruct(irType)); } + LoweredValInfo visitDefaultConstructExpr(DefaultConstructExpr* expr) + { + return LoweredValInfo::simple(getBuilder()->emitDefaultConstruct(lowerType(context, expr->type))); + } + LoweredValInfo getDefaultVal(DeclRef<VarDeclBase> decl) { if(auto initExpr = decl.getDecl()->initExpr) diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp index 04d48c85f..bcf9256bb 100644 --- a/source/slang/slang-options.cpp +++ b/source/slang/slang-options.cpp @@ -289,6 +289,10 @@ void initCommandOptions(CommandOptions& options) { OptionKind::Language, "-lang", "-lang <language>", "Set the language for the following input files."}, { OptionKind::MatrixLayoutColumn, "-matrix-layout-column-major", nullptr, "Set the default matrix layout to column-major."}, { OptionKind::MatrixLayoutRow,"-matrix-layout-row-major", nullptr, "Set the default matrix layout to row-major."}, + { OptionKind::ZeroInitialize, "-zero-initialize", nullptr, + "Initialize all variables to zero." + "Structs will set all struct-fields without an init expression to 0." + "All variables will call their default constructor if not explicitly initialized as usual."}, { OptionKind::IgnoreCapabilities,"-ignore-capabilities", nullptr, "Do not warn or error if capabilities are violated"}, { OptionKind::MinimumSlangOptimization, "-minimum-slang-optimization", nullptr, "Perform minimum code optimization in Slang to favor compilation time."}, { OptionKind::DisableNonEssentialValidations, "-disable-non-essential-validations", nullptr, "Disable non-essential IR validations such as use of uninitialized variables."}, @@ -1693,6 +1697,7 @@ SlangResult OptionsParser::_parse( case OptionKind::VulkanUseEntryPointName: case OptionKind::VulkanUseGLLayout: case OptionKind::VulkanEmitReflection: + case OptionKind::ZeroInitialize: case OptionKind::IgnoreCapabilities: case OptionKind::MinimumSlangOptimization: case OptionKind::DisableNonEssentialValidations: diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index ede22ec4f..10dc8f57c 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -1515,7 +1515,7 @@ SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::createTypeConformanceComponentTy SharedSemanticsContext sharedSemanticsContext(this, nullptr, &sink); SemanticsVisitor visitor(&sharedSemanticsContext); auto witness = - visitor.isSubtype((Slang::Type*)type, (Slang::Type*)interfaceType); + visitor.isSubtype((Slang::Type*)type, (Slang::Type*)interfaceType, IsSubTypeOptions::None); if (auto subtypeWitness = as<SubtypeWitness>(witness)) { result = new TypeConformance(this, subtypeWitness, conformanceIdOverride, &sink); diff --git a/tests/language-feature/struct-field-initializers/struct-field-initializer-static.slang b/tests/language-feature/struct-field-initializers/struct-field-initializer-static.slang index 7f0b0577f..b2ed006be 100644 --- a/tests/language-feature/struct-field-initializers/struct-field-initializer-static.slang +++ b/tests/language-feature/struct-field-initializers/struct-field-initializer-static.slang @@ -16,11 +16,13 @@ struct DefaultStructNoInit_base struct DefaultStructNoInit : DefaultStructNoInit_base { int data3 = 2; + static int data4 = 2; }; [numthreads(1, 1, 1)] void computeMain(uint3 dispatchThreadID: SV_DispatchThreadID) { + DefaultStructNoInit::data4 = 0; DefaultStructNoInit noInit; // BUF: 1 outputBuffer[0] = true @@ -28,5 +30,6 @@ void computeMain(uint3 dispatchThreadID: SV_DispatchThreadID) && noInit.data1 == 2 && noInit.data2 == 2 && noInit.data3 == 2 + && noInit.data4 == 0; ; }
\ No newline at end of file diff --git a/tests/language-feature/zero-initialize/IDefaultExplicit-wrapper-type.slang b/tests/language-feature/zero-initialize/IDefaultExplicit-wrapper-type.slang new file mode 100644 index 000000000..fd08b5bee --- /dev/null +++ b/tests/language-feature/zero-initialize/IDefaultExplicit-wrapper-type.slang @@ -0,0 +1,52 @@ +//TEST:SIMPLE(filecheck=CHECK): -target glsl -stage compute -entry computeMain +// CHECK: {{.* }}= 0; +// CHECK-NOT: {{.* }}= 0; + +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -entry computeMain -allow-glsl +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -entry computeMain -emit-spirv-directly -allow-glsl +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-cpu -compute -entry computeMain -allow-glsl +//TEST(smoke,compute):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-dx12 -use-dxil -compute -entry computeMain -allow-glsl -profile sm_6_2 -xslang -DDX12 + +//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer; + +struct Base : IDefaultInitializableType +{ + int data1; +}; + +struct idefault1_base : IDefaultInitializableType +{ +}; +interface idefault2_base : IDefaultInitializableType +{ +}; + +struct idefault1 : idefault1_base = Base; + +struct idefault2 : idefault2_base = Base; + +idefault1 getDefault1() +{ + idefault1 default1; + return default1; +} +idefault2 getDefault2() +{ + idefault2 default2; + return default2; +} + +[numthreads(1, 1, 1)] +void computeMain(int3 dispatchThreadID: SV_DispatchThreadID) +{ + // BUF: 1 + idefault1 default1 = getDefault1(); + idefault2 default2 = getDefault2(); + + outputBuffer[0] = true + && default1.inner.data1 == 0 + + && default2.inner.data1 == 0 + ; +} diff --git a/tests/language-feature/zero-initialize/IDefaultExplicit.slang b/tests/language-feature/zero-initialize/IDefaultExplicit.slang new file mode 100644 index 000000000..78c6b1596 --- /dev/null +++ b/tests/language-feature/zero-initialize/IDefaultExplicit.slang @@ -0,0 +1,103 @@ +//TEST:SIMPLE(filecheck=CHECK): -target glsl -stage compute -entry computeMain +// CHECK-COUNT-6: {{.* }}= 0U; + +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -entry computeMain -allow-glsl +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -entry computeMain -emit-spirv-directly -allow-glsl +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-cpu -compute -entry computeMain -allow-glsl +//TEST(smoke,compute):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-dx12 -use-dxil -compute -entry computeMain -allow-glsl -profile sm_6_2 -xslang -DDX12 + +//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer; + +struct idefault1 : IDefaultInitializableType +{ + uint data; +}; + +struct idefault2_base : IDefaultInitializableType +{ + uint data1; +}; +struct idefault2 : idefault2_base +{ + uint data2 = 1; +}; + +interface idefault3_base : IDefaultInitializableType +{ +}; +struct idefault3 : idefault3_base +{ + uint data; +}; + +struct idefault4 +{ + uint data; +}; + +extension idefault4 : IDefaultInitializableType +{ +} + +struct idefault5_base : IDefaultInitializableType +{ + uint data1; +}; + +struct idefault5 : idefault5_base +{ + uint data2; +}; + +idefault1 getDefault1() +{ + idefault1 default1; + return default1; +} +idefault2 getDefault2() +{ + idefault2 default2; + return default2; +} +idefault3 getDefault3() +{ + idefault3 default3; + return default3; +} +idefault4 getDefault4() +{ + idefault4 default4; + return default4; +} + +idefault5 getDefault5() +{ + idefault5 default5; + return default5; +} + +[numthreads(1, 1, 1)] +void computeMain(int3 dispatchThreadID: SV_DispatchThreadID) +{ + // BUF: 1 + idefault1 default1 = getDefault1(); + idefault2 default2 = getDefault2(); + idefault3 default3 = getDefault3(); + idefault4 default4 = getDefault4(); + idefault5 default5 = getDefault5(); + + outputBuffer[0] = true + && default1.data == 0 + + && default2.data1 == 0 + && default2.data2 == 1 + + && default3.data == 0 + + && default4.data == 0 + + && default5.data1 == 0 + && default5.data2 == 0 + ; +} diff --git a/tests/language-feature/zero-initialize/IDefaultExplicitGenerics.slang b/tests/language-feature/zero-initialize/IDefaultExplicitGenerics.slang new file mode 100644 index 000000000..edb438c1c --- /dev/null +++ b/tests/language-feature/zero-initialize/IDefaultExplicitGenerics.slang @@ -0,0 +1,30 @@ +//TEST:SIMPLE(filecheck=CHECK): -target glsl -stage compute -entry computeMain +// CHECK: vec4(0.0 + +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -entry computeMain -allow-glsl +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -entry computeMain -emit-spirv-directly -allow-glsl +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-cpu -compute -entry computeMain -allow-glsl +//TEST(smoke,compute):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-dx12 -use-dxil -compute -entry computeMain -allow-glsl -profile sm_6_2 + +//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer; + +__generic<T> +struct idefault1 : IDefaultInitializableType +{ + vector<T,4> data; +}; + +[numthreads(1, 1, 1)] +void computeMain(int3 dispatchThreadID: SV_DispatchThreadID) +{ + // BUF: 1 + idefault1<float> default1; + + outputBuffer[0] = true + && default1.data[0] == 0 + && default1.data[1] == 0 + && default1.data[2] == 0 + && default1.data[3] == 0 + ; +} diff --git a/tests/language-feature/zero-initialize/generic.slang b/tests/language-feature/zero-initialize/generic.slang new file mode 100644 index 000000000..25f52238c --- /dev/null +++ b/tests/language-feature/zero-initialize/generic.slang @@ -0,0 +1,113 @@ +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -entry computeMain -allow-glsl -xslang -zero-initialize +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -entry computeMain -emit-spirv-directly -allow-glsl -xslang -zero-initialize +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-cpu -compute -entry computeMain -allow-glsl -xslang -zero-initialize +//TEST(smoke,compute):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-dx12 -use-dxil -compute -entry computeMain -allow-glsl -profile sm_6_2 -xslang -zero-initialize -xslang -DDX12 + +//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer; + +__generic<T : __BuiltinArithmeticType> +bool getValue() +{ + T genericOut; + return genericOut == T(0); +} +bool getValueBoolean() +{ + bool genericOut; + return genericOut == bool(0); +} + +__generic<T : __BuiltinArithmeticType, let N : int> +bool getValueVector() +{ + typealias gvec = vector<T, N>; + + gvec genericOut; + for (int i = 0; i < N; ++i) + if (genericOut[i] != T(0)) + return false; + return true; +} +__generic<let N : int> +bool getValueVectorBoolean() +{ + typealias gvec = vector<bool, N>; + + gvec genericOut; + for (int i = 0; i < N; ++i) + if (genericOut[i] != bool(0)) + return false; + return true; +} + +[numthreads(1, 1, 1)] +void computeMain(int3 dispatchThreadID: SV_DispatchThreadID) +{ +// BUF: 1 + outputBuffer[0] = true + && getValue<int>() +#ifndef DX12 + && getValue<uint8_t>() + && getValue<int8_t>() +#endif + && getValue<uint16_t>() + && getValue<int16_t>() + && getValue<uint32_t>() + && getValue<int32_t>() + && getValue<uint64_t>() + && getValue<int64_t>() + && getValue<half>() + && getValue<float>() + && getValue<double>() + && getValueBoolean() + + && getValueVector<int, 2>() +#ifndef DX12 + && getValueVector<uint8_t, 2>() + && getValueVector<int8_t, 2>() +#endif + && getValueVector<uint16_t, 2>() + && getValueVector<int16_t, 2>() + && getValueVector<uint32_t, 2>() + && getValueVector<int32_t, 2>() + && getValueVector<uint64_t, 2>() + && getValueVector<int64_t, 2>() + && getValueVector<half, 2>() + && getValueVector<float, 2>() + && getValueVector<double, 2>() + && getValueVectorBoolean<2>() + + && getValueVector<int, 3>() +#ifndef DX12 + && getValueVector<uint8_t, 3>() + && getValueVector<int8_t, 3>() +#endif + && getValueVector<uint16_t, 3>() + && getValueVector<int16_t, 3>() + && getValueVector<uint32_t, 3>() + && getValueVector<int32_t, 3>() + && getValueVector<uint64_t, 3>() + && getValueVector<int64_t, 3>() + && getValueVector<half, 3>() + && getValueVector<float, 3>() + && getValueVector<double, 3>() + && getValueVectorBoolean<3>() + + && getValueVector<int, 4>() +#ifndef DX12 + && getValueVector<uint8_t, 4>() + && getValueVector<int8_t, 4>() +#endif + && getValueVector<uint16_t, 4>() + && getValueVector<int16_t, 4>() + && getValueVector<uint32_t, 4>() + && getValueVector<int32_t, 4>() + && getValueVector<uint64_t, 4>() + && getValueVector<int64_t, 4>() + && getValueVector<half, 4>() + && getValueVector<float, 4>() + && getValueVector<double, 4>() + && getValueVectorBoolean<4>() + ; +} diff --git a/tests/language-feature/zero-initialize/missing-zero-init.slang b/tests/language-feature/zero-initialize/missing-zero-init.slang new file mode 100644 index 000000000..f9d715e10 --- /dev/null +++ b/tests/language-feature/zero-initialize/missing-zero-init.slang @@ -0,0 +1,23 @@ +//TEST:SIMPLE(filecheck=CHECK): -target hlsl -stage compute -entry computeMain +//TEST:SIMPLE(filecheck=CHECK): -target glsl -stage compute -entry computeMain + +// CHECK-NOT: {{.* }}= 0; +// CHECK-NOT: return 0; + +RWStructuredBuffer<int> outputBuffer; + +[noinline] +int returnInt() +{ + int myInt; + return myInt; +} + +[numthreads(1, 1, 1)] +void computeMain(int3 dispatchThreadID: SV_DispatchThreadID) +{ +// BUF: 1 + outputBuffer[0] = true + && returnInt() == 0 + ; +} diff --git a/tests/language-feature/zero-initialize/regular.slang b/tests/language-feature/zero-initialize/regular.slang new file mode 100644 index 000000000..5627762c3 --- /dev/null +++ b/tests/language-feature/zero-initialize/regular.slang @@ -0,0 +1,116 @@ +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -entry computeMain -allow-glsl -xslang -zero-initialize +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -entry computeMain -emit-spirv-directly -allow-glsl -xslang -zero-initialize +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-cpu -compute -entry computeMain -allow-glsl -xslang -zero-initialize +//TEST(smoke,compute):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-dx12 -use-dxil -compute -entry computeMain -allow-glsl -profile sm_6_2 -xslang -zero-initialize -xslang -DDX12 + +//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer; + +enum MyFlags +{ + Zero = 0, + One, +} +MyFlags getMyFlagsUint_One() +{ + return MyFlags::One; +} + +#ifndef DX12 +enum MyFlags_uint8 : uint8_t +{ + Zero = 0, + One, +} +MyFlags_uint8 getMyFlagsUint8_One() +{ + return MyFlags_uint8::One; +} +#endif + +enum MyFlags_uint16 : uint16_t +{ + Zero = 0, + One, +} +MyFlags_uint16 getMyFlagsUint16_One() +{ + return MyFlags_uint16::One; +} + +enum MyFlags_uint32 : uint32_t +{ + Zero = 0, + One, +} +MyFlags_uint32 getMyFlagsUint32_One() +{ + return MyFlags_uint32::One; +} + +enum MyFlags_uint64 : uint64_t +{ + Zero = 0, + One, +} +MyFlags_uint64 getMyFlagsUint64_One() +{ + return MyFlags_uint64::One; +} + +int getDefaultInt() +{ + int val; + return val; +} +float getDefaultFloat() +{ + float val; + return val; +} +double getDefaultDouble() +{ + double val; + return val; +} +vector<int, 2> getDefaultIntVec2() +{ + vector<int, 2> val; + return val; +} +vector<int, 3> getDefaultIntVec3() +{ + vector<int, 3> val; + return val; +} +vector<int, 4> getDefaultIntVec4() +{ + vector<int, 4> val; + return val; +} +[numthreads(1, 1, 1)] +void computeMain(int3 dispatchThreadID: SV_DispatchThreadID) +{ + int defaultInt = getDefaultInt(); + float defaultFloat = getDefaultFloat(); + double defaultDouble = getDefaultDouble(); + vector<int, 2> vector2 = getDefaultIntVec2(); + vector<int, 3> vector3 = getDefaultIntVec3(); + vector<int, 4> vector4 = getDefaultIntVec4(); +// BUF: 1 + outputBuffer[0] = true + && defaultInt == 0 + && defaultFloat == 0 + && defaultDouble == 0 + && vector2 == vector<int, 2>(0) + && vector3 == vector<int, 3>(0) + && vector4 == vector<int, 4>(0) + && MyFlags::One == getMyFlagsUint_One() +#ifndef DX12 + && MyFlags_uint8::One == getMyFlagsUint8_One() +#endif + && MyFlags_uint16::One == getMyFlagsUint16_One() + && MyFlags_uint32::One == getMyFlagsUint32_One() + && MyFlags_uint64::One == getMyFlagsUint64_One(); + ; +} diff --git a/tests/language-feature/zero-initialize/static-struct-field-init-list.slang b/tests/language-feature/zero-initialize/static-struct-field-init-list.slang new file mode 100644 index 000000000..e40ca7e56 --- /dev/null +++ b/tests/language-feature/zero-initialize/static-struct-field-init-list.slang @@ -0,0 +1,24 @@ +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -entry computeMain -allow-glsl -xslang -zero-initialize + +//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer +RWStructuredBuffer<uint> outputBuffer; + +struct structData +{ + uint data; +}; + +struct structOuterData +{ + static structData data1 = { 1 }; +}; + +[numthreads(1,1,1)] +void computeMain() +{ + // BUF: 1 + structOuterData mat; + outputBuffer[0] = true + && mat.data1.data == 1 + ; +}
\ No newline at end of file diff --git a/tests/language-feature/zero-initialize/static-struct-field-init.slang b/tests/language-feature/zero-initialize/static-struct-field-init.slang new file mode 100644 index 000000000..ad2db5027 --- /dev/null +++ b/tests/language-feature/zero-initialize/static-struct-field-init.slang @@ -0,0 +1,25 @@ +//TEST:SIMPLE(filecheck=CHECK): -target glsl -stage compute -entry computeMain -zero-initialize +// CHECK: {{.* }}= 0U; + +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -entry computeMain -allow-glsl -xslang -zero-initialize + +//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer +RWStructuredBuffer<uint> outputBuffer; + +struct structData +{ + uint data; +}; + +struct structOuterData +{ + static structData data; +}; + +[numthreads(1,1,1)] +void computeMain() +{ + // BUF: 1 + structOuterData mat; + outputBuffer[0] = mat.data.data == 0; +}
\ No newline at end of file diff --git a/tests/language-feature/zero-initialize/struct-array-some-member-missing-init.slang b/tests/language-feature/zero-initialize/struct-array-some-member-missing-init.slang new file mode 100644 index 000000000..cdd3f703f --- /dev/null +++ b/tests/language-feature/zero-initialize/struct-array-some-member-missing-init.slang @@ -0,0 +1,42 @@ +//TEST:SIMPLE(filecheck=CHECK): -target hlsl -stage compute -entry computeMain -xslang -zero-initialize +//TEST:SIMPLE(filecheck=CHECK): -target glsl -stage compute -entry computeMain -xslang -zero-initialize + +// CHECK-NOT: {{.* }}= 0; + +RWStructuredBuffer<int> outputBuffer; + +struct MyStruct_base +{ + int a = 1; + int b; +}; +struct MyStruct : MyStruct_base +{ + int c = 1; + int d; +}; + +Array<MyStruct,2> getStructs() +{ + Array<MyStruct, 2> outData; + return outData; +} + +[numthreads(1, 1, 1)] +void computeMain(int3 dispatchThreadID: SV_DispatchThreadID) +{ + Array<MyStruct,2> myStruct = getStructs(); + +// BUF: 1 + outputBuffer[0] = true + && myStruct[0].a == 1 + && myStruct[0].b == 0 + && myStruct[0].c == 1 + && myStruct[0].d == 0 + + && myStruct[1].a == 1 + && myStruct[1].b == 0 + && myStruct[1].c == 1 + && myStruct[1].d == 0 + ; +} diff --git a/tests/language-feature/zero-initialize/struct-array.slang b/tests/language-feature/zero-initialize/struct-array.slang new file mode 100644 index 000000000..1f1b8eede --- /dev/null +++ b/tests/language-feature/zero-initialize/struct-array.slang @@ -0,0 +1,38 @@ +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -entry computeMain -allow-glsl -xslang -zero-initialize +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -entry computeMain -emit-spirv-directly -allow-glsl -xslang -zero-initialize +//TEST(compute):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-cpu -compute -entry computeMain -allow-glsl -xslang -zero-initialize +//TEST(smoke,compute):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-dx12 -use-dxil -compute -entry computeMain -allow-glsl -xslang -zero-initialize + +//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer; + +struct MyStruct_base +{ + int a; +}; +struct MyStruct : MyStruct_base +{ + int b; + int c; +}; +[numthreads(1, 1, 1)] +void computeMain(int3 dispatchThreadID: SV_DispatchThreadID) +{ + + Array<MyStruct, 3> myStructs; + +// BUF: 1 + outputBuffer[0] = true + && myStructs[0].a == 0 + && myStructs[0].b == 0 + && myStructs[0].c == 0 + + && myStructs[1].a == 0 + && myStructs[1].b == 0 + && myStructs[1].c == 0 + + && myStructs[2].a == 0 + && myStructs[2].b == 0 + && myStructs[2].c == 0 + ; +} diff --git a/tests/language-feature/zero-initialize/struct-no-zero-init.slang b/tests/language-feature/zero-initialize/struct-no-zero-init.slang new file mode 100644 index 000000000..3a4a5f945 --- /dev/null +++ b/tests/language-feature/zero-initialize/struct-no-zero-init.slang @@ -0,0 +1,33 @@ +//TEST:SIMPLE(filecheck=CHECK): -target hlsl -stage compute -entry computeMain +//TEST:SIMPLE(filecheck=CHECK): -target glsl -stage compute -entry computeMain + +// CHECK-NOT: {{.* }}= 0; + +RWStructuredBuffer<int> outputBuffer; + +struct MyStruct_base +{ + int a; +}; +struct MyStruct : MyStruct_base +{ + int b; + int c; +}; +MyStruct getStruct() +{ + MyStruct myStruct; + return myStruct; +} +[numthreads(1, 1, 1)] +void computeMain(int3 dispatchThreadID: SV_DispatchThreadID) +{ + MyStruct myStruct = getStruct(); + +// BUF: 1 + outputBuffer[0] = true + && myStruct.a == 0 + && myStruct.b == 0 + && myStruct.c == 0 + ; +} diff --git a/tests/language-feature/zero-initialize/struct-some-member-init-missing-zero-init.slang b/tests/language-feature/zero-initialize/struct-some-member-init-missing-zero-init.slang new file mode 100644 index 000000000..2b584ad00 --- /dev/null +++ b/tests/language-feature/zero-initialize/struct-some-member-init-missing-zero-init.slang @@ -0,0 +1,35 @@ +//TEST:SIMPLE(filecheck=CHECK): -target hlsl -stage compute -entry computeMain +//TEST:SIMPLE(filecheck=CHECK): -target glsl -stage compute -entry computeMain + +// CHECK-NOT: {{.* }}= 0; + +RWStructuredBuffer<int> outputBuffer; + +struct MyStruct_base +{ + int a = 1; + int b; +}; +struct MyStruct : MyStruct_base +{ + int c = 1; + int d; +}; +MyStruct getStruct() +{ + MyStruct myStruct; + return myStruct; +} +[numthreads(1, 1, 1)] +void computeMain(int3 dispatchThreadID: SV_DispatchThreadID) +{ + MyStruct myStruct = getStruct(); + +// BUF: 1 + outputBuffer[0] = true + && myStruct.a == 1 + && myStruct.b == 0 + && myStruct.c == 1 + && myStruct.d == 0 + ; +} diff --git a/tests/language-feature/zero-initialize/struct-some-zero-init.slang b/tests/language-feature/zero-initialize/struct-some-zero-init.slang new file mode 100644 index 000000000..9bbb1f49e --- /dev/null +++ b/tests/language-feature/zero-initialize/struct-some-zero-init.slang @@ -0,0 +1,39 @@ +//TEST:SIMPLE(filecheck=CHECK): -target glsl -stage compute -entry computeMain -zero-initialize +// CHECK-COUNT-2: {{.* }}= 0; + +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -entry computeMain -allow-glsl -xslang -zero-initialize +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -entry computeMain -emit-spirv-directly -allow-glsl -xslang -zero-initialize +//TEST(compute):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-cpu -compute -entry computeMain -allow-glsl -xslang -zero-initialize +//TEST(smoke,compute):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-dx12 -use-dxil -compute -entry computeMain -allow-glsl -xslang -zero-initialize + +//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer; + +struct MyStruct_base +{ + int a = 1; + int b; +}; +struct MyStruct : MyStruct_base +{ + int c = 1; + int d; +}; +MyStruct getStruct() +{ + MyStruct myStruct; + return myStruct; +} +[numthreads(1, 1, 1)] +void computeMain(int3 dispatchThreadID: SV_DispatchThreadID) +{ + MyStruct myStruct = getStruct(); + +// BUF: 1 + outputBuffer[0] = true + && myStruct.a == 1 + && myStruct.b == 0 + && myStruct.c == 1 + && myStruct.d == 0 + ; +} diff --git a/tests/language-feature/zero-initialize/struct.slang b/tests/language-feature/zero-initialize/struct.slang new file mode 100644 index 000000000..62f403903 --- /dev/null +++ b/tests/language-feature/zero-initialize/struct.slang @@ -0,0 +1,37 @@ +//TEST:SIMPLE(filecheck=CHECK): -target glsl -stage compute -entry computeMain -zero-initialize +// CHECK-COUNT-3: {{.* }}= 0; + +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -entry computeMain -allow-glsl -xslang -zero-initialize +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -entry computeMain -emit-spirv-directly -allow-glsl -xslang -zero-initialize +//TEST(compute):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-cpu -compute -entry computeMain -allow-glsl -xslang -zero-initialize +//TEST(smoke,compute):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-dx12 -use-dxil -compute -entry computeMain -allow-glsl -xslang -zero-initialize + +//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer; + +struct MyStruct_base +{ + int a; +}; +struct MyStruct : MyStruct_base +{ + int b; + int c; +}; +MyStruct getStruct() +{ + MyStruct myStruct; + return myStruct; +} +[numthreads(1, 1, 1)] +void computeMain(int3 dispatchThreadID: SV_DispatchThreadID) +{ + MyStruct myStruct = getStruct(); + +// BUF: 1 + outputBuffer[0] = true + && myStruct.a == 0 + && myStruct.b == 0 + && myStruct.c == 0 + ; +} diff --git a/tests/spirv/debug-type-pointer.slang b/tests/spirv/debug-type-pointer.slang index 505675089..ff6d5e6b8 100644 --- a/tests/spirv/debug-type-pointer.slang +++ b/tests/spirv/debug-type-pointer.slang @@ -1,4 +1,5 @@ //TEST(compute, vulkan):SIMPLE(filecheck=SPV): -stage compute -entry computeMain -target spirv -emit-spirv-directly -g2 +//TEST(compute, vulkan):SIMPLE(filecheck=SPV): -stage compute -entry computeMain -target spirv -emit-spirv-directly -g2 -zero-initialize // This test is to check if DebugTypePointer is emitted when "-g2" option is used |
