summaryrefslogtreecommitdiff
path: root/source/slang/lower-to-ir.cpp
diff options
context:
space:
mode:
authorTim Foley <tim.foley.is@gmail.com>2017-09-25 11:27:54 -0700
committerGitHub <noreply@github.com>2017-09-25 11:27:54 -0700
commitb6cf0f4ae0f3f9d1f377d3f134dcf994676e68b4 (patch)
treebcc781497b36664cbebe10d91f888be674c82f8b /source/slang/lower-to-ir.cpp
parentb206af702cbc8cc42c73052ad690d69984ecd7b7 (diff)
parent0aa440a22ab18bc4a9077fcf17966ed4949d684b (diff)
Merge pull request #191 from tfoleyNV/ir-work
More work on IR-based lowering and cross-compilation
Diffstat (limited to 'source/slang/lower-to-ir.cpp')
-rw-r--r--source/slang/lower-to-ir.cpp800
1 files changed, 705 insertions, 95 deletions
diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp
index 6f64a2215..464d5d50a 100644
--- a/source/slang/lower-to-ir.cpp
+++ b/source/slang/lower-to-ir.cpp
@@ -5,6 +5,7 @@
#include "ir.h"
#include "ir-insts.h"
+#include "mangle.h"
#include "type-layout.h"
#include "visitor.h"
@@ -80,6 +81,7 @@ struct BoundSubscriptInfo : ExtendedValueInfo
DeclRef<SubscriptDecl> declRef;
IRType* type;
List<IRValue*> args;
+ UInt genericArgCount;
};
// Some cases of `ExtendedValueInfo` need to
@@ -288,6 +290,11 @@ struct IRGenContext
SharedIRGenContext* shared;
IRBuilder* irBuilder;
+
+ Session* getSession()
+ {
+ return shared->entryPoint->compileRequest->mSession;
+ }
};
LoweredValInfo ensureDecl(
@@ -430,7 +437,9 @@ LoweredValInfo emitCallToDeclRef(
IRType* type,
DeclRef<Decl> funcDeclRef,
UInt argCount,
- IRValue* const* args)
+ IRValue* const* args,
+ // How many of the arguments are generic args?
+ UInt genericArgCount)
{
auto builder = context->irBuilder;
@@ -473,6 +482,7 @@ LoweredValInfo emitCallToDeclRef(
boundSubscript->declRef = subscriptDeclRef;
boundSubscript->type = type;
boundSubscript->args.AddRange(args, argCount);
+ boundSubscript->genericArgCount = genericArgCount;
context->shared->extValues.Add(boundSubscript);
@@ -488,6 +498,12 @@ LoweredValInfo emitCallToDeclRef(
auto funcDecl = funcDeclRef.getDecl();
if(auto intrinsicOpModifier = funcDecl->FindModifier<IntrinsicOpModifier>())
{
+ // We don't want to include generic arguments when calling an
+ // intrinsic for now, because we assume that they already
+ // handle the parameterization internally.
+ args += genericArgCount;
+ argCount -= genericArgCount;
+
auto op = getIntrinsicOp(funcDecl, intrinsicOpModifier);
if (Int(op) < 0)
@@ -544,6 +560,14 @@ LoweredValInfo emitCallToDeclRef(
// HACK: we know all constructors are builtins for now,
// so we need to emit them as a call to the corresponding
// builtin operation.
+ //
+ // TODO: these should all either be intrinsic operations,
+ // or calls to library functions.
+
+ // Skip the generic arguments, so they don't screw up
+ // other logic.
+ args += genericArgCount;
+ argCount -= genericArgCount;
return LoweredValInfo::simple(builder->emitConstructorInst(type, argCount, args));
}
@@ -556,9 +580,10 @@ LoweredValInfo emitCallToDeclRef(
IRGenContext* context,
IRType* type,
DeclRef<Decl> funcDeclRef,
- List<IRValue*> const& args)
+ List<IRValue*> const& args,
+ UInt genericArgCount)
{
- return emitCallToDeclRef(context, type, funcDeclRef, args.Count(), args.Buffer());
+ return emitCallToDeclRef(context, type, funcDeclRef, args.Count(), args.Buffer(), genericArgCount);
}
IRValue* getSimpleVal(IRGenContext* context, LoweredValInfo lowered)
@@ -587,7 +612,8 @@ top:
context,
boundSubscriptInfo->type,
getter,
- boundSubscriptInfo->args);
+ boundSubscriptInfo->args,
+ boundSubscriptInfo->genericArgCount);
goto top;
}
@@ -1096,6 +1122,53 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
}
}
+ // In the context of a call, we need to add the generic
+ // arguments to the call to the list of IR-level arguments
+ // we are going to pass through. We do that by looking
+ // at the specializations attached to the declaration
+ // reference itself.
+ //
+ void addGenericArgs(
+ InvokeExpr* expr,
+ DeclRef<Decl> declRef,
+ List<IRValue*>* ioArgs)
+ {
+ auto subst = declRef.substitutions;
+ if(!subst)
+ return;
+
+ auto genericDecl = subst->genericDecl;
+
+ // If there is an outer layer of generic arguments
+ // (e.g., we are calling a generic method nested
+ // inside a generic type) then we need to add
+ // the outer arguments *before* those from the
+ // inner context.
+ //
+ DeclRef<Decl> outerDeclRef;
+ outerDeclRef.decl = genericDecl;
+ outerDeclRef.substitutions = subst->outer;
+ addGenericArgs(expr, outerDeclRef, ioArgs);
+
+ // Okay, now we can process the argument list at this
+ // level.
+ //
+ // TODO: there could be a sticking point here because
+ // this logic needs to agree with the logic that adds
+ // generic *parameters* to the callee, and right now
+ // we have a mismatch that constraint witnesses are
+ // explicit in the parmaeter list, but not in
+ // declaration references as currently constructed
+ // inside the front-end.
+ //
+ for(auto arg : subst->args)
+ {
+ auto loweredVal = lowerVal(context, arg);
+ addArgs(context, ioArgs, loweredVal);
+ }
+ }
+
+
// After a call to a function with `out` or `in out`
// parameters, we may need to copy data back into
// the l-value locations used for output arguments.
@@ -1233,6 +1306,74 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
}
}
+ struct ResolvedCallInfo
+ {
+ DeclRef<Decl> funcDeclRef;
+ Expr* baseExpr = nullptr;
+ };
+
+ // Try to resolve a the function expression for a call
+ // into a reference to a specific declaration, along
+ // with some contextual information about the declaration
+ // we are calling.
+ bool tryResolveDeclRefForCall(
+ RefPtr<Expr> funcExpr,
+ ResolvedCallInfo* outInfo)
+ {
+ // TODO: unwrap any "identity" expressions that might
+ // be wrapping the callee.
+
+ // First look to see if the expression references a
+ // declaration at all.
+ auto declRefExpr = funcExpr.As<DeclRefExpr>();
+ if(!declRefExpr)
+ return false;
+
+ // A little bit of future proofing here: if we ever
+ // allow higher-order functions, then we might be
+ // calling through a variable/field that has a function
+ // type, but is not itself a function.
+ // In such a case we should be careful to not statically
+ // resolve things.
+ //
+ if(auto callableDecl = dynamic_cast<CallableDecl*>(declRefExpr->declRef.getDecl()))
+ {
+ // Okay, the declaration is directly callable, so we can continue.
+ }
+ else
+ {
+ // The callee declaration isn't itself a callable (it must have
+ // a funciton type, though).
+ return false;
+ }
+
+ // Now we can look at the specific kinds of declaration references,
+ // and try to tease them apart.
+ if (auto memberFuncExpr = funcExpr.As<MemberExpr>())
+ {
+ outInfo->funcDeclRef = memberFuncExpr->declRef;
+ outInfo->baseExpr = memberFuncExpr->BaseExpression;
+ return true;
+ }
+ else if (auto staticMemberFuncExpr = funcExpr.As<StaticMemberExpr>())
+ {
+ outInfo->funcDeclRef = staticMemberFuncExpr->declRef;
+ return true;
+ }
+ else if (auto varExpr = funcExpr.As<VarExpr>())
+ {
+ outInfo->funcDeclRef = varExpr->declRef;
+ return true;
+ }
+ else
+ {
+ // Seems to be a case of declaration-reference we don't know about.
+ SLANG_UNEXPECTED("unknown declaration reference kind");
+ return false;
+ }
+ }
+
+
LoweredValInfo visitInvokeExpr(InvokeExpr* expr)
{
auto type = lowerSimpleType(context, expr->type);
@@ -1262,31 +1403,33 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
List<OutArgumentFixup> argFixups;
auto funcExpr = expr->FunctionExpr;
- if (auto memberFuncExpr = funcExpr.As<MemberExpr>())
+ ResolvedCallInfo resolvedInfo;
+ if( tryResolveDeclRefForCall(funcExpr, &resolvedInfo) )
{
- auto loweredBaseVal = lowerRValueExpr(context, memberFuncExpr->BaseExpression);
- addArgs(context, &irArgs, loweredBaseVal);
-
- auto funcDeclRef = memberFuncExpr->declRef;
+ // In this case we know exaclty what declaration we
+ // are going to call, and so we can resolve things
+ // appropriately.
+ auto funcDeclRef = resolvedInfo.funcDeclRef;
+ auto baseExpr = resolvedInfo.baseExpr;
+
+ // First come any generic arguments:
+ addGenericArgs(expr, funcDeclRef, &irArgs);
+ UInt genericArgCount = irArgs.Count();
+
+ // Next comes the `this` argument if we are calling
+ // a member function:
+ if( baseExpr )
+ {
+ auto loweredBaseVal = lowerRValueExpr(context, baseExpr);
+ addArgs(context, &irArgs, loweredBaseVal);
+ }
+ // Then we have the "direct" arguments to the call.
+ // These may include `out` and `inout` arguments that
+ // require "fixup" work on the other side.
+ //
addDirectCallArgs(expr, funcDeclRef, &irArgs, &argFixups);
- auto result = emitCallToDeclRef(context, type, funcDeclRef, irArgs);
- applyOutArgumentFixups(argFixups);
- return result;
- }
- else if (auto staticMemberFuncExpr = funcExpr.As<StaticMemberExpr>())
- {
- auto funcDeclRef = staticMemberFuncExpr->declRef;
- addDirectCallArgs(expr, funcDeclRef, &irArgs, &argFixups);
- auto result = emitCallToDeclRef(context, type, funcDeclRef, irArgs);
- applyOutArgumentFixups(argFixups);
- return result;
- }
- else if (auto varExpr = funcExpr.As<VarExpr>())
- {
- auto funcDeclRef = varExpr->declRef;
- addDirectCallArgs(expr, funcDeclRef, &irArgs, &argFixups);
- auto result = emitCallToDeclRef(context, type, funcDeclRef, irArgs);
+ auto result = emitCallToDeclRef(context, type, funcDeclRef, irArgs, genericArgCount);
applyOutArgumentFixups(argFixups);
return result;
}
@@ -1925,7 +2068,8 @@ top:
context,
builder->getVoidType(),
setterDeclRef,
- allArgs);
+ allArgs,
+ subscriptInfo->genericArgCount);
return;
}
@@ -1966,6 +2110,25 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
SLANG_UNIMPLEMENTED_X("decl catch-all");
}
+ LoweredValInfo visitDeclGroup(DeclGroup* declGroup)
+ {
+ // To lowere a group of declarations, we just
+ // lower each one individually.
+ //
+ for (auto decl : declGroup->decls)
+ {
+ // Note: I am directly invoking `dispatch` here,
+ // instead of `ensureDecl` just to try and
+ // make sure that we don't accidentally
+ // emit things to an outer context.
+ //
+ // TODO: make sure that can't happen anyway.
+ dispatch(decl);
+ }
+
+ return LoweredValInfo();
+ }
+
LoweredValInfo visitSubscriptDecl(SubscriptDecl* decl)
{
// A subscript operation may encompass one or more
@@ -2096,98 +2259,451 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
return LoweredValInfo::simple(irStruct);
}
- LoweredValInfo visitFunctionDeclBase(FunctionDeclBase* decl)
+ // Sometimes we need to refer to a declaration the way that it would be specialized
+ // inside the context where it is declared (e.g., with generic parameters filled in
+ // using their archetypes).
+ //
+ RefPtr<Substitutions> createDefaultSubstitutions(Decl* decl)
{
- IRBuilder subBuilderStorage = *getBuilder();
- IRBuilder* subBuilder = &subBuilderStorage;
+ auto dd = decl->ParentDecl;
+ while( dd )
+ {
+ if( auto genericDecl = dynamic_cast<GenericDecl*>(dd) )
+ {
+ auto session = context->getSession();
- // need to create an IR function here
+ RefPtr<Substitutions> subst = new Substitutions();
+ subst->genericDecl = genericDecl;
+ subst->outer = createDefaultSubstitutions(genericDecl);
- IRFunc* irFunc = subBuilder->createFunc();
- subBuilder->parentInst = irFunc;
+ for( auto mm : genericDecl->Members )
+ {
+ if( auto genericTypeParamDecl = mm.As<GenericTypeParamDecl>() )
+ {
+ subst->args.Add(DeclRefType::Create(session, makeDeclRef(genericTypeParamDecl.Ptr())));
+ }
+ else if( auto genericValueParamDecl = mm.As<GenericValueParamDecl>() )
+ {
+ subst->args.Add(new GenericParamIntVal(makeDeclRef(genericValueParamDecl.Ptr())));
+ }
+ }
- IRBlock* entryBlock = subBuilder->emitBlock();
- subBuilder->parentInst = entryBlock;
+ return subst;
+ }
+ dd = dd->ParentDecl;
+ }
+ return nullptr;
+ }
+ DeclRef<Decl> createDefaultSpecializedDeclRefImpl(Decl* decl)
+ {
+ DeclRef<Decl> declRef;
+ declRef.decl = decl;
+ declRef.substitutions = createDefaultSubstitutions(decl);
+ return declRef;
+ }
+ //
+ // The client should actually call the templated wrapper, to preserve type information.
+ template<typename D>
+ DeclRef<D> createDefaultSpecializedDeclRef(D* decl)
+ {
+ return createDefaultSpecializedDeclRefImpl(decl).As<D>();
+ }
- IRGenContext subContextStorage = *context;
- IRGenContext* subContext = &subContextStorage;
- subContext->irBuilder = subBuilder;
- // set up sub context for generating our new function
+ // When lowering something callable (most commonly a function declaration),
+ // we need to construct an appropriate parameter list for the IR function
+ // that folds in any contributions from both the declaration itself *and*
+ // its parent declaration(s).
+ //
+ // For example, given code like:
+ //
+ // struct Foo { int bar(float y) { ... } };
+ //
+ // we need to generate IR-level code something like:
+ //
+ // func Foo_bar(Foo this, float y) -> int;
+ //
+ // that is, the `this` parameter has become explicit.
+ //
+ // The same applies to generic parameters, and these
+ // should apply even if the nested declaration is `static`:
+ //
+ // struct Foo<T> { static int bar(T y) { ... } };
+ //
+ // becomes:
+ //
+ // func Foo_bar<T>(T y) -> int;
+ //
+ // In order to implement this, we are going to do a recursive
+ // walk over a declaration and its parents, collecting separate
+ // lists of ordinary and generic parameters that will need
+ // to be included in the final declaration's parameter list.
+ //
+ // When doing code generation for an ordinary value parameter,
+ // we mostly care about its type, and then also its "direction"
+ // (`in`, `out`, `in out`). We sometimes need acess to the
+ // original declaration so that we can inspect it for meta-data,
+ // but in some cases there is no such declaration (e.g., a `this`
+ // parameter doesn't get an explicit declaration in the AST).
+ // To handle this we break out the relevant data into derived
+ // structures:
+ //
+ enum ParameterDirection
+ {
+ kParameterDirection_In,
+ kParameterDirection_Out,
+ kParameterDirection_InOut,
+ };
+ struct ParameterInfo
+ {
+ Type* type;
+ ParameterDirection direction;
+ VarDeclBase* decl;
+ };
+ //
+ // We need a way to compute the appropriate `ParameterDirection` for a
+ // declared parameter:
+ //
+ ParameterDirection getParameterDirection(VarDeclBase* paramDecl)
+ {
+ if( paramDecl->HasModifier<InOutModifier>() )
+ {
+ // The AST specified `inout`:
+ return kParameterDirection_InOut;
+ }
+ if (paramDecl->HasModifier<OutModifier>())
+ {
+ // We saw an `out` modifier, so now we need
+ // to check if there was a paired `in`.
+ if(paramDecl->HasModifier<InModifier>())
+ return kParameterDirection_InOut;
+ else
+ return kParameterDirection_Out;
+ }
+ else
+ {
+ // No direction modifier, or just `in`:
+ return kParameterDirection_In;
+ }
+ }
+ // We need a way to be able to create a `ParameterInfo` given the declaration
+ // of a parameter:
+ //
+ ParameterInfo getParameterInfo(VarDeclBase* paramDecl)
+ {
+ ParameterInfo info;
+ info.type = paramDecl->getType();
+ info.decl = paramDecl;
+ info.direction = getParameterDirection(paramDecl);
+ return info;
+ }
+ //
+
+ // Here's the declaration for the type to hold the lists:
+ struct ParameterLists
+ {
+ List<ParameterInfo> params;
+ List<Decl*> genericParams;
+ };
+ //
+ // Because there might be a `static` declaration somewhere
+ // along the lines, we need to be careful to prohibit adding
+ // non-generic parameters in some cases.
+ enum ParameterListCollectMode
+ {
+ // Collect everything: ordinary and generic parameters.
+ kParameterListCollectMode_Default,
+
+
+ // Only collect generic parameters.
+ kParameterListCollectMode_Static,
+ };
+ //
+ // We also need to be able to detect whether a declaration is
+ // either explicitly or implicitly treated as `static`:
+ bool isMemberDeclarationEffectivelyStatic(
+ Decl* decl,
+ ContainerDecl* parentDecl)
+ {
+ // Anything explicitly marked `static` counts.
+ //
+ // There is a subtle detail here with a global-scope `static`
+ // variable not really meaning `static` in the same way, but
+ // it doesn't matter because the module shouldn't introduce
+ // any parameters we care about.
+ if(decl->HasModifier<HLSLStaticModifier>())
+ return true;
+
+ // Next we need to deal with cases where a declaration is
+ // effectively `static` even if the language doesn't make
+ // the user say so. Most languages make the default assumption
+ // that nested types are `static` even if they don't say
+ // so (Java is an exception here, perhaps due to some
+ // includence from the Scandanavian OOP tradition).
+ if(dynamic_cast<AggTypeDecl*>(decl))
+ return true;
+
+ // Things nested inside functions may have dependencies
+ // on values from the enclosing scope, but this needs to
+ // be dealt with via "capture" so they are also effectively
+ // `static`
+ if(dynamic_cast<FunctionDeclBase*>(parentDecl))
+ return true;
+
+ return false;
+ }
+ // We also need to be able to detect whether a declaration is
+ // either explicitly or implicitly treated as `static`:
+ ParameterListCollectMode getModeForCollectingParentParameters(
+ Decl* decl,
+ ContainerDecl* parentDecl)
+ {
+ // If we have a `static` parameter, then it is obvious
+ // that we should use the `static` mode
+ if(isMemberDeclarationEffectivelyStatic(decl, parentDecl))
+ return kParameterListCollectMode_Static;
+
+ // Otherwise, let's default to collecting everything
+ return kParameterListCollectMode_Default;
+ }
+ //
+ // When dealing with a member function, we need to be able to add the `this`
+ // parameter for the enclosing type:
+ //
+ void addThisParameter(
+ Type* type,
+ ParameterLists* ioParameterLists)
+ {
+ // For now we make any `this` parameter default to `in`. Eventually
+ // we should add a way for the user to opt in to mutability of `this`
+ // in cases where they really want it.
+ //
+ // Note: an alternative here might be to have the built-in types like
+ // `Texture2D` actually use `class` declarations and say that the
+ // `this` parameter for a class type is always `in`, while `struct`
+ // types can default to `in out`.
+ ParameterDirection direction = kParameterDirection_In;
+
+ ParameterInfo info;
+ info.type = type;
+ info.decl = nullptr;
+ info.direction = direction;
+
+ ioParameterLists->params.Add(info);
+ }
+ void addThisParameter(
+ AggTypeDecl* typeDecl,
+ ParameterLists* ioParameterLists)
+ {
+ // We need to construct an appopriate declaration-reference
+ // for the type declaration we were given. In particular,
+ // we need to specialize it for any generic parameters
+ // that are in scope here.
+ auto declRef = createDefaultSpecializedDeclRef(typeDecl);
+ auto type = DeclRefType::Create(context->getSession(), declRef);
+ addThisParameter(
+ type,
+ ioParameterLists);
+ }
+ //
+ // And here is our function that will do the recursive walk:
+ void collectParameterLists(
+ Decl* decl,
+ ParameterLists* ioParameterLists,
+ ParameterListCollectMode mode)
+ {
+ // The parameters introduced by any "parent" declarations
+ // will need to come first, so we'll deal with that
+ // logic here.
+ if( auto parentDecl = decl->ParentDecl )
+ {
+ // Compute the mode to use when collecting parameters from
+ // the outer declaration. The most important question here
+ // is whether parameters of the outer declaration should
+ // also count as parameters of the inner declaration.
+ ParameterListCollectMode innerMode = getModeForCollectingParentParameters(decl, parentDecl);
+
+ // Don't down-grade our `static`-ness along the chain.
+ if(innerMode < mode)
+ innerMode = mode;
+
+ // Now collect any parameters from the parent declaration itself
+ collectParameterLists(parentDecl, ioParameterLists, innerMode);
+
+ // We also need to consider whether the inner declaration needs to have a `this`
+ // parameter corresponding to the outer declaration.
+ if( innerMode != kParameterListCollectMode_Static )
+ {
+ if( auto aggTypeDecl = dynamic_cast<AggTypeDecl*>(parentDecl) )
+ {
+ addThisParameter(aggTypeDecl, ioParameterLists);
+ }
+ else if( auto extensionDecl = dynamic_cast<ExtensionDecl*>(parentDecl) )
+ {
+ addThisParameter(extensionDecl->targetType, ioParameterLists);
+ }
+ }
+ }
+
+ // Once we've added any parameters based on parent declarations,
+ // we can see if this declaration itself introduces parameters.
+ //
+ if( auto callableDecl = dynamic_cast<CallableDecl*>(decl) )
+ {
+ // Don't collect parameters from the outer scope if
+ // we are in a `static` context.
+ if( mode == kParameterListCollectMode_Default )
+ {
+ for( auto paramDecl : callableDecl->GetParameters() )
+ {
+ ioParameterLists->params.Add(getParameterInfo(paramDecl));
+ }
+ }
+ }
+ else if( auto genericDecl = dynamic_cast<GenericDecl*>(decl) )
+ {
+ for( auto memberDecl : genericDecl->Members )
+ {
+ if( auto genericTypeParamDecl = memberDecl.As<GenericTypeParamDecl>() )
+ {
+ ioParameterLists->genericParams.Add(genericTypeParamDecl);
+ }
+ else if( auto genericValueParamDecl = memberDecl.As<GenericValueParamDecl>() )
+ {
+ ioParameterLists->genericParams.Add(genericValueParamDecl);
+ }
+ else if( auto genericConstraintDel = memberDecl.As<GenericTypeConstraintDecl>() )
+ {
+ // When lowering to the IR we need to reify the constraints on
+ // a generic parameter as concrete parameters of their own.
+ // These parameter will usually be satisfied by passing a "witness"
+ // as the argument to correspond to the parameter.
+ //
+ // TODO: it is possible that all witness parameters should come
+ // after the other generic parameters, and thus should be collected
+ // in a third list.
+ //
+ ioParameterLists->genericParams.Add(genericConstraintDel);
+ }
+ }
+ }
+
+ }
+
+ void trySetMangledName(
+ IRInst* inst,
+ Decl* decl)
+ {
+ // We want to generate a mangled name for the given declaration and attach
+ // it to the instruction.
+ //
+ // TODO: we probably want to start be doing an early-exit in cases
+ // where it doesn't make sense to attach a mangled name (e.g., because
+ // the declaration in question shouldn't have linkage).
+ //
+
+ String mangledName = getMangledName(decl);
+
+ auto decoration = getBuilder()->addDecoration<IRMangledNameDecoration>(inst);
+ decoration->mangledName = mangledName;
+ }
+
+
+ LoweredValInfo visitFunctionDeclBase(FunctionDeclBase* decl)
+ {
+ // Collect the parameter lists we will use for our new function.
+ ParameterLists parameterLists;
+ collectParameterLists(decl, &parameterLists, kParameterListCollectMode_Default);
+
+ // TODO: if there are any generic parameters in the collected list, then
+ // we need to output an IR function with generic parameters (or a generic
+ // with a nested function... the exact representation is still TBD).
- CallableDecl* declForParameters = decl;
+ // In most cases the return type for a declaration can be read off the declaration
+ // itself, but things get a bit more complicated when we have to deal with
+ // accessors for subscript declarations (and eventually for properties).
+ //
+ // We compute a declaration to use for looking up the return type here:
CallableDecl* declForReturnType = decl;
if (auto accessorDecl = dynamic_cast<AccessorDecl*>(decl))
{
+ // We are some kind of accessor, so the parent declaration should
+ // know the correct return type to expose.
+ //
auto parentDecl = accessorDecl->ParentDecl;
if (auto subscriptDecl = dynamic_cast<SubscriptDecl*>(parentDecl))
{
- declForParameters = subscriptDecl;
declForReturnType = subscriptDecl;
}
}
- List<IRType*> paramTypes;
+ IRBuilder subBuilderStorage = *getBuilder();
+ IRBuilder* subBuilder = &subBuilderStorage;
- for( auto paramDecl : declForParameters->GetParameters() )
- {
- IRType* irParamType = lowerSimpleType(context, paramDecl->getType());
+ IRGenContext subContextStorage = *context;
+ IRGenContext* subContext = &subContextStorage;
+ subContext->irBuilder = subBuilder;
- LoweredValInfo paramVal;
+ // need to create an IR function here
- if (paramDecl->HasModifier<OutModifier>()
- || paramDecl->HasModifier<InOutModifier>())
- {
- // The parameter is being used for input/output purposes,
- // so it will lower to an actual parameter with a pointer type.
- //
- // TODO: Is this the best representation we can use?
+ IRFunc* irFunc = subBuilder->createFunc();
+ subBuilder->parentInst = irFunc;
- auto irPtrType = subBuilder->getPtrType(irParamType);
- paramTypes.Add(irPtrType);
+ trySetMangledName(irFunc, decl);
+
+ List<IRType*> paramTypes;
- IRParam* irParamPtr = subBuilder->emitParam(irPtrType);
- subBuilder->addHighLevelDeclDecoration(irParamPtr, paramDecl);
+ // We first need to walk the generic parameters (if any)
+ // because these will influence the declared type of
+ // the function.
+ UInt genericParamCounter = 0;
+ for( auto genericParamDecl : parameterLists.genericParams )
+ {
+ UInt genericParamIndex = genericParamCounter++;
+ if( auto genericTypeParamDecl = dynamic_cast<GenericTypeParamDecl*>(genericParamDecl) )
+ {
+ // In the logical type for the function, a generic
+ // type parameter will be represented as a parameter of type `Type`
- paramVal = LoweredValInfo::ptr(irParamPtr);
+ IRType* irTypeType = context->irBuilder->getTypeType();
+ paramTypes.Add(irTypeType);
- // TODO: We might want to copy the pointed-to value into
- // a temporary at the start of the function, and then copy
- // back out at the end, so that we don't have to worry
- // about things like aliasing in the function body.
- //
- // For now we will just use the storage that was passed
- // in by the caller, knowing that our current lowering
- // at call sites will guarantee a fresh/unique location.
+ // Anywhere else in the parameter type list where this type parameter
+ // is referenced, we'll need to substitute in a reference
+ // to the appropriate generic parameter position.
+
+ IRType* irParameterType = context->irBuilder->getGenericParameterType(genericParamIndex);
+ LoweredValInfo LoweredValInfo = LoweredValInfo::simple(irParameterType);
+ subContext->shared->declValues[makeDeclRef(genericTypeParamDecl)] = LoweredValInfo;
}
else
{
- // Simple case of a by-value input parameter.
- // But note that HLSL allows an input parameter
- // to be used as a local variable inside of a
- // function body, so we need to introduce a temporary
- // and then copy over to it...
- //
- // TODO: we could skip this step if we knew
- // the parameter was marked `const` or similar.
-
- paramTypes.Add(irParamType);
+ // TODO: handle the other cases here.
+ SLANG_UNEXPECTED("generic parameter kind");
+ }
+ }
- IRParam* irParam = subBuilder->emitParam(irParamType);
- subBuilder->addHighLevelDeclDecoration(irParam, paramDecl);
- paramVal = LoweredValInfo::simple(irParam);
+ for( auto paramInfo : parameterLists.params )
+ {
+ IRType* irParamType = lowerSimpleType(context, paramInfo.type);
- auto irLocal = subBuilder->emitVar(irParamType);
- auto localVal = LoweredValInfo::ptr(irLocal);
+ switch( paramInfo.direction )
+ {
+ case kParameterDirection_In:
+ // Simple case of a by-value input parameter.
+ paramTypes.Add(irParamType);
+ break;
- assign(subContext, localVal, paramVal);
+ default:
+ // The parameter is being used for input/output purposes,
+ // so it will lower to an actual parameter with a pointer type.
+ //
+ // TODO: Is this the best representation we can use?
- paramVal = localVal;
+ auto irPtrType = subBuilder->getPtrType(irParamType);
+ paramTypes.Add(irPtrType);
}
-
- DeclRef<ParamDecl> paramDeclRef = makeDeclRef(paramDecl.Ptr());
- subContext->shared->declValues.Add(paramDeclRef, paramVal);
}
auto irResultType = lowerSimpleType(context, declForReturnType->ReturnType);
@@ -2218,20 +2734,111 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
irResultType);
irFunc->type.init(irFunc, irFuncType);
- lowerStmt(subContext, decl->Body);
+ if (!decl->Body)
+ {
+ // This is a function declaration without a body.
+ // In Slang we currently try not to support forward declarations
+ // (although we might have to give in eventually), so the
+ // only case where this arises is for a function that
+ // needs to be imported from another module.
+
+ // TODO: we may need to attach something to the declaration,
+ // so that later passes don't get confused by it not having
+ // a body.
- // We need to carefully add a terminator instruction to the end
- // of the body, in case the user didn't do so.
- if (!isTerminatorInst(subContext->irBuilder->parentInst->lastChild))
+ }
+ else
{
- if (irResultType->op == kIROp_VoidType)
+ // This is a function definition, so we need to actually
+ // construct IR for the body...
+ IRBlock* entryBlock = subBuilder->emitBlock();
+ subBuilder->parentInst = entryBlock;
+
+ UInt paramTypeIndex = 0;
+ for( auto paramInfo : parameterLists.params )
{
- subContext->irBuilder->emitReturn();
+ auto irParamType = paramTypes[paramTypeIndex++];
+
+ LoweredValInfo paramVal;
+
+ switch( paramInfo.direction )
+ {
+ default:
+ {
+ // The parameter is being used for input/output purposes,
+ // so it will lower to an actual parameter with a pointer type.
+ //
+ // TODO: Is this the best representation we can use?
+
+ auto irPtrType = (IRPtrType*)irParamType;
+
+ IRParam* irParamPtr = subBuilder->emitParam(irPtrType);
+ if(auto paramDecl = paramInfo.decl)
+ subBuilder->addHighLevelDeclDecoration(irParamPtr, paramDecl);
+
+ paramVal = LoweredValInfo::ptr(irParamPtr);
+
+ // TODO: We might want to copy the pointed-to value into
+ // a temporary at the start of the function, and then copy
+ // back out at the end, so that we don't have to worry
+ // about things like aliasing in the function body.
+ //
+ // For now we will just use the storage that was passed
+ // in by the caller, knowing that our current lowering
+ // at call sites will guarantee a fresh/unique location.
+ }
+ break;
+
+ case kParameterDirection_In:
+ {
+ // Simple case of a by-value input parameter.
+ // But note that HLSL allows an input parameter
+ // to be used as a local variable inside of a
+ // function body, so we need to introduce a temporary
+ // and then copy over to it...
+ //
+ // TODO: we could skip this step if we knew
+ // the parameter was marked `const` or similar.
+
+ paramTypes.Add(irParamType);
+
+ IRParam* irParam = subBuilder->emitParam(irParamType);
+ if(auto paramDecl = paramInfo.decl)
+ subBuilder->addHighLevelDeclDecoration(irParam, paramDecl);
+ paramVal = LoweredValInfo::simple(irParam);
+
+ auto irLocal = subBuilder->emitVar(irParamType);
+ auto localVal = LoweredValInfo::ptr(irLocal);
+
+ assign(subContext, localVal, paramVal);
+
+ paramVal = localVal;
+ }
+ break;
+ }
+
+ if( auto paramDecl = paramInfo.decl )
+ {
+ DeclRef<VarDeclBase> paramDeclRef = makeDeclRef(paramDecl);
+ subContext->shared->declValues.Add(paramDeclRef, paramVal);
+ }
}
- else
+
+ lowerStmt(subContext, decl->Body);
+
+ // We need to carefully add a terminator instruction to the end
+ // of the body, in case the user didn't do so.
+ if (!isTerminatorInst(subContext->irBuilder->parentInst->lastChild))
{
- SLANG_UNEXPECTED("Needed a return here");
- subContext->irBuilder->emitReturn();
+ if (irResultType->op == kIROp_VoidType)
+ {
+ subContext->irBuilder->emitReturn();
+ }
+ else
+ {
+ SLANG_UNEXPECTED("Needed a return here");
+ subContext->irBuilder->emitReturn();
+ }
}
}
@@ -2341,6 +2948,9 @@ static void lowerEntryPointToIR(
auto entryPointDecoration = builder->addDecoration<IREntryPointDecoration>(irFunc);
entryPointDecoration->profile = profile;
+ // Attach layout information here.
+ builder->addLayoutDecoration(irFunc, entryPointLayout);
+
// Next, we need to start attaching the meta-data that is
// required based on the particular stage we are targetting:
switch (stage)