summaryrefslogtreecommitdiffstats
path: root/source/slang/mangle.cpp
diff options
context:
space:
mode:
authorTim Foley <tfoley@nvidia.com>2017-09-22 08:24:14 -0700
committerTim Foley <tfoley@nvidia.com>2017-09-22 14:18:44 -0700
commit1972fa3616af55c223096e9b11465f580530d88f (patch)
tree990e9f62c9ece4ec9996866480d6dd469c4f982f /source/slang/mangle.cpp
parentb206af702cbc8cc42c73052ad690d69984ecd7b7 (diff)
More work on IR-based lowering and cross-compilation
None of these changes are made "live" at the moment. I'm just trying to get them checked in to avoid divering too far from `master` at any point during development. - Add basic emit logic to produce GLSL from the IR in a few cases (the existing IR emit logic was ad hoc and HLSL-specific) - When lowering a function declaration, walk up its chain of parent declarations to collect additional parameters as needed - When lowering a call, make sure to add generic arguments that come from the declaration reference being called - Attach a "mangled name" to symbols when lowering, so that we can eventually use that name to resolve things for linkage. - After the above work, I had to apply some fixups to make sure that generic arguments *don't* get added when the user is calling an `__intrinsic_op` function, since those should map 1-to-1 down to instructions with just their ordinary parameter list. A big open question right now is whether I should continue to represent the generic arguments as just part of the ordinary argument list for a function, or split them out into separate `applyGeneric` and `apply` steps. A strongly related question is whether a declaration with generic parameters should lower into a single declaration, or one declaration nested inside an outer generic declaration. A good future step at this point would be to eliminate a lot of the `__intrinsic_op` stuff in favor of having the builtin functions include their own definitions, which might be in terms of a new expression-level construct for writing inline IR operations. This can't be done until the existing AST-to-AST path is no longer needed for cross-compilation purposes. More immediate next steps here: - We need a way to round-trip calls to external declaration that get handled by this mangled-name logic. Basically, if we are asked to output HLSL and we see a call to `_S...GetDimensions...(float4, t, a, ...)` we need to be able to walk the mangled name and get back to `t.getDimensions(a, ...)` without a whole lot of manual definitions to make things round-trip. - In the other case, where a declaration isn't built-in for the chosen target, we need to be able to load a module of target-specific definitions (which will somehow map back to symbols with certain mangled names) and then look these up (by mangled name) and then load/link/inline them into the user's IR to satisfy requirements in their code.
Diffstat (limited to 'source/slang/mangle.cpp')
-rw-r--r--source/slang/mangle.cpp190
1 files changed, 190 insertions, 0 deletions
diff --git a/source/slang/mangle.cpp b/source/slang/mangle.cpp
new file mode 100644
index 000000000..ce36a97b6
--- /dev/null
+++ b/source/slang/mangle.cpp
@@ -0,0 +1,190 @@
+#include "mangle.h"
+
+#include "name.h"
+#include "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 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.
+ UInt length = str.Length();
+ emit(context, length);
+ context->sb.append(str);
+ }
+
+ void emitType(
+ ManglingContext* context,
+ Type* type)
+ {
+ // TODO: actually implement this bit...
+ }
+
+ void emitQualifiedName(
+ ManglingContext* context,
+ Decl* decl)
+ {
+ auto parentDecl = decl->ParentDecl;
+ if( parentDecl )
+ {
+ emitQualifiedName(context, parentDecl);
+ }
+
+ // 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 genericDecl = dynamic_cast<GenericDecl*>(decl) )
+ {
+ return;
+ }
+
+ emitName(context, decl->nameAndLoc.name);
+
+ if( auto parentGenericDecl = dynamic_cast<GenericDecl*>(parentDecl))
+ {
+ emitRaw(context, "g");
+ UInt genericParameterCount = 0;
+ for( auto mm : parentGenericDecl->Members )
+ {
+ if(mm.As<GenericTypeParamDecl>())
+ {
+ genericParameterCount++;
+ }
+ else if(mm.As<GenericValueParamDecl>())
+ {
+ genericParameterCount++;
+ }
+ else if(mm.As<GenericTypeConstraintDecl>())
+ {
+ genericParameterCount++;
+ }
+ else
+ {
+ }
+ }
+
+ emit(context, genericParameterCount);
+ for( auto mm : parentGenericDecl->Members )
+ {
+ if(auto genericTypeParamDecl = mm.As<GenericTypeParamDecl>())
+ {
+ emitRaw(context, "T");
+ }
+ else if(auto genericValueParamDecl = mm.As<GenericValueParamDecl>())
+ {
+ emitRaw(context, "v");
+ emitType(context, genericValueParamDecl->getType());
+ }
+ 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 callableDecl = dynamic_cast<CallableDecl*>(decl) )
+ {
+ emitRaw(context, "p");
+ UInt parameterCount = callableDecl->GetParameters().Count();
+ emit(context, parameterCount);
+ for(auto pp : callableDecl->GetParameters())
+ {
+ emitType(context, pp->getType());
+ }
+
+ emitType(context, callableDecl->ReturnType);
+ }
+ }
+
+ void mangleName(
+ ManglingContext* context,
+ Decl* decl)
+ {
+ // 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");
+
+ // 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(dynamic_cast<FuncDecl*>(decl))
+ {}
+ // Types will get a `T` prefix:
+ else if(dynamic_cast<AggTypeDecl*>(decl))
+ emitRaw(context, "T");
+ else if(dynamic_cast<TypeDefDecl*>(decl))
+ emitRaw(context, "T");
+ // Variables will get a `V` prefix:
+ //
+ // TODO: probably need to pull constant-buffer
+ // declarations out of this...
+ else if(dynamic_cast<VarDeclBase*>(decl))
+ emitRaw(context, "V");
+ else
+ {
+ // TODO: handle other cases
+ }
+
+ // Now we encode the qualified name of the decl.
+ emitQualifiedName(context, decl);
+ }
+
+
+
+ String getMangledName(Decl* decl)
+ {
+ ManglingContext context;
+
+ mangleName(&context, decl);
+
+ return context.sb.ProduceString();
+ }
+}