summaryrefslogtreecommitdiff
path: root/source/slang/lower-to-ir.cpp
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2017-10-18 11:08:47 -0700
committerGitHub <noreply@github.com>2017-10-18 11:08:47 -0700
commita12480fe49d5ba7c0a9c2ac63363dc76b599ddbd (patch)
tree6344dec2a432f2f5cb1cdd981d0aafe21ea6a443 /source/slang/lower-to-ir.cpp
parentf12c2552b3f494cbc8245edb90b32b93ca8a1539 (diff)
Work on IR-based cross-compilation (#222)
There are two big changes here: - Add logic during the initial IR cloning pass for an entry point + target that tries to pick the best possible version of any target-overloaded function. This allows us to pick the intrinsic version of `saturate()` when compiling for HLSL output, but then pick the non-intrinsic version (that is implemented in terms of `clamp()`) when targetting GLSL. - Add an initial specialization pass that tries to deal with generics. This required some fixing work to IR generation, so that we correctly generate explicit operations to specialize a generic for specific types (this is currently implemented as a `specialize` instruction that takes the generic to specialize plus a declaration-reference that represents the specialized form). With that work in place, we can scan for `specialize` instructions inside of non-generic functions, and use them to trigger generation of specialized code. We rely on the name-mangling scheme to help us find pre-existing specializations when possible. There are also a bunch of cleanups encountered along the way: - Don't use the explicit `layout(offset=...)` for uniforms, because it isn't supported by all current drivers. For now we will just assume that our layout rules compute the same values that the driver would for un-marked-up code. We can come back later and try to implement a workaround in the cases where this doesn't apply (e.g., by re-running the layout logic as part of emission, and dropping layout modifiers from variables that don't need explicit layout). - Fix some issues in IR dump printing so that we print function declarations more nicely. - Testing: print out failing pixel when image-diff fails
Diffstat (limited to 'source/slang/lower-to-ir.cpp')
-rw-r--r--source/slang/lower-to-ir.cpp184
1 files changed, 105 insertions, 79 deletions
diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp
index d4551421f..a3d67b670 100644
--- a/source/slang/lower-to-ir.cpp
+++ b/source/slang/lower-to-ir.cpp
@@ -270,7 +270,7 @@ struct SharedIRGenContext
{
CompileRequest* compileRequest;
- Dictionary<DeclRef<Decl>, LoweredValInfo> declValues;
+ Dictionary<Decl*, LoweredValInfo> declValues;
// Arrays we keep around strictly for memory-management purposes:
@@ -294,9 +294,16 @@ struct IRGenContext
}
};
+// Ensure that a version of the given declaration has been emitted to the IR
LoweredValInfo ensureDecl(
- IRGenContext* context,
- DeclRef<Decl> const& declRef);
+ IRGenContext* context,
+ Decl* decl);
+
+// Emit code as needed to construct a reference to the given declaration with
+// any needed specializations in place.
+LoweredValInfo emitDeclRef(
+ IRGenContext* context,
+ DeclRef<Decl> declRef);
IRValue* getSimpleVal(IRGenContext* context, LoweredValInfo lowered);
@@ -564,7 +571,7 @@ LoweredValInfo emitCallToDeclRef(
}
// Fallback case is to emit an actual call.
- LoweredValInfo funcVal = ensureDecl(context, funcDeclRef);
+ LoweredValInfo funcVal = emitDeclRef(context, funcDeclRef);
return emitCallToVal(context, type, funcVal, argCount, args);
}
@@ -750,6 +757,7 @@ RefPtr<IRFuncType> getFuncType(
IRType* resultType)
{
RefPtr<FuncType> funcType = new FuncType();
+ funcType->setSession(context->getSession());
funcType->resultType = resultType;
for (UInt pp = 0; pp < paramCount; ++pp)
{
@@ -810,43 +818,8 @@ struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, Lower
LoweredTypeInfo visitDeclRefType(DeclRefType* type)
{
-#if 1
// TODO: is there actually anything to be done at this point?
return LoweredTypeInfo(type);
-#else
- // We need to detect builtin/intrinsic types here, since they should map to custom modifiers
- // We need to catch builtin/intrinsic types here
- if( auto intrinsicTypeMod = type->declRef.getDecl()->FindModifier<IntrinsicTypeModifier>() )
- {
- auto builder = getBuilder();
- auto intType = getIntType(context);
- //
- List<IRValue*> irArgs;
- for( auto val : intrinsicTypeMod->irOperands )
- {
- irArgs.Add(builder->getIntValue(intType, val));
- }
-
- addGenericArgs(&irArgs, type->declRef);
-
- auto irType = getBuilder()->getIntrinsicType(IROp(intrinsicTypeMod->irOp), irArgs.Count(), irArgs.Buffer());
- return LoweredTypeInfo(irType);
- }
-
- // Catch-all for user-defined type references
- LoweredValInfo loweredDeclRef = ensureDecl(context, type->declRef);
-
- // TODO: make sure that the value is actually a type...
-
- switch (loweredDeclRef.flavor)
- {
- case LoweredValInfo::Flavor::Simple:
- return LoweredTypeInfo((IRType*)loweredDeclRef.val);
-
- default:
- SLANG_UNIMPLEMENTED_X("type lowering");
- }
-#endif
}
LoweredTypeInfo visitBasicExpressionType(BasicExpressionType* type)
@@ -956,7 +929,7 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
LoweredValInfo visitVarExpr(VarExpr* expr)
{
- LoweredValInfo info = ensureDecl(context, expr->declRef);
+ LoweredValInfo info = emitDeclRef(context, expr->declRef);
return info;
}
@@ -1431,7 +1404,7 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
LoweredValInfo visitStaticMemberExpr(StaticMemberExpr* expr)
{
- return ensureDecl(context, expr->declRef);
+ return emitDeclRef(context, expr->declRef);
}
LoweredValInfo visitSelectExpr(SelectExpr* expr)
@@ -2028,7 +2001,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
if (accessor->HasModifier<IntrinsicOpModifier>())
continue;
- ensureDecl(context, makeDeclRef(accessor.Ptr()));
+ ensureDecl(context, accessor);
}
// The subscript declaration itself won't correspond
@@ -2561,8 +2534,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
irFunc->mangledName = mangledName;
}
-
- LoweredValInfo visitFunctionDeclBase(FunctionDeclBase* decl)
+ LoweredValInfo lowerFuncDecl(FunctionDeclBase* decl)
{
// Collect the parameter lists we will use for our new function.
ParameterLists parameterLists;
@@ -2610,35 +2582,14 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
// 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 )
- {
- irFunc->genericParams.Add(genericParamDecl);
-
-#if 0
- 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`
-
- IRType* irTypeType = context->irBuilder->getTypeType();
- paramTypes.Add(irTypeType);
-
- // 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::type(irParameterType);
- subContext->shared->declValues[makeDeclRef(genericTypeParamDecl)] = LoweredValInfo;
- }
- else
+ for(auto pp = decl->ParentDecl; pp; pp = pp->ParentDecl)
+ {
+ if(auto genericAncestor = dynamic_cast<GenericDecl*>(pp))
{
- // TODO: handle the other cases here.
- SLANG_UNEXPECTED("generic parameter kind");
+ irFunc->genericDecl = genericAncestor;
+ break;
}
-#endif
}
for( auto paramInfo : parameterLists.params )
@@ -2809,6 +2760,18 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
getBuilder()->addHighLevelDeclDecoration(irFunc, decl);
+ // If this declaration was marked as being an intrinsic for a particular
+ // target, then we should reflect that here.
+ for( auto targetMod : decl->GetModifiersOfType<SpecializedForTargetModifier>() )
+ {
+ // `targetMod` indicates that this particular declaration represents
+ // a specialized definition of the particular function for the given
+ // target, and we need to reflect that at the IR level.
+
+ auto decoration = getBuilder()->addDecoration<IRTargetDecoration>(irFunc);
+ decoration->targetName = targetMod->targetToken.Content;
+ }
+
// For convenience, ensure that any additional global
// values that were emitted while outputting the function
// body appear before the function itself in the list
@@ -2817,6 +2780,43 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
return LoweredValInfo::simple(irFunc);
}
+
+
+ LoweredValInfo visitFunctionDeclBase(FunctionDeclBase* decl)
+ {
+ // A function declaration may have multiple, target-specific
+ // overloads, and we need to emit an IR version of each of these.
+
+ // The front end will form a linked list of declaratiosn with
+ // the same signature, whenever there is any kind of redeclaration.
+ // We will look to see if that linked list has been formed.
+ auto primaryDecl = decl->primaryDecl;
+
+ if (!primaryDecl)
+ {
+ // If there is no linked list then we are in the ordinary
+ // case with a single declaration, and no special handling
+ // is needed.
+ return lowerFuncDecl(decl);
+ }
+
+ // Otherwise, we need to walk the linked list of declarations
+ // and make sure to emit IR code for any targets that need it.
+
+ // TODO: Need to be careful about how this is approached,
+ // to avoid emitting a bunch of extra definitions in the IR.
+
+ auto primaryFuncDecl = dynamic_cast<FunctionDeclBase*>(primaryDecl);
+ assert(primaryFuncDecl);
+ LoweredValInfo result = lowerFuncDecl(primaryFuncDecl);
+ for (auto dd = primaryDecl->nextDecl; dd; dd = dd->nextDecl)
+ {
+ auto funcDecl = dynamic_cast<FunctionDeclBase*>(dd);
+ assert(funcDecl);
+ lowerFuncDecl(funcDecl);
+ }
+ return result;
+ }
};
LoweredValInfo lowerDecl(
@@ -2828,20 +2828,17 @@ LoweredValInfo lowerDecl(
return visitor.dispatch(decl);
}
+// Ensure that a version of the given declaration has been emitted to the IR
LoweredValInfo ensureDecl(
- IRGenContext* context,
- DeclRef<Decl> const& declRef)
+ IRGenContext* context,
+ Decl* decl)
{
auto shared = context->shared;
LoweredValInfo result;
- if(shared->declValues.TryGetValue(declRef, result))
+ if(shared->declValues.TryGetValue(decl, result))
return result;
- // TODO: this is where we need to apply any specializations
- // from the declaration reference, so that they can be
- // applied correctly to the declaration itself...
-
IRBuilder subIRBuilder;
subIRBuilder.shared = context->irBuilder->shared;
@@ -2849,13 +2846,42 @@ LoweredValInfo ensureDecl(
subContext.irBuilder = &subIRBuilder;
- result = lowerDecl(&subContext, declRef.getDecl());
+ result = lowerDecl(&subContext, decl);
- shared->declValues[declRef] = result;
+ shared->declValues[decl] = result;
return result;
}
+LoweredValInfo emitDeclRef(
+ IRGenContext* context,
+ DeclRef<Decl> declRef)
+{
+ // First we need to construct an IR value representing the
+ // unspecialized declaration.
+ LoweredValInfo loweredDecl = ensureDecl(context, declRef.getDecl());
+
+ // If this declaration reference doesn't involve any specializations,
+ // then we are done at this point.
+ if(!declRef.substitutions)
+ return loweredDecl;
+
+ auto val = getSimpleVal(context, loweredDecl);
+
+ RefPtr<Type> type;
+ if(auto declType = val->getType())
+ {
+ type = declType->Substitute(declRef.substitutions).As<Type>();
+ }
+
+ // Otherwise, we need to construct a specialization of the
+ // given declaration.
+ return LoweredValInfo::simple(context->irBuilder->emitSpecializeInst(
+ type,
+ val,
+ declRef));
+}
+
static void lowerEntryPointToIR(
IRGenContext* context,
EntryPointRequest* entryPointRequest)