summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2021-03-15 11:16:32 -0400
committerGitHub <noreply@github.com>2021-03-15 11:16:32 -0400
commitfd304c6c02c73a0024f6d982b1936451e4812370 (patch)
tree7f18ac953ea82bb21f3bddce3b36c60e0adacbed /source
parent3d10d139dbc2872b566500bad0eb81f1f9069efa (diff)
Improvements in Docs requirements/availability (#1751)
* #include an absolute path didn't work - because paths were taken to always be relative. * Use capability system in docs. Simplify how requirements/availability is produced. * Small fixes in output of availablity. * Updated stdlib doc. * Small improvements. Co-authored-by: Tim Foley <tfoleyNV@users.noreply.github.com>
Diffstat (limited to 'source')
-rw-r--r--source/slang/slang-capability.cpp25
-rw-r--r--source/slang/slang-capability.h3
-rw-r--r--source/slang/slang-doc-markdown-writer.cpp359
-rw-r--r--source/slang/slang-doc-markdown-writer.h22
4 files changed, 266 insertions, 143 deletions
diff --git a/source/slang/slang-capability.cpp b/source/slang/slang-capability.cpp
index 5de05a5a4..0b82cb92c 100644
--- a/source/slang/slang-capability.cpp
+++ b/source/slang/slang-capability.cpp
@@ -127,6 +127,31 @@ CapabilityAtom findCapabilityAtom(UnownedStringSlice const& name)
return CapabilityAtom::Invalid;
}
+bool isCapabilityDerivedFrom(CapabilityAtom atom, CapabilityAtom base)
+{
+ if (atom == base)
+ {
+ return true;
+ }
+
+ const auto& info = kCapabilityAtoms[Index(atom)];
+
+ for (auto cur : info.bases)
+ {
+ if (cur == CapabilityAtom::Invalid)
+ {
+ return false;
+ }
+
+ if (isCapabilityDerivedFrom(cur, base))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
//
// CapabilitySet
//
diff --git a/source/slang/slang-capability.h b/source/slang/slang-capability.h
index dd3ffce2c..90b03ed29 100644
--- a/source/slang/slang-capability.h
+++ b/source/slang/slang-capability.h
@@ -156,6 +156,9 @@ inline bool operator!=(CapabilitySet const& left, CapabilitySet const& right)
return !(left == right);
}
+ /// Returns true if atom is derived from base
+bool isCapabilityDerivedFrom(CapabilityAtom atom, CapabilityAtom base);
+
/// Find a capability atom with the given `name`, or return CapabilityAtom::Invalid.
CapabilityAtom findCapabilityAtom(UnownedStringSlice const& name);
}
diff --git a/source/slang/slang-doc-markdown-writer.cpp b/source/slang/slang-doc-markdown-writer.cpp
index 259762e88..5105b0b26 100644
--- a/source/slang/slang-doc-markdown-writer.cpp
+++ b/source/slang/slang-doc-markdown-writer.cpp
@@ -2,49 +2,13 @@
#include "slang-doc-markdown-writer.h"
#include "../core/slang-string-util.h"
+#include "../core/slang-type-text-util.h"
#include "slang-ast-builder.h"
#include "slang-lookup.h"
namespace Slang {
-struct DocMarkdownWriter::StringListSet
-{
- Index add(const HashSet<String>& set)
- {
- // -1 means empty, which we don't explicitly store
- Index index = -1;
- if (set.Count())
- {
- List<String> values;
- for (auto& value : set)
- {
- values.add(value);
- }
- // Sort so that can be compared for uniqueness
- values.sort();
-
- index = m_uniqueValues.indexOf(values);
- if (index < 0)
- {
- index = m_uniqueValues.getCount();
- m_uniqueValues.add(values);
- }
- }
- m_map.add(index);
- return index;
- }
-
- Index getValueIndex(Index index) const { return m_map[index]; }
- List<String>& getValuesAt(Index valueIndex) { return m_uniqueValues[valueIndex]; }
- const List<List<String>>& getUniqueValues() const { return m_uniqueValues; }
-
- StringListSet() {}
-
-protected:
- List<Index> m_map;
- List<List<String>> m_uniqueValues;
-};
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DocMarkDownWriter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
@@ -435,8 +399,92 @@ List<DocMarkdownWriter::NameAndText> DocMarkdownWriter::_getUniqueParams(const L
return out;
}
-static void _addRequirements(Decl* decl, HashSet<String>& outRequirements)
+static void _addRequirement(const DocMarkdownWriter::Requirement& req, List<DocMarkdownWriter::Requirement>& ioReqs)
+{
+ if (ioReqs.indexOf(req) < 0)
+ {
+ ioReqs.add(req);
+ }
+}
+
+static void _addRequirement(CodeGenTarget target, const String& value, List<DocMarkdownWriter::Requirement>& ioReqs)
{
+ _addRequirement(DocMarkdownWriter::Requirement{ target, value }, ioReqs);
+}
+
+static DocMarkdownWriter::Requirement _getRequirementFromTargetToken(const Token& tok)
+{
+ typedef DocMarkdownWriter::Requirement Requirement;
+
+ if (tok.type == TokenType::Unknown)
+ {
+ return Requirement{ CodeGenTarget::None, String() };
+ }
+
+ auto targetName = tok.getContent();
+ const CapabilityAtom targetCap = findCapabilityAtom(targetName);
+
+ if (targetCap == CapabilityAtom::Invalid)
+ {
+ return Requirement{ CodeGenTarget::None, String() };
+ }
+
+ static const CapabilityAtom rootAtoms[] =
+ {
+ CapabilityAtom::GLSL,
+ CapabilityAtom::HLSL,
+ CapabilityAtom::CUDA,
+ CapabilityAtom::CPP,
+ CapabilityAtom::C,
+ };
+
+ for (auto rootAtom : rootAtoms)
+ {
+ if (rootAtom == targetCap)
+ {
+ // If its one of the roots we don't need to store the name
+ targetName = UnownedStringSlice();
+ break;
+ }
+ }
+
+ if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::GLSL))
+ {
+ return Requirement{CodeGenTarget::GLSL, targetName};
+ }
+ else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::HLSL))
+ {
+ return Requirement{ CodeGenTarget::HLSL, targetName };
+ }
+ else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::CUDA))
+ {
+ return Requirement{ CodeGenTarget::CUDASource, targetName };
+ }
+ else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::CPP))
+ {
+ return Requirement{ CodeGenTarget::CPPSource, targetName };
+ }
+ else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::C))
+ {
+ return Requirement{ CodeGenTarget::CSource, targetName };
+ }
+
+ return Requirement{ CodeGenTarget::Unknown, String() };
+}
+
+static void _addRequirementFromTargetToken(const Token& tok, List<DocMarkdownWriter::Requirement>& ioReqs)
+{
+ auto req = _getRequirementFromTargetToken(tok);
+ if (req.target != CodeGenTarget::None)
+ {
+ _addRequirement(req, ioReqs);
+ }
+}
+
+static void _addRequirements(Decl* decl, List<DocMarkdownWriter::Requirement>& ioReqs)
+{
+ typedef DocMarkdownWriter::Requirement Requirement;
+
StringBuilder buf;
if (auto spirvRequiredModifier = decl->findModifier<RequiredSPIRVVersionModifier>())
@@ -444,63 +492,137 @@ static void _addRequirements(Decl* decl, HashSet<String>& outRequirements)
buf.Clear();
buf << "SPIR-V ";
spirvRequiredModifier->version.append(buf);
- outRequirements.Add(buf);
+ _addRequirement(CodeGenTarget::GLSL, buf, ioReqs);
}
if (auto glslRequiredModifier = decl->findModifier<RequiredGLSLVersionModifier>())
{
buf.Clear();
buf << "GLSL" << glslRequiredModifier->versionNumberToken.getContent();
- outRequirements.Add(buf);
+ _addRequirement(CodeGenTarget::GLSL, buf, ioReqs);
}
if (auto cudaSMVersionModifier = decl->findModifier<RequiredCUDASMVersionModifier>())
{
buf.Clear();
- buf << "CUDA SM ";
+ buf << "SM ";
cudaSMVersionModifier->version.append(buf);
- outRequirements.Add(buf);
+ _addRequirement(CodeGenTarget::CUDASource, buf, ioReqs);
}
if (auto extensionModifier = decl->findModifier<RequiredGLSLExtensionModifier>())
{
buf.Clear();
- buf << "GLSL " << extensionModifier->extensionNameToken.getContent();
- outRequirements.Add(buf);
+ buf << extensionModifier->extensionNameToken.getContent();
+ _addRequirement(CodeGenTarget::GLSL, buf, ioReqs);
}
if (auto requiresNVAPIAttribute = decl->findModifier<RequiresNVAPIAttribute>())
{
- outRequirements.Add("NVAPI");
+ _addRequirement(CodeGenTarget::HLSL, "NVAPI", ioReqs);
+ }
+
+ for (auto targetIntrinsic : decl->getModifiersOfType<TargetIntrinsicModifier>())
+ {
+ _addRequirementFromTargetToken(targetIntrinsic->targetToken, ioReqs);
+ }
+ for (auto specializedForTarget : decl->getModifiersOfType<SpecializedForTargetModifier>())
+ {
+ _addRequirementFromTargetToken(specializedForTarget->targetToken, ioReqs);
}
}
-void DocMarkdownWriter::_maybeAppendSet(const UnownedStringSlice& title, const StringListSet& set)
+void DocMarkdownWriter::_writeTargetRequirements(const Requirement* reqs, Index reqsCount)
{
+ if (reqsCount == 0)
+ {
+ return;
+ }
+
auto& out = m_builder;
- const auto& uniqueValues = set.getUniqueValues();
+ // Okay we need the name of the CodeGen target
+ UnownedStringSlice name = TypeTextUtil::getCompileTargetName(SlangCompileTarget(reqs->target));
+ out << toSlice("**") << String(name).toUpper() << toSlice("**");
- const Index uniqueCount = uniqueValues.getCount();
- if (uniqueCount > 0 && uniqueValues[0].getCount() > 0)
+ if (!(reqsCount == 1 && reqs[0].value.getLength() == 0))
{
- out << title;
- if (uniqueCount > 1)
+ // Put in a list so we can use convenience funcs
+ List<String> values;
+ for (Index i = 0; i < reqsCount; i++)
{
- for (Index i = 0; i < uniqueCount; ++i)
+ if (reqs[i].value.getLength() > 0)
{
- out << (i + 1) << (". ");
- _appendCommaList(uniqueValues[i], '`');
- out << toSlice("\n");
+ values.add(reqs[i].value);
}
}
- else
+
+ out << toSlice(" ");
+ _appendCommaList(values, '`');
+ }
+
+ out << toSlice(" ");
+}
+
+void DocMarkdownWriter::_appendRequirements(const List<Requirement>& requirements)
+{
+ Index startIndex = 0;
+ CodeGenTarget curTarget = CodeGenTarget::None;
+
+ for (Index i = 0; i < requirements.getCount(); ++i)
+ {
+ const auto& req = requirements[i];
+
+ if (req.target != curTarget)
{
- _appendCommaList(uniqueValues[0], '`');
- out << toSlice("\n");
+ _writeTargetRequirements(requirements.getBuffer() + startIndex, i - startIndex);
+
+ startIndex = i;
+ curTarget = req.target;
}
- out << toSlice("\n");
}
+
+ _writeTargetRequirements(requirements.getBuffer() + startIndex, requirements.getCount() - startIndex);
+}
+
+void DocMarkdownWriter::_maybeAppendRequirements(const UnownedStringSlice& title, const List<List<DocMarkdownWriter::Requirement>>& uniqueRequirements)
+{
+ auto& out = m_builder;
+ const Index uniqueCount = uniqueRequirements.getCount();
+
+ if (uniqueCount <= 0)
+ {
+ return;
+ }
+
+ if (uniqueCount == 1)
+ {
+ const auto& reqs = uniqueRequirements[0];
+
+ // If just HLSL on own, then ignore
+ if (reqs.getCount() == 0 || (reqs.getCount() == 1 && reqs[0] == Requirement{ CodeGenTarget::HLSL, String() }))
+ {
+ return;
+ }
+
+ out << title;
+
+ _appendRequirements(reqs);
+ out << toSlice("\n");
+ }
+ else
+ {
+ out << title;
+
+ for (Index i = 0; i < uniqueCount; ++i)
+ {
+ out << (i + 1) << (". ");
+ _appendRequirements(uniqueRequirements[i]);
+ out << toSlice("\n");
+ }
+ }
+
+ out << toSlice("\n");
}
static Decl* _getSameNameDecl(Decl* decl)
@@ -600,120 +722,73 @@ void DocMarkdownWriter::writeCallableOverridable(const DocMarkup::Entry& entry,
sigs.sort([](CallableDecl* a, CallableDecl* b) -> bool { return a->loc.getRaw() < b->loc.getRaw(); });
}
- // Set of sets of requirements, holds index map from addition to the set entry
- StringListSet requirementsSetSet;
+ // The unique requirements found
+ List<List<Requirement>> uniqueRequirements;
+ // Maps a sig index to a unique requirements set
+ List<Index> requirementsMap;
- // Similarly for targets
- StringListSet targetSetSet;
-
- // We want to determine the unique signature, and then the requirements for the signature
+ for (Index i = 0; i < sigs.getCount(); ++i)
{
- for (Index i = 0; i < sigs.getCount(); ++i)
+ CallableDecl* sig = sigs[i];
+
+ // Add the requirements for all the different versions
+ List<Requirement> requirements;
+ for (CallableDecl* curSig = sig; curSig; curSig = curSig->nextDecl)
{
- CallableDecl* sig = sigs[i];
- // Add the requirements for all the different versions
- {
- HashSet<String> requirementsSet;
- HashSet<String> targetSet;
- for (CallableDecl* curSig = sig; curSig; curSig = curSig->nextDecl)
- {
- _addRequirements(sig, requirementsSet);
+ _addRequirements(sig, requirements);
+ }
- // Handle Target info
+ // TODO(JS): HACK - almost everything is available for HLSL, and is generally not marked up in stdlib
+ // So assume here it's available
+ _addRequirement(Requirement{ CodeGenTarget::HLSL, String() }, requirements);
- for (auto targetIntrinsic : sig->getModifiersOfType<TargetIntrinsicModifier>())
- {
- targetSet.Add(String(targetIntrinsic->targetToken.getContent()).toUpper());
- }
- for (auto specializedForTarget : sig->getModifiersOfType<SpecializedForTargetModifier>())
- {
- targetSet.Add(String(specializedForTarget->targetToken.getContent()).toUpper());
- }
- }
-
- requirementsSetSet.add(requirementsSet);
+ // We want to make the requirements set unique, so we can look it up easily in the uniqueRequirements, so we sort it
+ // This also has the benefit of keeping the ordering consistent
+ requirements.sort();
- // TODO(JS): This really isn't right, we ideally have markup that made hlsl availability explicit
- // We *assume* that we have 'hlsl' for now
- targetSet.Add(String("HLSL"));
- targetSetSet.add(targetSet);
- }
+ // See if we already have this combination of requirements
+ Index uniqueRequirementIndex = uniqueRequirements.indexOf(requirements);
+ if (uniqueRequirementIndex < 0)
+ {
+ // If not add it
+ uniqueRequirementIndex = uniqueRequirements.getCount();
+ uniqueRequirements.add(requirements);
}
+
+ // Add the uniqueRequirement index for this sig index
+ requirementsMap.add(uniqueRequirementIndex);
}
-
+
// Output the signature
{
out << toSlice("## Signature \n\n");
out << toSlice("```\n");
Index prevRequirementsIndex = -1;
- Index prevTargetIndex = -1;
-
+
const Int sigCount = sigs.getCount();
for (Index i = 0; i < sigCount; ++i)
- {
+ {
auto sig = sigs[i];
+ // Get the requirements index for this sig
+ const Index requirementsIndex = requirementsMap[i];
// Output if needs unique requirements
- if (requirementsSetSet.getUniqueValues().getCount() > 1)
+ if (uniqueRequirements.getCount() > 1 )
{
- const Index requirementsIndex = requirementsSetSet.getValueIndex(i);
if (requirementsIndex != prevRequirementsIndex)
{
- if (requirementsIndex >= 0)
- {
- out << toSlice("/// See Requirement ") << (requirementsIndex + 1) << toSlice("\n");
- }
- else
- {
- out << toSlice("/// No requirements\n");
- }
+ out << toSlice("/// See Availability ") << (requirementsIndex + 1) << toSlice("\n");
prevRequirementsIndex = requirementsIndex;
}
}
- if (targetSetSet.getUniqueValues().getCount() > 1)
- {
- const Index targetIndex = targetSetSet.getValueIndex(i);
- if (targetIndex != prevTargetIndex)
- {
- if (targetIndex >= 0)
- {
- out << toSlice("/// See Target Availability ") << (targetIndex + 1) << toSlice("\n");
- }
- else
- {
- out << toSlice("/// All Targets\n");
- }
- prevTargetIndex = targetIndex;
- }
- }
-
writeSignature(sig);
}
out << "```\n\n";
}
- _maybeAppendSet(toSlice("## Requirements\n\n"), requirementsSetSet);
-
- // Target availability
-
- {
- const auto& uniqueValues = targetSetSet.getUniqueValues();
- if (uniqueValues.getCount() == 1 && uniqueValues[0].getCount() == 1 && uniqueValues[0][0] == "hlsl")
- {
- // TODO(JS):
- // If something is marked up for hlsl, and nothing else, that indicates it might *only* be available on hlsl, but
- // we don't correctly handle that here.
-
- // If the only thing we have is 'hlsl' - we injected that so we'll *assume* it's available everywhere, so
- // don't bother outputting.
- }
- else
- {
- _maybeAppendSet(toSlice("## Target Availability\n\n"), targetSetSet);
- }
- }
+ _maybeAppendRequirements(toSlice("## Availability\n\n"), uniqueRequirements);
{
// We will use the first documentation found for each parameter type
diff --git a/source/slang/slang-doc-markdown-writer.h b/source/slang/slang-doc-markdown-writer.h
index 93607d5f4..7a6c0a7e0 100644
--- a/source/slang/slang-doc-markdown-writer.h
+++ b/source/slang/slang-doc-markdown-writer.h
@@ -4,6 +4,7 @@
#include "slang-doc-extractor.h"
#include "slang-ast-print.h"
+#include "slang-compiler.h"
namespace Slang {
@@ -28,6 +29,23 @@ struct DocMarkdownWriter
Part name;
};
+ struct Requirement
+ {
+ typedef Requirement ThisType;
+
+ bool operator<(const ThisType& rhs) const { return Index(target) < Index(rhs.target) || (target == rhs.target && value < rhs.value); }
+
+ bool operator==(const ThisType& rhs) const { return target == rhs.target && value == rhs.value; }
+ SLANG_FORCE_INLINE bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
+
+ /// Using CodeGenTarget may not be most appropriate, perhaps it should use a CapabilityAtom
+ /// For now use target, and since we always go through Source -> byte code it is fairly straight forward to understand the
+ /// meaning.
+ CodeGenTarget target;
+ /// The 'value' requirement associated with a target. If it's empty it's just the target that is a requirement.
+ String value;
+ };
+
/// Write out all documentation to the output buffer
void writeAll();
@@ -106,7 +124,9 @@ struct DocMarkdownWriter
void _appendCommaList(const List<String>& strings, char wrapChar);
- void _maybeAppendSet(const UnownedStringSlice& title, const StringListSet& set);
+ void _appendRequirements(const List<DocMarkdownWriter::Requirement>& requirements);
+ void _maybeAppendRequirements(const UnownedStringSlice& title, const List<List<DocMarkdownWriter::Requirement>>& uniqueRequirements);
+ void _writeTargetRequirements(const Requirement* reqs, Index reqsCount);
/// Appends prefix and the list of types derived from
void _appendDerivedFrom(const UnownedStringSlice& prefix, AggTypeDeclBase* aggTypeDecl);