summaryrefslogtreecommitdiff
path: root/source/slang/emit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/emit.cpp')
-rw-r--r--source/slang/emit.cpp198
1 files changed, 196 insertions, 2 deletions
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index 9f9a089c8..4ed7dd5a7 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -136,6 +136,14 @@ struct SharedEmitContext
// How far are we indented?
Int indentLevel = 0;
+
+ // Map a string name to the number of times we have seen this
+ // name used so far during code emission.
+ Dictionary<String, UInt> uniqueNameCounters;
+
+ // Map an IR instruction to the name that we've decided
+ // to use for it when emitting code.
+ Dictionary<IRInst*, String> mapInstToName;
};
struct EmitContext
@@ -1895,8 +1903,128 @@ struct EmitVisitor
return id;
}
- String getIRName(
- IRInst* inst)
+ /// "Scrub" a name so that it complies with restrictions of the target language.
+ String scrubName(
+ String const& name)
+ {
+ // We will use a plain `U` as a dummy character to insert
+ // whenever we need to insert things to make a string into
+ // valid name.
+ //
+ char const* dummyChar = "U";
+
+ // Special case a name that is the empty string, just in case.
+ if(name.Length() == 0)
+ return dummyChar;
+
+ // Otherwise, we are going to walk over the name byte by byte
+ // and write some legal characters to the output as we go.
+ StringBuilder sb;
+
+ if(getTarget(context) == CodeGenTarget::GLSL)
+ {
+ // GLSL reserverse all names that start with `gl_`,
+ // so if we are in danger of collision, then make
+ // our name start with a dummy character instead.
+ if(name.StartsWith("gl_"))
+ {
+ sb.append(dummyChar);
+ }
+ }
+
+ // We will also detect user-defined names that
+ // might overlap with our convention for mangled names,
+ // to avoid an possible collision.
+ if(name.StartsWith("_S"))
+ {
+ sb.Append(dummyChar);
+ }
+
+ // TODO: This is where we might want to consult
+ // a dictionary of reserved words for the chosen target
+ //
+ // if(isReservedWord(name)) { sb.Append(dummyChar); }
+ //
+
+ // We need to track the previous byte in
+ // order to detect consecutive underscores for GLSL.
+ int prevChar = -1;
+
+ for(auto c : name)
+ {
+ // We will treat a dot character just like an underscore
+ // for the purposes of producing a scrubbed name, so
+ // that we translate `SomeType.someMethod` into
+ // `SomeType_someMethod`.
+ //
+ // By handling this case at the top of this loop, we
+ // ensure that a `.`-turned-`_` is handled just like
+ // a `_` in the original name, and will be properly
+ // scrubbed for GLSL output.
+ //
+ if(c == '.')
+ {
+ c = '_';
+ }
+
+ if(((c >= 'a') && (c <= 'z'))
+ || ((c >= 'A') && (c <= 'Z')))
+ {
+ // Ordinary ASCII alphabetic characters are assumed
+ // to always be okay.
+ }
+ else if((c >= '0') && (c <= '9'))
+ {
+ // We don't want to allow a digit as the first
+ // byte in a name, since the result wouldn't
+ // be a valid identifier in many target languages.
+ if(prevChar == -1)
+ {
+ sb.append(dummyChar);
+ }
+ }
+ else if(c == '_')
+ {
+ // We will collapse any consecutive sequence of `_`
+ // characters into a single one (this means that
+ // some names that were unique in the original
+ // code might not resolve to unique names after
+ // scrubbing, but that was true in general).
+
+ if(prevChar == '_')
+ {
+ // Skip this underscore, so we don't output
+ // more than one in a row.
+ continue;
+ }
+ }
+ else
+ {
+ // If we run into a character that wouldn't normally
+ // be allowed in an identifier, we need to translate
+ // it into something that *is* valid.
+ //
+ // Our solution for now will be very clumsy: we will
+ // emit `x` and then the hexadecimal version of
+ // the byte we were given.
+ sb.append("x");
+ sb.append(uint32_t((unsigned char) c), 16);
+
+ // We don't want to apply the default handling below,
+ // so skip to the top of the loop now.
+ prevChar = c;
+ continue;
+ }
+
+ sb.append(c);
+ prevChar = c;
+ }
+
+ return sb.ProduceString();
+ }
+
+ String generateIRName(
+ IRInst* inst)
{
// If the instruction names something
// that should be emitted as a target intrinsic,
@@ -1906,6 +2034,58 @@ struct EmitVisitor
return intrinsicDecoration->definition;
}
+ // If we have a name hint on the instruction, then we will try to use that
+ // to provide the actual name in the output code.
+ //
+ // We need to be careful that the name follows the rules of the target language,
+ // so there is a "scrubbing" step that needs to be applied here.
+ //
+ // We also need to make sure that the name won't collide with other declarations
+ // that might have the same name hint applied, so we will still unique
+ // them by appending the numeric ID of the instruction.
+ //
+ // TODO: Find cases where we can drop the suffix safely.
+ //
+ // TODO: When we start having to handle symbols with external linkage for
+ // things like DXIL libraries, we will need to *not* use the friendly
+ // names for stuff that should be link-able.
+ //
+ if(auto nameHintDecoration = inst->findDecoration<IRNameHintDecoration>())
+ {
+ // The name we output will basically be:
+ //
+ // <nameHint>_<uniqueID>
+ //
+ // Except that we will "scrub" the name hint first,
+ // and we will omit the underscore if the (scrubbed)
+ // name hint already ends with one.
+ //
+
+ String nameHint = nameHintDecoration->name->text;
+ nameHint = scrubName(nameHint);
+
+ StringBuilder sb;
+ sb.append(nameHint);
+
+ // Avoid introducing a double underscore
+ if(!nameHint.EndsWith("_"))
+ {
+ sb.append("_");
+ }
+
+ String key = sb.ProduceString();
+ UInt count = 0;
+ context->shared->uniqueNameCounters.TryGetValue(key, count);
+
+ context->shared->uniqueNameCounters[key] = count+1;
+
+ sb.append(count);
+ return sb.ProduceString();
+ }
+
+
+
+
// If the instruction has a mangled name, then emit using that.
if (auto globalValue = as<IRGlobalValue>(inst))
{
@@ -1925,9 +2105,23 @@ struct EmitVisitor
StringBuilder sb;
sb << "_S";
sb << getID(inst);
+
+
return sb.ProduceString();
}
+ String getIRName(
+ IRInst* inst)
+ {
+ String name;
+ if(!context->shared->mapInstToName.TryGetValue(inst, name))
+ {
+ name = generateIRName(inst);
+ context->shared->mapInstToName.Add(inst, name);
+ }
+ return name;
+ }
+
struct IRDeclaratorInfo
{
enum class Flavor