From a12480fe49d5ba7c0a9c2ac63363dc76b599ddbd Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Wed, 18 Oct 2017 11:08:47 -0700 Subject: 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 --- source/slang/lower-to-ir.cpp | 184 ++++++++++++++++++++++++------------------- 1 file changed, 105 insertions(+), 79 deletions(-) (limited to 'source/slang/lower-to-ir.cpp') 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, LoweredValInfo> declValues; + Dictionary 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 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 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 getFuncType( IRType* resultType) { RefPtr funcType = new FuncType(); + funcType->setSession(context->getSession()); funcType->resultType = resultType; for (UInt pp = 0; pp < paramCount; ++pp) { @@ -810,43 +818,8 @@ struct ValLoweringVisitor : ValVisitordeclRef.getDecl()->FindModifier() ) - { - auto builder = getBuilder(); - auto intType = getIntType(context); - // - List 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 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 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 if (accessor->HasModifier()) continue; - ensureDecl(context, makeDeclRef(accessor.Ptr())); + ensureDecl(context, accessor); } // The subscript declaration itself won't correspond @@ -2561,8 +2534,7 @@ struct DeclLoweringVisitor : DeclVisitor 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 // 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(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(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 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() ) + { + // `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(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 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(primaryDecl); + assert(primaryFuncDecl); + LoweredValInfo result = lowerFuncDecl(primaryFuncDecl); + for (auto dd = primaryDecl->nextDecl; dd; dd = dd->nextDecl) + { + auto funcDecl = dynamic_cast(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 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 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; + if(auto declType = val->getType()) + { + type = declType->Substitute(declRef.substitutions).As(); + } + + // 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) -- cgit v1.2.3