summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArielG-NV <159081215+ArielG-NV@users.noreply.github.com>2024-07-18 11:03:30 -0400
committerGitHub <noreply@github.com>2024-07-18 11:03:30 -0400
commit6f1371a7870a7c371a77cfdee1daa5c32f0c5377 (patch)
tree70fa0962fa5f34ea4646a531c3969bbf7eb8d74b
parent494efd7254f28ec46aff84bb1c06fe582a743c1a (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.h2
-rw-r--r--source/slang/slang-check-decl.cpp2
-rw-r--r--source/slang/slang-check-impl.h2
-rw-r--r--source/slang/slang-check-shader.cpp50
-rw-r--r--source/slang/slang-compiler.cpp21
-rw-r--r--source/slang/slang-diagnostic-defs.h3
-rw-r--r--source/slang/slang-options.cpp19
-rw-r--r--tests/language-feature/capability/conflicting-profile-stage-for-entry-point.slang36
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