summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-mangle.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-mangle.cpp')
-rw-r--r--source/slang/slang-mangle.cpp478
1 files changed, 478 insertions, 0 deletions
diff --git a/source/slang/slang-mangle.cpp b/source/slang/slang-mangle.cpp
new file mode 100644
index 000000000..16f7b64bb
--- /dev/null
+++ b/source/slang/slang-mangle.cpp
@@ -0,0 +1,478 @@
+#include "slang-mangle.h"
+
+#include "slang-name.h"
+#include "slang-syntax.h"
+
+namespace Slang
+{
+ struct ManglingContext
+ {
+ StringBuilder sb;
+ };
+
+ void emitRaw(
+ ManglingContext* context,
+ char const* text)
+ {
+ context->sb.append(text);
+ }
+
+ void emit(
+ ManglingContext* context,
+ UInt value)
+ {
+ context->sb.append(value);
+ }
+
+ void emit(
+ ManglingContext* context,
+ String const& value)
+ {
+ context->sb.append(value);
+ }
+
+ void emitName(
+ ManglingContext* context,
+ Name* name)
+ {
+ String str = getText(name);
+
+ // If the name consists of only traditional "identifer characters"
+ // (`[a-zA-Z_]`), then we wnat to emit it more or less directly.
+ //
+ // If it contains code points outside that range, we'll need to
+ // do something to encode them. I don't want to deal with
+ // that right now, so I'm going to ignore it.
+
+ // We prefix the string with its byte length, so that
+ // decoding doesn't have to worry about finding a terminator.
+ Index length = str.getLength();
+ emit(context, length);
+ context->sb.append(str);
+ }
+
+ void emitVal(
+ ManglingContext* context,
+ Val* val);
+
+ void emitQualifiedName(
+ ManglingContext* context,
+ DeclRef<Decl> declRef);
+
+ void emitSimpleIntVal(
+ ManglingContext* context,
+ Val* val)
+ {
+ if( auto constVal = as<ConstantIntVal>(val) )
+ {
+ auto cVal = constVal->value;
+ if(cVal >= 0 && cVal <= 9 )
+ {
+ emit(context, (UInt)cVal);
+ return;
+ }
+ }
+
+ // Fallback:
+ emitVal(context, val);
+ }
+
+ void emitBaseType(
+ ManglingContext* context,
+ BaseType baseType)
+ {
+ switch( baseType )
+ {
+ case BaseType::Void: emitRaw(context, "V"); break;
+ case BaseType::Bool: emitRaw(context, "b"); break;
+ case BaseType::Int8: emitRaw(context, "c"); break;
+ case BaseType::Int16: emitRaw(context, "s"); break;
+ case BaseType::Int: emitRaw(context, "i"); break;
+ case BaseType::Int64: emitRaw(context, "I"); break;
+ case BaseType::UInt8: emitRaw(context, "C"); break;
+ case BaseType::UInt16: emitRaw(context, "S"); break;
+ case BaseType::UInt: emitRaw(context, "u"); break;
+ case BaseType::UInt64: emitRaw(context, "U"); break;
+ case BaseType::Half: emitRaw(context, "h"); break;
+ case BaseType::Float: emitRaw(context, "f"); break;
+ case BaseType::Double: emitRaw(context, "d"); break;
+ break;
+
+ default:
+ SLANG_UNEXPECTED("unimplemented case in mangling");
+ break;
+ }
+ }
+
+ void emitType(
+ ManglingContext* context,
+ Type* type)
+ {
+ // TODO: actually implement this bit...
+
+ if( auto basicType = dynamicCast<BasicExpressionType>(type) )
+ {
+ emitBaseType(context, basicType->baseType);
+ }
+ else if( auto vecType = dynamicCast<VectorExpressionType>(type) )
+ {
+ emitRaw(context, "v");
+ emitSimpleIntVal(context, vecType->elementCount);
+ emitType(context, vecType->elementType);
+ }
+ else if( auto matType = dynamicCast<MatrixExpressionType>(type) )
+ {
+ emitRaw(context, "m");
+ emitSimpleIntVal(context, matType->getRowCount());
+ emitRaw(context, "x");
+ emitSimpleIntVal(context, matType->getColumnCount());
+ emitType(context, matType->getElementType());
+ }
+ else if( auto namedType = dynamicCast<NamedExpressionType>(type) )
+ {
+ emitType(context, GetType(namedType->declRef));
+ }
+ else if( auto declRefType = dynamicCast<DeclRefType>(type) )
+ {
+ emitQualifiedName(context, declRefType->declRef);
+ }
+ else if (auto arrType = dynamicCast<ArrayExpressionType>(type))
+ {
+ emitRaw(context, "a");
+ emitSimpleIntVal(context, arrType->ArrayLength);
+ emitType(context, arrType->baseType);
+ }
+ else if( auto taggedUnionType = dynamicCast<TaggedUnionType>(type) )
+ {
+ emitRaw(context, "u");
+ for( auto caseType : taggedUnionType->caseTypes )
+ {
+ emitType(context, caseType);
+ }
+ emitRaw(context, "U");
+ }
+ else
+ {
+ SLANG_UNEXPECTED("unimplemented case in mangling");
+ }
+ }
+
+ void emitVal(
+ ManglingContext* context,
+ Val* val)
+ {
+ if( auto type = dynamicCast<Type>(val) )
+ {
+ emitType(context, type);
+ }
+ else if( auto witness = dynamicCast<Witness>(val) )
+ {
+ // We don't emit witnesses as part of a mangled
+ // name, because the way that the front-end
+ // arrived at the witness is not important;
+ // what matters is that the type constraint
+ // was satisfied.
+ //
+ // TODO: make sure we can't get name collisions
+ // between specializations of declarations
+ // with the same numbers of generic parameters,
+ // but different constraints. We might have
+ // to mangle in the constraints even when
+ // the whole thing is specialized...
+ }
+ else if( auto genericParamIntVal = dynamicCast<GenericParamIntVal>(val) )
+ {
+ // TODO: we shouldn't be including the names of generic parameters
+ // anywhere in mangled names, since changing parameter names
+ // shouldn't break binary compatibility.
+ //
+ // The right solution in the long term is for generic parameters
+ // (both types and values) to be mangled in terms of their
+ // "depth" (how many outer generics) and "index" (which
+ // parameter are they at the specified depth).
+ emitRaw(context, "K");
+ emitName(context, genericParamIntVal->declRef.GetName());
+ }
+ else if( auto constantIntVal = dynamicCast<ConstantIntVal>(val) )
+ {
+ // TODO: need to figure out what prefix/suffix is needed
+ // to allow demangling later.
+ emitRaw(context, "k");
+ emit(context, (UInt) constantIntVal->value);
+ }
+ else
+ {
+ SLANG_UNEXPECTED("unimplemented case in mangling");
+ }
+ }
+
+ void emitQualifiedName(
+ ManglingContext* context,
+ DeclRef<Decl> declRef)
+ {
+ auto parentDeclRef = declRef.GetParent();
+ auto parentGenericDeclRef = parentDeclRef.as<GenericDecl>();
+ if( parentDeclRef )
+ {
+ // In certain cases we want to skip emitting the parent
+ if(parentGenericDeclRef && (parentGenericDeclRef.getDecl()->inner.Ptr() != declRef.getDecl()))
+ {
+ }
+ else if(parentDeclRef.as<FunctionDeclBase>())
+ {
+ }
+ else
+ {
+ emitQualifiedName(context, parentDeclRef);
+ }
+ }
+
+ // A generic declaration is kind of a pseudo-declaration
+ // as far as the user is concerned; so we don't want
+ // to emit its name.
+ if(auto genericDeclRef = declRef.as<GenericDecl>())
+ {
+ return;
+ }
+
+ // Inheritance declarations don't have meaningful names,
+ // and so we should emit them based on the type
+ // that is doing the inheriting.
+ if(auto inheritanceDeclRef = declRef.as<InheritanceDecl>())
+ {
+ emit(context, "I");
+ emitType(context, GetSup(inheritanceDeclRef));
+ return;
+ }
+
+ // Similarly, an extension doesn't have a name worth
+ // emitting, and we should base things on its target
+ // type instead.
+ if(auto extensionDeclRef = declRef.as<ExtensionDecl>())
+ {
+ // TODO: as a special case, an "unconditional" extension
+ // that is in the same module as the type it extends should
+ // be treated as equivalent to the type itself.
+ emit(context, "X");
+ emitType(context, GetTargetType(extensionDeclRef));
+ return;
+ }
+
+ emitName(context, declRef.GetName());
+
+ // Special case: accessors need some way to distinguish themselves
+ // so that a getter/setter/ref-er don't all compile to the same name.
+ {
+ if (declRef.is<GetterDecl>()) emitRaw(context, "Ag");
+ if (declRef.is<SetterDecl>()) emitRaw(context, "As");
+ if (declRef.is<RefAccessorDecl>()) emitRaw(context, "Ar");
+ }
+
+ // Are we the "inner" declaration beneath a generic decl?
+ if(parentGenericDeclRef && (parentGenericDeclRef.getDecl()->inner.Ptr() == declRef.getDecl()))
+ {
+ // There are two cases here: either we have specializations
+ // in place for the parent generic declaration, or we don't.
+
+ auto subst = findInnerMostGenericSubstitution(declRef.substitutions);
+ if( subst && subst->genericDecl == parentGenericDeclRef.getDecl() )
+ {
+ // This is the case where we *do* have substitutions.
+ emitRaw(context, "G");
+ UInt genericArgCount = subst->args.getCount();
+ emit(context, genericArgCount);
+ for( auto aa : subst->args )
+ {
+ emitVal(context, aa);
+ }
+ }
+ else
+ {
+ // We don't have substitutions, so we will emit
+ // information about the parameters of the generic here.
+ emitRaw(context, "g");
+ UInt genericParameterCount = 0;
+ for( auto mm : getMembers(parentGenericDeclRef) )
+ {
+ if(mm.is<GenericTypeParamDecl>())
+ {
+ genericParameterCount++;
+ }
+ else if(mm.is<GenericValueParamDecl>())
+ {
+ genericParameterCount++;
+ }
+ else if(mm.is<GenericTypeConstraintDecl>())
+ {
+ genericParameterCount++;
+ }
+ else
+ {
+ }
+ }
+
+ emit(context, genericParameterCount);
+ for( auto mm : getMembers(parentGenericDeclRef) )
+ {
+ if(auto genericTypeParamDecl = mm.as<GenericTypeParamDecl>())
+ {
+ emitRaw(context, "T");
+ }
+ else if(auto genericValueParamDecl = mm.as<GenericValueParamDecl>())
+ {
+ emitRaw(context, "v");
+ emitType(context, GetType(genericValueParamDecl));
+ }
+ else if(mm.as<GenericTypeConstraintDecl>())
+ {
+ emitRaw(context, "C");
+ // TODO: actually emit info about the constraint
+ }
+ else
+ {
+ }
+ }
+ }
+ }
+
+ // If the declaration has parameters, then we need to emit
+ // those parameters to distinguish it from other declarations
+ // of the same name that might have different parameters.
+ //
+ // We'll also go ahead and emit the result type as well,
+ // just for completeness.
+ //
+ if( auto callableDeclRef = declRef.as<CallableDecl>())
+ {
+ auto parameters = GetParameters(callableDeclRef);
+ UInt parameterCount = parameters.Count();
+
+ emitRaw(context, "p");
+ emit(context, parameterCount);
+ emitRaw(context, "p");
+
+ for(auto paramDeclRef : parameters)
+ {
+ emitType(context, GetType(paramDeclRef));
+ }
+
+ // Don't print result type for an initializer/constructor,
+ // since it is implicit in the qualified name.
+ if (!callableDeclRef.is<ConstructorDecl>())
+ {
+ emitType(context, GetResultType(callableDeclRef));
+ }
+ }
+ }
+
+ void mangleName(
+ ManglingContext* context,
+ DeclRef<Decl> declRef)
+ {
+ // TODO: catch cases where the declaration should
+ // forward to something else? E.g., what if we
+ // are asked to mangle the name of a `typedef`?
+
+ // We will start with a unique prefix to avoid
+ // clashes with user-defined symbols:
+ emitRaw(context, "_S");
+
+ auto decl = declRef.getDecl();
+
+ // Next we will add a bit of info to register
+ // the *kind* of declaration we are dealing with.
+ //
+ // Functions will get no prefix, since we assume
+ // they are a common case:
+ if(as<FuncDecl>(decl))
+ {}
+ // Types will get a `T` prefix:
+ else if(as<AggTypeDecl>(decl))
+ emitRaw(context, "T");
+ else if(as<TypeDefDecl>(decl))
+ emitRaw(context, "T");
+ // Variables will get a `V` prefix:
+ //
+ // TODO: probably need to pull constant-buffer
+ // declarations out of this...
+ else if(as<VarDeclBase>(decl))
+ emitRaw(context, "V");
+ else
+ {
+ // TODO: handle other cases
+ }
+
+ // Now we encode the qualified name of the decl.
+ emitQualifiedName(context, declRef);
+ }
+
+ String getMangledName(DeclRef<Decl> const& declRef)
+ {
+ ManglingContext context;
+ mangleName(&context, declRef);
+ return context.sb.ProduceString();
+ }
+
+ String getMangledName(DeclRefBase const & declRef)
+ {
+ return getMangledName(
+ DeclRef<Decl>(declRef.decl, declRef.substitutions));
+ }
+
+ String getMangledName(Decl* decl)
+ {
+ return getMangledName(makeDeclRef(decl));
+ }
+
+ String getMangledNameForConformanceWitness(
+ DeclRef<Decl> sub,
+ DeclRef<Decl> sup)
+ {
+ ManglingContext context;
+ emitRaw(&context, "_SW");
+ emitQualifiedName(&context, sub);
+ emitQualifiedName(&context, sup);
+ return context.sb.ProduceString();
+ }
+
+ String getMangledNameForConformanceWitness(
+ DeclRef<Decl> sub,
+ Type* sup)
+ {
+ // The mangled form for a witness that `sub`
+ // conforms to `sup` will be named:
+ //
+ // {Conforms(sub,sup)} => _SW{sub}{sup}
+ //
+ ManglingContext context;
+ emitRaw(&context, "_SW");
+ emitQualifiedName(&context, sub);
+ emitType(&context, sup);
+ return context.sb.ProduceString();
+ }
+
+ String getMangledNameForConformanceWitness(
+ Type* sub,
+ Type* sup)
+ {
+ // The mangled form for a witness that `sub`
+ // conforms to `sup` will be named:
+ //
+ // {Conforms(sub,sup)} => _SW{sub}{sup}
+ //
+ ManglingContext context;
+ emitRaw(&context, "_SW");
+ emitType(&context, sub);
+ emitType(&context, sup);
+ return context.sb.ProduceString();
+ }
+
+ String getMangledTypeName(Type* type)
+ {
+ ManglingContext context;
+ emitType(&context, type);
+ return context.sb.ProduceString();
+ }
+
+
+}