diff options
| author | ArielG-NV <159081215+ArielG-NV@users.noreply.github.com> | 2024-07-18 11:03:30 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-07-18 11:03:30 -0400 |
| commit | 6f1371a7870a7c371a77cfdee1daa5c32f0c5377 (patch) | |
| tree | 70fa0962fa5f34ea4646a531c3969bbf7eb8d74b | |
| parent | 494efd7254f28ec46aff84bb1c06fe582a743c1a (diff) | |
Adjust how `slang` and `slangc` uses a `profile` to manage the stage of an entry-point (#4670)
* Fixes #4656
Changes:
1. Setting a profile via slangc no-longer sets an entry-point target-stage, this is to allow slangc to follow how the SLANG-API works (else `main` is assumed to be the default entry-point)
2. If the stage specified by a profile is not equal to the stage specified by a entry-point, we throw a capability error.
3. Resolving the stage of an entry point was changed to function (mostly) equally for when 0 entry-points are specified versus to when there are 1 or more.
4. changed capabilitySet Iterator so it is invalid if backing data is nullptr (although this should never happen, it would stop crashes in the worst case).
* remove the breaking change since it likely is going to be a lot more than just a simple change due to the implicit `main` and stage through `profile` code.
* print out profile name with errors
* use target's profile for printing
* change logic to print warning in a different method (account for more cases)
* set unknown stages
| -rw-r--r-- | source/slang/slang-capability.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-check-decl.cpp | 2 | ||||
| -rw-r--r-- | source/slang/slang-check-impl.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-check-shader.cpp | 50 | ||||
| -rw-r--r-- | source/slang/slang-compiler.cpp | 21 | ||||
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 3 | ||||
| -rw-r--r-- | source/slang/slang-options.cpp | 19 | ||||
| -rw-r--r-- | tests/language-feature/capability/conflicting-profile-stage-for-entry-point.slang | 36 |
8 files changed, 91 insertions, 44 deletions
diff --git a/source/slang/slang-capability.h b/source/slang/slang-capability.h index 53164be7f..cbdf48ac6 100644 --- a/source/slang/slang-capability.h +++ b/source/slang/slang-capability.h @@ -207,7 +207,7 @@ public: public: operator bool() const { - return atomSetNode->has_value(); + return (atomSetNode) ? atomSetNode->has_value() : false; } const CapabilityAtomSet& operator*() const { diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index 28d1ae02e..99aecd37c 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -10489,7 +10489,7 @@ namespace Slang auto stageCaps = CapabilitySet(Profile(entryPointAttr->stage).getCapabilityName()); if (declaredCaps.isIncompatibleWith(stageCaps)) { - maybeDiagnose(getSink(), this->getOptionSet(), DiagnosticCategory::Capability, funcDecl->loc, Diagnostics::stageIsInCompatibleWithCapabilityDefinition, funcDecl, stageCaps, declaredCaps); + maybeDiagnose(getSink(), this->getOptionSet(), DiagnosticCategory::Capability, funcDecl->loc, Diagnostics::stageIsIncompatibleWithCapabilityDefinition, funcDecl, stageCaps, declaredCaps); } else { diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h index 39f5f46b3..86655cede 100644 --- a/source/slang/slang-check-impl.h +++ b/source/slang/slang-check-impl.h @@ -2877,4 +2877,6 @@ namespace Slang RefPtr<EntryPoint> findAndValidateEntryPoint( FrontEndEntryPointRequest* entryPointReq); + + bool resolveStageOfProfileWithEntryPoint(Profile& entryPointProfile, CompilerOptionSet& optionSet, const List<RefPtr<TargetRequest>>& targets, FuncDecl* entryPointFuncDecl, DiagnosticSink* sink); } diff --git a/source/slang/slang-check-shader.cpp b/source/slang/slang-check-shader.cpp index 14495dd6e..3d086b189 100644 --- a/source/slang/slang-check-shader.cpp +++ b/source/slang/slang-check-shader.cpp @@ -577,6 +577,30 @@ namespace Slang } } + bool resolveStageOfProfileWithEntryPoint(Profile& entryPointProfile, CompilerOptionSet& optionSet, const List<RefPtr<TargetRequest>>& targets, FuncDecl* entryPointFuncDecl, DiagnosticSink* sink) + { + if (auto entryPointAttr = entryPointFuncDecl->findModifier<EntryPointAttribute>()) + { + auto entryPointProfileStage = entryPointProfile.getStage(); + // Ensure every target is specifying the same stage as an entry` point + // if a profile+stage was set, else user will not be aware that their + // code is requiring `fragment` on a `vertex` shader + for (auto target : targets) + { + auto targetProfile = target->getOptionSet().getProfile(); + auto profileStage = targetProfile.getStage(); + if (profileStage != Stage::Unknown && profileStage != entryPointAttr->stage) + maybeDiagnose(sink, optionSet, DiagnosticCategory::Capability, entryPointAttr, Diagnostics::entryPointAndProfileAreIncompatible, entryPointFuncDecl, entryPointAttr->stage, targetProfile.getName()); + } + if (entryPointProfileStage == Stage::Unknown) + entryPointProfile.setStage(entryPointAttr->stage); + else if (entryPointProfileStage != Stage::Unknown && entryPointProfileStage != entryPointAttr->stage) + maybeDiagnose(sink, optionSet, DiagnosticCategory::Capability, entryPointFuncDecl, Diagnostics::specifiedStageDoesntMatchAttribute, entryPointFuncDecl->getName(), entryPointProfileStage, entryPointAttr->stage); + return true; + } + return false; + } + // Given an entry point specified via API or command line options, // attempt to find a matching AST declaration that implements the specified // entry point. If such a function is found, then validate that it actually @@ -618,27 +642,13 @@ namespace Slang // // If the entry point specifies a stage via a `[shader("...")]` attribute, // then we might be able to infer a stage for the entry point request if - // it didn't have one, *or* issue a diagnostic if there is a mismatch. - // + // it didn't have one, *or* issue a diagnostic if there is a mismatch with the profile. + auto entryPointProfile = entryPointReq->getProfile(); - if( auto entryPointAttribute = entryPointFuncDecl->findModifier<EntryPointAttribute>() ) - { - auto entryPointStage = entryPointProfile.getStage(); - if( entryPointStage == Stage::Unknown ) - { - entryPointProfile.setStage(entryPointAttribute->stage); - } - else if( entryPointAttribute->stage != entryPointStage ) - { - sink->diagnose(entryPointFuncDecl, Diagnostics::specifiedStageDoesntMatchAttribute, entryPointName, entryPointStage, entryPointAttribute->stage); - } - } - else - { - // TODO: Should we attach a `[shader(...)]` attribute to an - // entry point that didn't have one, so that we can have - // a more uniform representation in the AST? - } + resolveStageOfProfileWithEntryPoint(entryPointProfile, linkage->m_optionSet, linkage->targets, entryPointFuncDecl, sink); + // TODO: Should we attach a `[shader(...)]` attribute to an + // entry point that didn't have one, so that we can have + // a more uniform representation in the AST? RefPtr<EntryPoint> entryPoint = EntryPoint::create( linkage, diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp index e61c0f220..912e42572 100644 --- a/source/slang/slang-compiler.cpp +++ b/source/slang/slang-compiler.cpp @@ -11,6 +11,7 @@ #include "../core/slang-castable.h" #include "slang-check.h" +#include "slang-check-impl.h" #include "slang-compiler.h" #include "../compiler-core/slang-lexer.h" @@ -2505,24 +2506,8 @@ namespace Slang continue; Profile profile; - - auto entryPointAttr = funcDecl->findModifier<EntryPointAttribute>(); - if (entryPointAttr) - { - // We've discovered a valid entry point. It is a function (possibly - // generic) that has a `[shader(...)]` attribute to mark it as an - // entry point. - // - // We will now register that entry point as an `EntryPoint` - // with an appropriately chosen profile. - // - // The profile will only include a stage, so that the profile "family" - // and "version" are left unspecified. Downstream code will need - // to be able to handle this case. - // - profile.setStage(entryPointAttr->stage); - } - else + bool resolvedStageOfProfileWithEntryPoint = resolveStageOfProfileWithEntryPoint(profile, getLinkage()->m_optionSet, targets, funcDecl, sink); + if(!resolvedStageOfProfileWithEntryPoint) { // If there isn't a [shader] attribute, look for a [numthreads] attribute // since that implicitly means a compute shader. We'll not do this when compiling for diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index 3e7d71369..be15daae9 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -392,8 +392,9 @@ DIAGNOSTIC(36106, Error, expectCapability, "expect a capability name.") DIAGNOSTIC(36107, Error, entryPointUsesUnavailableCapability, "entrypoint '$0' requires capability '$1', which is incompatible with the current compilation target '$2'.") DIAGNOSTIC(36108, Error, declHasDependenciesNotCompatibleOnTarget, "'$0' has dependencies that are not compatible on the required target '$1'.") DIAGNOSTIC(36109, Error, invalidTargetSwitchCase, "'$0' cannot be used as a target_switch case.") -DIAGNOSTIC(36110, Error, stageIsInCompatibleWithCapabilityDefinition, "'$0' is defined for stage '$1', which is incompatible with the declared capability set '$2'.") +DIAGNOSTIC(36110, Error, stageIsIncompatibleWithCapabilityDefinition, "'$0' is defined for stage '$1', which is incompatible with the declared capability set '$2'.") DIAGNOSTIC(36111, Error, unexpectedCapability, "'$0' resolves into a disallowed `$1` Capability.") +DIAGNOSTIC(36112, Warning, entryPointAndProfileAreIncompatible, "'$0' is defined for stage '$1', which is incompatible with the declared profile '$2'.") // Attributes DIAGNOSTIC(31000, Warning, unknownAttributeName, "unknown attribute '$0'") diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp index 4670af9df..0f5a4b417 100644 --- a/source/slang/slang-options.cpp +++ b/source/slang/slang-options.cpp @@ -696,6 +696,7 @@ struct OptionsParser RawTarget* getCurrentTarget(); void setProfileVersion(RawTarget* rawTarget, ProfileVersion profileVersion); + void setProfile(RawTarget* rawTarget, Profile profile); void addCapabilityAtom(RawTarget* rawTarget, CapabilityName atom); void setFloatingPointMode(RawTarget* rawTarget, FloatingPointMode mode); @@ -1056,6 +1057,20 @@ void OptionsParser::setProfileVersion(RawTarget* rawTarget, ProfileVersion profi rawTarget->optionSet.setProfileVersion(profileVersion); } +void OptionsParser::setProfile(RawTarget* rawTarget, Profile profile) +{ + if (rawTarget->optionSet.getProfile() != Profile::Unknown) + { + rawTarget->redundantProfileSet = true; + + if (profile != rawTarget->optionSet.getProfile()) + { + rawTarget->conflictingProfilesSet = true; + } + } + rawTarget->optionSet.setProfile(profile); +} + void OptionsParser::addCapabilityAtom(RawTarget* rawTarget, CapabilityName atom) { CapabilitySet capSet(atom); @@ -1592,10 +1607,8 @@ SlangResult OptionsParser::_parseProfile(const CommandLineArg& arg) { auto profile = Profile(profileID); - setProfileVersion(getCurrentTarget(), profile.getVersion()); + setProfile(this->getCurrentTarget(), profile); - // A `-profile` option that also specifies a stage (e.g., `-profile vs_5_0`) - // should be treated like a composite (e.g., `-profile sm_5_0 -stage vertex`) auto stage = profile.getStage(); if (stage != Stage::Unknown) { diff --git a/tests/language-feature/capability/conflicting-profile-stage-for-entry-point.slang b/tests/language-feature/capability/conflicting-profile-stage-for-entry-point.slang new file mode 100644 index 000000000..9cc06347f --- /dev/null +++ b/tests/language-feature/capability/conflicting-profile-stage-for-entry-point.slang @@ -0,0 +1,36 @@ +//TEST:SIMPLE(filecheck=CHECK_ERROR): -target spirv -entry psmain -profile vs_6_0 +//TEST:SIMPLE(filecheck=CHECK_ERROR): -target spirv -entry vsmain -profile ps_6_0 + +//TEST:SIMPLE(filecheck=CHECK): -target spirv -entry vsmain -profile vs_6_0 +//TEST:SIMPLE(filecheck=CHECK): -target spirv -entry psmain -profile vs_6_0 -ignore-capabilities +//TEST:SIMPLE(filecheck=CHECK): -target spirv -entry vsmain -profile ps_6_0 -ignore-capabilities + +// CHECK_ERROR: warning 36112 +// CHECK-NOT: warning 36112 + +struct CameraProperties { + float4x4 MVP; +}; + +[[vk::push_constant]] +ConstantBuffer<CameraProperties> Cam; + +struct VSOutput { + float4 PositionCS : SV_POSITION; + float3 Color : COLOR; +}; + +[shader("vertex")] +VSOutput vsmain(float3 PositionOS : POSITION, float3 Color : COLOR0) +{ + VSOutput output = (VSOutput)0; + output.PositionCS = mul(Cam.MVP, float4(PositionOS, 1)); + output.Color = Color; + return output; +} + +[shader("pixel")] +float4 psmain(VSOutput input) : SV_TARGET +{ + return float4(input.Color, 1); +}
\ No newline at end of file |
