diff options
| author | Tim Foley <tfoley@nvidia.com> | 2017-09-22 08:24:14 -0700 |
|---|---|---|
| committer | Tim Foley <tfoley@nvidia.com> | 2017-09-22 14:18:44 -0700 |
| commit | 1972fa3616af55c223096e9b11465f580530d88f (patch) | |
| tree | 990e9f62c9ece4ec9996866480d6dd469c4f982f /source/slang/mangle.cpp | |
| parent | b206af702cbc8cc42c73052ad690d69984ecd7b7 (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.cpp | 190 |
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(); + } +} |
