From 6cbc3929a54d37bd23cb5efa8e3320ba02f78b2f Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Fri, 31 May 2019 17:20:37 -0400 Subject: Use slang- prefix on slang compiler and core source (#973) * Prefixing source files in source/slang with slang- * Prefix source in source/slang with slang- prefix. * Rename core source files with slang- prefix. * Update project files. * Fix problems from automatic merge. --- source/slang/lower-to-ir.cpp | 6498 ------------------------------------------ 1 file changed, 6498 deletions(-) delete mode 100644 source/slang/lower-to-ir.cpp (limited to 'source/slang/lower-to-ir.cpp') diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp deleted file mode 100644 index a7be244c8..000000000 --- a/source/slang/lower-to-ir.cpp +++ /dev/null @@ -1,6498 +0,0 @@ -// lower.cpp -#include "lower-to-ir.h" - -#include "../../slang.h" - -#include "check.h" -#include "ir.h" -#include "ir-constexpr.h" -#include "ir-insts.h" -#include "ir-missing-return.h" -#include "ir-sccp.h" -#include "ir-ssa.h" -#include "ir-validate.h" -#include "mangle.h" -#include "type-layout.h" -#include "visitor.h" - -namespace Slang -{ - -// This file implements lowering of the Slang AST to a simpler SSA -// intermediate representation. -// -// IR is generated in a context (`IRGenContext`), which tracks the current -// location in the IR where code should be emitted (e.g., what basic -// block to add instructions to). Lowering a statement will emit some -// number of instructions to the context, and possibly change the -// insertion point (because of control flow). -// -// When lowering an expression we have a more interesting challenge, for -// two main reasons: -// -// 1. There might be types that are representible in the AST, but which -// we don't want to support natively in the IR. An example is a `struct` -// type with both ordinary and resource-type members; we might want to -// split values with such a type into distinct values during lowering. -// -// 2. We need to handle the difference between l-value and r-value expressions, -// and in particular the fact that HLSL/Slang supports complicated sorts -// of l-values (e.g., `someVector.zxy` is an l-value, even though it can't -// be represented by a single pointer), and also allows l-values to appear -// in multiple contexts (not just the left-hand side of assignment, but -// also as an argument to match an `out` or `in out` parameter). -// -// Our solution to both of these problems is the same. Rather than having -// the lowering of an expression return a single IR-level value (`IRInst*`), -// we have it return a more complex type (`LoweredValInfo`) which can represent -// a wider range of conceptual "values" which might correspond to multiple IR-level -// values, and/or represent a pointer to an l-value rather than the r-value itself. - -// We want to keep the representation of a `LoweringValInfo` relatively light -// - right now it is just a single pointer plus a "tag" to distinguish the cases. -// -// This means that cases that can't fit in a single pointer need a heap allocation -// to store their payload. For simplicity we represent all of these with a class -// hierarchy: -// -struct ExtendedValueInfo : RefObject -{}; - -// This case is used to indicate a value that is a reference -// to an AST-level subscript declaration. -// -struct SubscriptInfo : ExtendedValueInfo -{ - DeclRef declRef; -}; - -// This case is used to indicate a reference to an AST-level -// subscript operation bound to particular arguments. -// -// For example in a case like this: -// -// RWStructuredBuffer gBuffer; -// ... gBuffer[someIndex] ... -// -// the expression `gBuffer[someIndex]` will be lowered to -// a value that references `RWStructureBuffer::operator[]` -// with arguments `(gBuffer, someIndex)`. -// -// Such a value can be an l-value, and depending on the context -// where it is used, can lower into a call to either the getter -// or setter operations of the subscript. -// -struct BoundSubscriptInfo : ExtendedValueInfo -{ - DeclRef declRef; - IRType* type; - List args; -}; - -// Some cases of `ExtendedValueInfo` need to -// recursively contain `LoweredValInfo`s, and -// so we forward declare them here and fill -// them in later. -// -struct BoundMemberInfo; -struct SwizzledLValueInfo; - - -// This type is our core representation of lowered values. -// In the simple case, it just wraps an `IRInst*`. -// More complex cases, representing l-values or aggregate -// values are also supported. -struct LoweredValInfo -{ - // Which of the cases of value are we looking at? - enum class Flavor - { - // No value (akin to a null pointer) - None, - - // A simple IR value - Simple, - - // An l-value represented as an IR - // pointer to the value - Ptr, - - // A member declaration bound to a particular `this` value - BoundMember, - - // A reference to an AST-level subscript operation - Subscript, - - // An AST-level subscript operation bound to a particular - // object and arguments. - BoundSubscript, - - // The result of applying swizzling to an l-value - SwizzledLValue, - }; - - union - { - IRInst* val; - ExtendedValueInfo* ext; - }; - Flavor flavor; - - LoweredValInfo() - { - flavor = Flavor::None; - val = nullptr; - } - - LoweredValInfo(IRType* t) - { - flavor = Flavor::Simple; - val = t; - } - - static LoweredValInfo simple(IRInst* v) - { - LoweredValInfo info; - info.flavor = Flavor::Simple; - info.val = v; - return info; - } - - static LoweredValInfo ptr(IRInst* v) - { - LoweredValInfo info; - info.flavor = Flavor::Ptr; - info.val = v; - return info; - } - - static LoweredValInfo boundMember( - BoundMemberInfo* boundMemberInfo); - - BoundMemberInfo* getBoundMemberInfo() - { - SLANG_ASSERT(flavor == Flavor::BoundMember); - return (BoundMemberInfo*)ext; - } - - static LoweredValInfo subscript( - SubscriptInfo* subscriptInfo); - - SubscriptInfo* getSubscriptInfo() - { - SLANG_ASSERT(flavor == Flavor::Subscript); - return (SubscriptInfo*)ext; - } - - static LoweredValInfo boundSubscript( - BoundSubscriptInfo* boundSubscriptInfo); - - BoundSubscriptInfo* getBoundSubscriptInfo() - { - SLANG_ASSERT(flavor == Flavor::BoundSubscript); - return (BoundSubscriptInfo*)ext; - } - - static LoweredValInfo swizzledLValue( - SwizzledLValueInfo* extInfo); - - SwizzledLValueInfo* getSwizzledLValueInfo() - { - SLANG_ASSERT(flavor == Flavor::SwizzledLValue); - return (SwizzledLValueInfo*)ext; - } -}; - -// Represents some declaration bound to a particular -// object. For example, if we had `obj.f` where `f` -// is a member function, we'd use a `BoundMemberInfo` -// to represnet this. -// -// Note: This case is largely avoided by special-casing -// in the handling of calls (like `obj.f(arg)`), but -// it is being left here as an example of what we might -// need/want to do in the long term. -struct BoundMemberInfo : ExtendedValueInfo -{ - // The base object - LoweredValInfo base; - - // The (AST-level) declaration reference. - DeclRef declRef; - - // The type of this value - IRType* type; -}; - -// Represents the result of a swizzle operation in -// an l-value context. A swizzle without duplicate -// elements is allowed as an l-value, even if the -// element are non-contiguous (`.xz`) or out of -// order (`.zxy`). -// -struct SwizzledLValueInfo : ExtendedValueInfo -{ - // The type of the expression. - IRType* type; - - // The base expression (this should be an l-value) - LoweredValInfo base; - - // The number of elements in the swizzle - UInt elementCount; - - // THe indices for the elements being swizzled - UInt elementIndices[4]; -}; - -LoweredValInfo LoweredValInfo::boundMember( - BoundMemberInfo* boundMemberInfo) -{ - LoweredValInfo info; - info.flavor = Flavor::BoundMember; - info.ext = boundMemberInfo; - return info; -} - -LoweredValInfo LoweredValInfo::subscript( - SubscriptInfo* subscriptInfo) -{ - LoweredValInfo info; - info.flavor = Flavor::Subscript; - info.ext = subscriptInfo; - return info; -} - -LoweredValInfo LoweredValInfo::boundSubscript( - BoundSubscriptInfo* boundSubscriptInfo) -{ - LoweredValInfo info; - info.flavor = Flavor::BoundSubscript; - info.ext = boundSubscriptInfo; - return info; -} - -LoweredValInfo LoweredValInfo::swizzledLValue( - SwizzledLValueInfo* extInfo) -{ - LoweredValInfo info; - info.flavor = Flavor::SwizzledLValue; - info.ext = extInfo; - return info; -} - -// An "environment" for mapping AST declarations to IR values. -// -// This is required because in some cases we might lower the -// same AST declaration to the IR multiple times (e.g., when -// a generic transitively contains multiple functions, we -// will emit a distinct IR generic for each function, with -// its own copies of the generic parameters). -// -struct IRGenEnv -{ - // Map an AST-level declaration to the IR-level value that represents it. - Dictionary mapDeclToValue; - - // The next outer env around this one - IRGenEnv* outer = nullptr; -}; - -struct SharedIRGenContext -{ - SharedIRGenContext( - Session* session, - DiagnosticSink* sink, - ModuleDecl* mainModuleDecl = nullptr) - : m_session(session) - , m_sink(sink) - , m_mainModuleDecl(mainModuleDecl) - {} - - Session* m_session = nullptr; - DiagnosticSink* m_sink = nullptr; - ModuleDecl* m_mainModuleDecl = nullptr; - - // The "global" environment for mapping declarations to their IR values. - IRGenEnv globalEnv; - - // Map an AST-level declaration of an interface - // requirement to the IR-level "key" that - // is used to fetch that requirement from a - // witness table. - Dictionary interfaceRequirementKeys; - - // Arrays we keep around strictly for memory-management purposes: - - // Any extended values created during lowering need - // to be cleaned up after the fact. We don't try - // to reference-count these along the way because - // they need to get stored into a `union` inside `LoweredValInfo` - List> extValues; - - // Map from an AST-level statement that can be - // used as the target of a `break` or `continue` - // to the appropriate basic block to jump to. - Dictionary breakLabels; - Dictionary continueLabels; -}; - - -struct IRGenContext -{ - // Shared state for the IR generation process - SharedIRGenContext* shared; - - // environment for mapping AST decls to IR values - IRGenEnv* env; - - // IR builder to use when building code under this context - IRBuilder* irBuilder; - - // The value to use for any `this` expressions - // that appear in the current context. - // - // TODO: If we ever allow nesting of (non-static) - // types, then we may need to support references - // to an "outer `this`", and this representation - // might be insufficient. - LoweredValInfo thisVal; - - explicit IRGenContext(SharedIRGenContext* inShared) - : shared(inShared) - , env(&inShared->globalEnv) - , irBuilder(nullptr) - {} - - Session* getSession() - { - return shared->m_session; - } - - DiagnosticSink* getSink() - { - return shared->m_sink; - } - - ModuleDecl* getMainModuleDecl() - { - return shared->m_mainModuleDecl; - } -}; - -void setGlobalValue(SharedIRGenContext* sharedContext, Decl* decl, LoweredValInfo value) -{ - sharedContext->globalEnv.mapDeclToValue[decl] = value; -} - -void setGlobalValue(IRGenContext* context, Decl* decl, LoweredValInfo value) -{ - setGlobalValue(context->shared, decl, value); -} - -void setValue(IRGenContext* context, Decl* decl, LoweredValInfo value) -{ - context->env->mapDeclToValue[decl] = value; -} - -ModuleDecl* findModuleDecl(Decl* decl) -{ - for (auto dd = decl; dd; dd = dd->ParentDecl) - { - if (auto moduleDecl = as(dd)) - return moduleDecl; - } - return nullptr; -} - -bool isFromStdLib(Decl* decl) -{ - for (auto dd = decl; dd; dd = dd->ParentDecl) - { - if (dd->HasModifier()) - return true; - } - return false; -} - -bool isImportedDecl(IRGenContext* context, Decl* decl) -{ - ModuleDecl* moduleDecl = findModuleDecl(decl); - if (!moduleDecl) - return false; - - // HACK: don't treat standard library code as - // being imported for right now, just because - // we don't load its IR in the same way as - // for other imports. - // - // TODO: Fix this the right way, by having standard - // library declarations have IR modules that we link - // in via the normal means. - if (isFromStdLib(decl)) - return false; - - if (moduleDecl != context->getMainModuleDecl()) - return true; - - return false; -} - - /// Should the given `decl` nested in `parentDecl` be treated as a static rather than instance declaration? -bool isEffectivelyStatic( - Decl* decl, - ContainerDecl* parentDecl); - -// Ensure that a version of the given declaration has been emitted to the IR -LoweredValInfo ensureDecl( - 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, - IRType* type); - -IRInst* getSimpleVal(IRGenContext* context, LoweredValInfo lowered); - -IROp getIntrinsicOp( - Decl* decl, - IntrinsicOpModifier* intrinsicOpMod) -{ - if (int(intrinsicOpMod->op) != 0) - return intrinsicOpMod->op; - - // No specified modifier? Then we need to look it up - // based on the name of the declaration... - - auto name = decl->getName(); - auto nameText = getUnownedStringSliceText(name); - - IROp op = findIROp(nameText); - SLANG_ASSERT(op != kIROp_Invalid); - return op; -} - -// Given a `LoweredValInfo` for something callable, along with a -// bunch of arguments, emit an appropriate call to it. -LoweredValInfo emitCallToVal( - IRGenContext* context, - IRType* type, - LoweredValInfo funcVal, - UInt argCount, - IRInst* const* args) -{ - auto builder = context->irBuilder; - switch (funcVal.flavor) - { - case LoweredValInfo::Flavor::None: - SLANG_UNEXPECTED("null function"); - default: - return LoweredValInfo::simple( - builder->emitCallInst(type, getSimpleVal(context, funcVal), argCount, args)); - } -} - -LoweredValInfo emitCompoundAssignOp( - IRGenContext* context, - IRType* type, - IROp op, - UInt argCount, - IRInst* const* args) -{ - auto builder = context->irBuilder; - SLANG_UNREFERENCED_PARAMETER(argCount); - SLANG_ASSERT(argCount == 2); - auto leftPtr = args[0]; - auto rightVal = args[1]; - - auto leftVal = builder->emitLoad(leftPtr); - - IRInst* innerArgs[] = { leftVal, rightVal }; - auto innerOp = builder->emitIntrinsicInst(type, op, 2, innerArgs); - - builder->emitStore(leftPtr, innerOp); - - return LoweredValInfo::ptr(leftPtr); -} - -IRInst* getOneValOfType( - IRGenContext* context, - IRType* type) -{ - switch(type->op) - { - case kIROp_IntType: - case kIROp_UIntType: - case kIROp_UInt64Type: - return context->irBuilder->getIntValue(type, 1); - - case kIROp_HalfType: - case kIROp_FloatType: - case kIROp_DoubleType: - return context->irBuilder->getFloatValue(type, 1.0); - - default: - break; - } - - // TODO: should make sure to handle vector and matrix types here - - SLANG_UNEXPECTED("inc/dec type"); - UNREACHABLE_RETURN(nullptr); -} - -LoweredValInfo emitPrefixIncDecOp( - IRGenContext* context, - IRType* type, - IROp op, - UInt argCount, - IRInst* const* args) -{ - auto builder = context->irBuilder; - SLANG_UNREFERENCED_PARAMETER(argCount); - SLANG_ASSERT(argCount == 1); - auto argPtr = args[0]; - - auto preVal = builder->emitLoad(argPtr); - - IRInst* oneVal = getOneValOfType(context, type); - - IRInst* innerArgs[] = { preVal, oneVal }; - auto innerOp = builder->emitIntrinsicInst(type, op, 2, innerArgs); - - builder->emitStore(argPtr, innerOp); - - // For a prefix operator like `++i` we return - // the value after the increment/decrement has - // been applied. In casual terms we "increment - // the varaible, then return its value." - // - return LoweredValInfo::simple(innerOp); -} - -LoweredValInfo emitPostfixIncDecOp( - IRGenContext* context, - IRType* type, - IROp op, - UInt argCount, - IRInst* const* args) -{ - auto builder = context->irBuilder; - SLANG_UNREFERENCED_PARAMETER(argCount); - SLANG_ASSERT(argCount == 1); - auto argPtr = args[0]; - - auto preVal = builder->emitLoad(argPtr); - - IRInst* oneVal = getOneValOfType(context, type); - - IRInst* innerArgs[] = { preVal, oneVal }; - auto innerOp = builder->emitIntrinsicInst(type, op, 2, innerArgs); - - builder->emitStore(argPtr, innerOp); - - // For a postfix operator like `i++` we return - // the value that we read before the increment/decrement - // gets applied. In casual terms we "read - // the variable, then increment it." - // - return LoweredValInfo::simple(preVal); -} - -LoweredValInfo lowerRValueExpr( - IRGenContext* context, - Expr* expr); - -IRType* lowerType( - IRGenContext* context, - Type* type); - -static IRType* lowerType( - IRGenContext* context, - QualType const& type) -{ - return lowerType(context, type.type); -} - -// Given a `DeclRef` for something callable, along with a bunch of -// arguments, emit an appropriate call to it. -LoweredValInfo emitCallToDeclRef( - IRGenContext* context, - IRType* type, - DeclRef funcDeclRef, - IRType* funcType, - UInt argCount, - IRInst* const* args) -{ - auto builder = context->irBuilder; - - - if (auto subscriptDeclRef = funcDeclRef.as()) - { - // A reference to a subscript declaration is a special case, - // because it is not possible to call a subscript directly; - // we must call one of its accessors. - // - // TODO: everything here will also apply to propery declarations - // once we have them, so some of this code might be shared - // some day. - - DeclRef getterDeclRef; - bool justAGetter = true; - for (auto accessorDeclRef : getMembersOfType(subscriptDeclRef)) - { - // We want to track whether this subscript has any accessors other than - // `get` (assuming that everything except `get` can be used for setting...). - - if (auto foundGetterDeclRef = accessorDeclRef.as()) - { - // We found a getter. - getterDeclRef = foundGetterDeclRef; - } - else - { - // There was something other than a getter, so we can't - // invoke an accessor just now. - justAGetter = false; - } - } - - if (!justAGetter || !getterDeclRef) - { - // We can't perform an actual call right now, because - // this expression might appear in an r-value or l-value - // position (or *both* if it is being passed as an argument - // for an `in out` parameter!). - // - // Instead, we will construct a special-case value to - // represent the latent subscript operation (abstractly - // this is a reference to a storage location). - - // The abstract storage location will need to include - // all the arguments being passed to the subscript operation. - - RefPtr boundSubscript = new BoundSubscriptInfo(); - boundSubscript->declRef = subscriptDeclRef; - boundSubscript->type = type; - boundSubscript->args.addRange(args, argCount); - - context->shared->extValues.add(boundSubscript); - - return LoweredValInfo::boundSubscript(boundSubscript); - } - - // Otherwise we are just call the getter, and so that - // is what we need to be emitting a call to... - funcDeclRef = getterDeclRef; - } - - auto funcDecl = funcDeclRef.getDecl(); - if(auto intrinsicOpModifier = funcDecl->FindModifier()) - { - auto op = getIntrinsicOp(funcDecl, intrinsicOpModifier); - - if (isPseudoOp(op)) - { - switch (op) - { - case kIRPseudoOp_Pos: - return LoweredValInfo::simple(args[0]); - - case kIRPseudoOp_Sequence: - // The main effect of "operator comma" is to enforce - // sequencing of its operands, but Slang already - // implements a strictly left-to-right evaluation - // order for function arguments, so in practice we - // just need to compile `a, b` to the value of `b` - // (because argument evaluation already happened). - return LoweredValInfo::simple(args[1]); - -#define CASE(COMPOUND, OP) \ - case COMPOUND: return emitCompoundAssignOp(context, type, OP, argCount, args) - - CASE(kIRPseudoOp_AddAssign, kIROp_Add); - CASE(kIRPseudoOp_SubAssign, kIROp_Sub); - CASE(kIRPseudoOp_MulAssign, kIROp_Mul); - CASE(kIRPseudoOp_DivAssign, kIROp_Div); - CASE(kIRPseudoOp_ModAssign, kIROp_Mod); - CASE(kIRPseudoOp_AndAssign, kIROp_BitAnd); - CASE(kIRPseudoOp_OrAssign, kIROp_BitOr); - CASE(kIRPseudoOp_XorAssign, kIROp_BitXor); - CASE(kIRPseudoOp_LshAssign, kIROp_Lsh); - CASE(kIRPseudoOp_RshAssign, kIROp_Rsh); - -#undef CASE - -#define CASE(COMPOUND, OP) \ - case COMPOUND: return emitPrefixIncDecOp(context, type, OP, argCount, args) - CASE(kIRPseudoOp_PreInc, kIROp_Add); - CASE(kIRPseudoOp_PreDec, kIROp_Sub); -#undef CASE - -#define CASE(COMPOUND, OP) \ - case COMPOUND: return emitPostfixIncDecOp(context, type, OP, argCount, args) - CASE(kIRPseudoOp_PostInc, kIROp_Add); - CASE(kIRPseudoOp_PostDec, kIROp_Sub); -#undef CASE - default: - SLANG_UNIMPLEMENTED_X("IR pseudo-op"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - } - - return LoweredValInfo::simple(builder->emitIntrinsicInst( - type, - op, - argCount, - args)); - } - // TODO: handle target intrinsic modifier too... - - if( auto ctorDeclRef = funcDeclRef.as() ) - { - // 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. - - return LoweredValInfo::simple(builder->emitConstructorInst(type, argCount, args)); - } - - // Fallback case is to emit an actual call. - if(!funcType) - { - List argTypes; - for(UInt ii = 0; ii < argCount; ++ii) - { - argTypes.add(args[ii]->getDataType()); - } - funcType = builder->getFuncType(argCount, argTypes.getBuffer(), type); - } - LoweredValInfo funcVal = emitDeclRef(context, funcDeclRef, funcType); - return emitCallToVal(context, type, funcVal, argCount, args); -} - -LoweredValInfo emitCallToDeclRef( - IRGenContext* context, - IRType* type, - DeclRef funcDeclRef, - IRType* funcType, - List const& args) -{ - return emitCallToDeclRef(context, type, funcDeclRef, funcType, args.getCount(), args.getBuffer()); -} - -IRInst* getFieldKey( - IRGenContext* context, - DeclRef field) -{ - return getSimpleVal(context, emitDeclRef(context, field, context->irBuilder->getKeyType())); -} - -LoweredValInfo extractField( - IRGenContext* context, - IRType* fieldType, - LoweredValInfo base, - DeclRef field) -{ - IRBuilder* builder = context->irBuilder; - - switch (base.flavor) - { - default: - { - IRInst* irBase = getSimpleVal(context, base); - return LoweredValInfo::simple( - builder->emitFieldExtract( - fieldType, - irBase, - getFieldKey(context, field))); - } - break; - - case LoweredValInfo::Flavor::BoundMember: - case LoweredValInfo::Flavor::BoundSubscript: - { - // The base value is one that is trying to defer a get-vs-set - // decision, so we will need to do the same. - - RefPtr boundMemberInfo = new BoundMemberInfo(); - boundMemberInfo->type = fieldType; - boundMemberInfo->base = base; - boundMemberInfo->declRef = field; - - context->shared->extValues.add(boundMemberInfo); - return LoweredValInfo::boundMember(boundMemberInfo); - } - break; - - case LoweredValInfo::Flavor::Ptr: - { - // We are "extracting" a field from an lvalue address, - // which means we should just compute an lvalue - // representing the field address. - IRInst* irBasePtr = base.val; - return LoweredValInfo::ptr( - builder->emitFieldAddress( - builder->getPtrType(fieldType), - irBasePtr, - getFieldKey(context, field))); - } - break; - } -} - - - -LoweredValInfo materialize( - IRGenContext* context, - LoweredValInfo lowered) -{ - auto builder = context->irBuilder; - -top: - switch(lowered.flavor) - { - case LoweredValInfo::Flavor::None: - case LoweredValInfo::Flavor::Simple: - case LoweredValInfo::Flavor::Ptr: - return lowered; - - case LoweredValInfo::Flavor::BoundSubscript: - { - auto boundSubscriptInfo = lowered.getBoundSubscriptInfo(); - - // We are being asked to extract a value from a subscript call - // (e.g., `base[index]`). We will first check if the subscript - // declared a getter and use that if possible, and then fall - // back to a `ref` accessor if one is defined. - // - // (Picking the `get` over the `ref` accessor simplifies things - // in case the `get` operation has a natural translation for - // a target, while the general `ref` case does not...) - - auto getters = getMembersOfType(boundSubscriptInfo->declRef); - if (getters.Count()) - { - lowered = emitCallToDeclRef( - context, - boundSubscriptInfo->type, - *getters.begin(), - nullptr, - boundSubscriptInfo->args); - goto top; - } - - auto refAccessors = getMembersOfType(boundSubscriptInfo->declRef); - if(refAccessors.Count()) - { - // The `ref` accessor will return a pointer to the value, so - // we need to reflect that in the type of our `call` instruction. - IRType* ptrType = context->irBuilder->getPtrType(boundSubscriptInfo->type); - - LoweredValInfo refVal = emitCallToDeclRef( - context, - ptrType, - *refAccessors.begin(), - nullptr, - boundSubscriptInfo->args); - - // The result from the call needs to be implicitly dereferenced, - // so that it can work as an l-value of the desired result type. - lowered = LoweredValInfo::ptr(getSimpleVal(context, refVal)); - - goto top; - } - - SLANG_UNEXPECTED("subscript had no getter"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - break; - - case LoweredValInfo::Flavor::BoundMember: - { - auto boundMemberInfo = lowered.getBoundMemberInfo(); - auto base = materialize(context, boundMemberInfo->base); - - auto declRef = boundMemberInfo->declRef; - if( auto fieldDeclRef = declRef.as() ) - { - lowered = extractField(context, boundMemberInfo->type, base, fieldDeclRef); - goto top; - } - else - { - - SLANG_UNEXPECTED("unexpected member flavor"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - } - break; - - case LoweredValInfo::Flavor::SwizzledLValue: - { - auto swizzleInfo = lowered.getSwizzledLValueInfo(); - - return LoweredValInfo::simple(builder->emitSwizzle( - swizzleInfo->type, - getSimpleVal(context, swizzleInfo->base), - swizzleInfo->elementCount, - swizzleInfo->elementIndices)); - } - - default: - SLANG_UNEXPECTED("unhandled value flavor"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - -} - -IRInst* getSimpleVal(IRGenContext* context, LoweredValInfo lowered) -{ - auto builder = context->irBuilder; - - // First, try to eliminate any "bound" operations along the chain, - // so that we are dealing with an ordinary value, or an l-value pointer. - lowered = materialize(context, lowered); - - switch(lowered.flavor) - { - case LoweredValInfo::Flavor::None: - return nullptr; - - case LoweredValInfo::Flavor::Simple: - return lowered.val; - - case LoweredValInfo::Flavor::Ptr: - return builder->emitLoad(lowered.val); - - default: - SLANG_UNEXPECTED("unhandled value flavor"); - UNREACHABLE_RETURN(nullptr); - } -} - -LoweredValInfo lowerVal( - IRGenContext* context, - Val* val); - -IRInst* lowerSimpleVal( - IRGenContext* context, - Val* val) -{ - auto lowered = lowerVal(context, val); - return getSimpleVal(context, lowered); -} - -LoweredValInfo lowerLValueExpr( - IRGenContext* context, - Expr* expr); - -void assign( - IRGenContext* context, - LoweredValInfo const& left, - LoweredValInfo const& right); - -IRInst* getAddress( - IRGenContext* context, - LoweredValInfo const& inVal, - SourceLoc diagnosticLocation); - -void lowerStmt( - IRGenContext* context, - Stmt* stmt); - -LoweredValInfo lowerDecl( - IRGenContext* context, - DeclBase* decl); - -IRType* getIntType( - IRGenContext* context) -{ - return context->irBuilder->getBasicType(BaseType::Int); -} - -static IRGeneric* getOuterGeneric(IRInst* gv) -{ - auto parentBlock = as(gv->getParent()); - if (!parentBlock) return nullptr; - - auto parentGeneric = as(parentBlock->getParent()); - return parentGeneric; -} - -static void addLinkageDecoration( - IRGenContext* context, - IRInst* inInst, - Decl* decl, - UnownedStringSlice const& mangledName) -{ - // If the instruction is nested inside one or more generics, - // then the mangled name should really apply to the outer-most - // generic, and not the declaration nested inside. - - auto builder = context->irBuilder; - - IRInst* inst = inInst; - while (auto outerGeneric = getOuterGeneric(inst)) - { - inst = outerGeneric; - } - - if(isImportedDecl(context, decl)) - { - builder->addImportDecoration(inst, mangledName); - } - else - { - builder->addExportDecoration(inst, mangledName); - } -} - -static void addLinkageDecoration( - IRGenContext* context, - IRInst* inst, - Decl* decl) -{ - addLinkageDecoration(context, inst, decl, getMangledName(decl).getUnownedSlice()); -} - -IRStructKey* getInterfaceRequirementKey( - IRGenContext* context, - Decl* requirementDecl) -{ - IRStructKey* requirementKey = nullptr; - if(context->shared->interfaceRequirementKeys.TryGetValue(requirementDecl, requirementKey)) - { - return requirementKey; - } - - IRBuilder builderStorage = *context->irBuilder; - auto builder = &builderStorage; - - builder->setInsertInto(builder->sharedBuilder->module->getModuleInst()); - - // Construct a key to serve as the representation of - // this requirement in the IR, and to allow lookup - // into the declaration. - requirementKey = builder->createStructKey(); - - addLinkageDecoration(context, requirementKey, requirementDecl); - - context->shared->interfaceRequirementKeys.Add(requirementDecl, requirementKey); - - return requirementKey; -} - - -SubstitutionSet lowerSubstitutions(IRGenContext* context, SubstitutionSet subst); -// - -struct ValLoweringVisitor : ValVisitor -{ - IRGenContext* context; - - IRBuilder* getBuilder() { return context->irBuilder; } - - LoweredValInfo visitVal(Val* /*val*/) - { - SLANG_UNIMPLEMENTED_X("value lowering"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - - LoweredValInfo visitGenericParamIntVal(GenericParamIntVal* val) - { - return emitDeclRef(context, val->declRef, - lowerType(context, GetType(val->declRef))); - } - - LoweredValInfo visitDeclaredSubtypeWitness(DeclaredSubtypeWitness* val) - { - return emitDeclRef(context, val->declRef, - context->irBuilder->getWitnessTableType()); - } - - LoweredValInfo visitTransitiveSubtypeWitness( - TransitiveSubtypeWitness* val) - { - // The base (subToMid) will turn into a value with - // witness-table type. - IRInst* baseWitnessTable = lowerSimpleVal(context, val->subToMid); - - // The next step should map to an interface requirement - // that is itself an interface conformance, so the result - // of lowering this value should be a "key" that we can - // use to look up a witness table. - IRInst* requirementKey = getInterfaceRequirementKey(context, val->midToSup.getDecl()); - - // TODO: There are some ugly cases here if `midToSup` is allowed - // to be an arbitrary witness, rather than just a declared one, - // and we should probably change the front-end representation - // to reflect the right constraints. - - return LoweredValInfo::simple(getBuilder()->emitLookupInterfaceMethodInst( - nullptr, - baseWitnessTable, - requirementKey)); - } - - LoweredValInfo visitTaggedUnionSubtypeWitness( - TaggedUnionSubtypeWitness* val) - { - // The sub-type in this case is a tagged union `A | B | ...`, - // and the witness holds an array of witnesses showing that each - // "case" (`A`, `B`, etc.) is a subtype of the super-type. - - // We will start by getting the IR-level representation of the - // sub type (the tagged union type). - // - auto irTaggedUnionType = lowerType(context, val->sub); - - // We can turn each of those per-case witnesses into a witness - // table value: - // - auto caseCount = val->caseWitnesses.getCount(); - List caseWitnessTables; - for( auto caseWitness : val->caseWitnesses ) - { - auto caseWitnessTable = lowerSimpleVal(context, caseWitness); - caseWitnessTables.add(caseWitnessTable); - } - - // Now we need to synthesize a witness table for the tagged union - // value, showing how it can implement all of the requirements - // of the super type by delegating to the appropriate implementation - // on a per-case basis. - // - // We will assume here that the super-type is an interface, and it - // will be left to the front-end to ensure this property. - // - auto supDeclRefType = as(val->sup); - if(!supDeclRefType) - { - SLANG_UNEXPECTED("super-type not a decl-ref type when generating tagged union witness table"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - auto supInterfaceDeclRef = supDeclRefType->declRef.as(); - if( !supInterfaceDeclRef ) - { - SLANG_UNEXPECTED("super-type not an interface type when generating tagged union witness table"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - - auto irWitnessTable = getBuilder()->createWitnessTable(); - - // Now we will iterate over the requirements (members) of the - // interface and try to synthesize an appropriate value for each. - // - for( auto reqDeclRef : getMembers(supInterfaceDeclRef) ) - { - // TODO: if there are any members we shouldn't process as a requirement, - // then we should detect and skip them here. - // - - // Every interface requirement will have a unique key that is used - // when looking up the requirement in a concrete witness table. - // - auto irReqKey = getInterfaceRequirementKey(context, reqDeclRef.getDecl()); - - // We expect that each of the witness tables in `caseWitnessTables` - // will have an entry to match these keys. However, we may not - // have a concrete `IRWitnessTable` for each of the case types, either - // because they are a specialization of a generic (so that the witness - // table reference is a `specialize` instruction at this point), or - // they are a type external to this module (so that we have a declaration - // rather than a definition of the witness table). - - // Our task is to create an IR value that can satisfy the interface - // requirement for the tagged union type, by appropriately delegating - // to the implementations of the same requirement in the case types. - // - IRInst* irSatisfyingVal = nullptr; - - - - if(auto callableDeclRef = reqDeclRef.as()) - { - // We have something callable, so we need to synthesize - // a function to satisfy it. - // - auto irFunc = getBuilder()->createFunc(); - irSatisfyingVal = irFunc; - - IRBuilder subBuilderStorage; - auto subBuilder = &subBuilderStorage; - subBuilder->sharedBuilder = getBuilder()->sharedBuilder; - subBuilder->setInsertInto(irFunc); - - // We will start by setting up the function parameters, - // which live in the entry block of the IR function. - // - auto entryBlock = subBuilder->emitBlock(); - subBuilder->setInsertInto(entryBlock); - - // Create a `this` parameter of the tagged-union type. - // - // TODO: need to handle the `[mutating]` case here... - // - auto irThisType = irTaggedUnionType; - auto irThisParam = subBuilder->emitParam(irThisType); - - List irParamTypes; - irParamTypes.add(irThisType); - - // Create the remaining parameters of the callable, - // using a decl-ref specialized to the tagged union - // type (so that things like associated types are - // mapped to the correct witness value). - // - List irParams; - for( auto paramDeclRef : getMembersOfType(callableDeclRef) ) - { - // TODO: need to handle `out` and `in out` here. Over all - // there is a lot of duplication here with the existing logic - // for emitting the signature of a `CallableDecl`, and we should - // try to re-use that if at all possible. - // - auto irParamType = lowerType(context, GetType(paramDeclRef)); - auto irParam = subBuilder->emitParam(irParamType); - - irParams.add(irParam); - irParamTypes.add(irParamType); - } - - auto irResultType = lowerType(context, GetResultType(callableDeclRef)); - - auto irFuncType = subBuilder->getFuncType( - irParamTypes, - irResultType); - irFunc->setFullType(irFuncType); - - // The first thing our function needs to do is extract the tag - // from the incoming `this` parameter. - // - auto irTagVal = subBuilder->emitExtractTaggedUnionTag(irThisParam); - - // Next we want to emit a `switch` on the tag value, but before we - // do that we need to generate the code for each of the cases so that - // our `switch` has somewhere to branch to. - // - List switchCaseOperands; - - IRBlock* defaultLabel = nullptr; - - for( Index ii = 0; ii < caseCount; ++ii ) - { - auto caseTag = subBuilder->getIntValue(irTagVal->getDataType(), ii); - - subBuilder->setInsertInto(irFunc); - auto caseLabel = subBuilder->emitBlock(); - - if(!defaultLabel) - defaultLabel = caseLabel; - - switchCaseOperands.add(caseTag); - switchCaseOperands.add(caseLabel); - - subBuilder->setInsertInto(caseLabel); - - // We need to look up the satisfying value for this interface - // requirement on the witness table of the particular case value. - // - // We already have the witness table, and the requirement key is - // just `irReqKey`. - // - auto caseWitnessTable = caseWitnessTables[ii]; - - // The subtle bit here is determining the type we expect the - // satisfying value to have, since that depends on the actual - // type that is satisfying the requirement. - // - IRType* caseResultType = irResultType; - IRType* caseFuncType = nullptr; - auto caseFunc = subBuilder->emitLookupInterfaceMethodInst( - caseFuncType, - caseWitnessTable, - irReqKey); - - // We are going to emit a `call` to the satisfying value - // for the case type, so we will collect the arguments for that call. - // - List caseArgs; - - // The `this` argument to the call will need to represent the - // appropriate field of our tagged union. - // - IRType* caseThisType = (IRType*) irTaggedUnionType->getOperand(ii); - auto caseThisArg = subBuilder->emitExtractTaggedUnionPayload( - caseThisType, - irThisParam, caseTag); - caseArgs.add(caseThisArg); - - // The remaining arguments to the call will just be forwarded from - // the parameters of the wrapper function. - // - // TODO: This would need to change if/when we started allowing `This` type - // or associated-type parameters to be used at call sites where a tagged - // union is used. - // - for( auto param : irParams ) - { - caseArgs.add(param); - } - - auto caseCall = subBuilder->emitCallInst(caseResultType, caseFunc, caseArgs); - - if( as(irResultType->getDataType()) ) - { - subBuilder->emitReturn(); - } - else - { - subBuilder->emitReturn(caseCall); - } - } - - // We will create a block to represent the supposedly-unreachable - // code that will run if no `case` matches. - // - subBuilder->setInsertInto(irFunc); - auto invalidLabel = subBuilder->emitBlock(); - subBuilder->setInsertInto(invalidLabel); - subBuilder->emitUnreachable(); - - if(!defaultLabel) defaultLabel = invalidLabel; - - // Now we have enough information to go back and emit the `switch` instruction - // into the entry block. - subBuilder->setInsertInto(entryBlock); - subBuilder->emitSwitch( - irTagVal, // value to `switch` on - invalidLabel, // `break` label (block after the `switch` statement ends) - defaultLabel, // `default` label (where to go if no `case` matches) - switchCaseOperands.getCount(), - switchCaseOperands.getBuffer()); - } - else - { - // TODO: We need to handle other cases of interface requirements. - SLANG_UNEXPECTED("unexpceted interface requirement when generating tagged union witness table"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - - // Once we've generating a value to satisfying the requirement, we install - // it into the witness table for our tagged-union type. - // - getBuilder()->createWitnessTableEntry(irWitnessTable, irReqKey, irSatisfyingVal); - } - - return LoweredValInfo::simple(irWitnessTable); - } - - LoweredValInfo visitConstantIntVal(ConstantIntVal* val) - { - // TODO: it is a bit messy here that the `ConstantIntVal` representation - // has no notion of a *type* associated with the value... - - auto type = getIntType(context); - return LoweredValInfo::simple(getBuilder()->getIntValue(type, val->value)); - } - - IRFuncType* visitFuncType(FuncType* type) - { - IRType* resultType = lowerType(context, type->getResultType()); - UInt paramCount = type->getParamCount(); - List paramTypes; - for (UInt pp = 0; pp < paramCount; ++pp) - { - paramTypes.add(lowerType(context, type->getParamType(pp))); - } - return getBuilder()->getFuncType( - paramCount, - paramTypes.getBuffer(), - resultType); - } - - IRType* visitDeclRefType(DeclRefType* type) - { - auto declRef = type->declRef; - auto decl = declRef.getDecl(); - - // Check for types with teh `__intrinsic_type` modifier. - if(decl->FindModifier()) - { - return lowerSimpleIntrinsicType(type); - } - - - return (IRType*) getSimpleVal( - context, - emitDeclRef(context, declRef, - context->irBuilder->getTypeKind())); - } - - IRType* visitNamedExpressionType(NamedExpressionType* type) - { - return (IRType*)getSimpleVal(context, dispatchType(type->GetCanonicalType())); - } - - IRType* visitBasicExpressionType(BasicExpressionType* type) - { - return getBuilder()->getBasicType( - type->baseType); - } - - IRType* visitVectorExpressionType(VectorExpressionType* type) - { - auto elementType = lowerType(context, type->elementType); - auto elementCount = lowerSimpleVal(context, type->elementCount); - - return getBuilder()->getVectorType( - elementType, - elementCount); - } - - IRType* visitMatrixExpressionType(MatrixExpressionType* type) - { - auto elementType = lowerType(context, type->getElementType()); - auto rowCount = lowerSimpleVal(context, type->getRowCount()); - auto columnCount = lowerSimpleVal(context, type->getColumnCount()); - - return getBuilder()->getMatrixType( - elementType, - rowCount, - columnCount); - } - - IRType* visitArrayExpressionType(ArrayExpressionType* type) - { - auto elementType = lowerType(context, type->baseType); - if (type->ArrayLength) - { - auto elementCount = lowerSimpleVal(context, type->ArrayLength); - return getBuilder()->getArrayType( - elementType, - elementCount); - } - else - { - return getBuilder()->getUnsizedArrayType( - elementType); - } - } - - // Lower a type where the type declaration being referenced is assumed - // to be an intrinsic type, which can thus be lowered to a simple IR - // type with the appropriate opcode. - IRType* lowerSimpleIntrinsicType(DeclRefType* type) - { - auto intrinsicTypeModifier = type->declRef.getDecl()->FindModifier(); - SLANG_ASSERT(intrinsicTypeModifier); - IROp op = IROp(intrinsicTypeModifier->irOp); - return getBuilder()->getType(op); - } - - // Lower a type where the type declaration being referenced is assumed - // to be an intrinsic type with a single generic type parameter, and - // which can thus be lowered to a simple IR type with the appropriate opcode. - IRType* lowerGenericIntrinsicType(DeclRefType* type, Type* elementType) - { - auto intrinsicTypeModifier = type->declRef.getDecl()->FindModifier(); - SLANG_ASSERT(intrinsicTypeModifier); - IROp op = IROp(intrinsicTypeModifier->irOp); - IRInst* irElementType = lowerType(context, elementType); - return getBuilder()->getType( - op, - 1, - &irElementType); - } - - IRType* lowerGenericIntrinsicType(DeclRefType* type, Type* elementType, IntVal* count) - { - auto intrinsicTypeModifier = type->declRef.getDecl()->FindModifier(); - SLANG_ASSERT(intrinsicTypeModifier); - IROp op = IROp(intrinsicTypeModifier->irOp); - IRInst* irElementType = lowerType(context, elementType); - - IRInst* irCount = lowerSimpleVal(context, count); - - IRInst* const operands[2] = - { - irElementType, - irCount, - }; - - return getBuilder()->getType( - op, - SLANG_COUNT_OF(operands), - operands); - } - - IRType* visitResourceType(ResourceType* type) - { - return lowerGenericIntrinsicType(type, type->elementType); - } - - IRType* visitSamplerStateType(SamplerStateType* type) - { - return lowerSimpleIntrinsicType(type); - } - - IRType* visitBuiltinGenericType(BuiltinGenericType* type) - { - return lowerGenericIntrinsicType(type, type->elementType); - } - - IRType* visitUntypedBufferResourceType(UntypedBufferResourceType* type) - { - return lowerSimpleIntrinsicType(type); - } - - IRType* visitHLSLPatchType(HLSLPatchType* type) - { - Type* elementType = type->getElementType(); - IntVal* count = type->getElementCount(); - - return lowerGenericIntrinsicType(type, elementType, count); - } - - IRType* visitExtractExistentialType(ExtractExistentialType* type) - { - auto declRef = type->declRef; - auto existentialType = lowerType(context, GetType(declRef)); - IRInst* existentialVal = getSimpleVal(context, emitDeclRef(context, declRef, existentialType)); - return getBuilder()->emitExtractExistentialType(existentialVal); - } - - LoweredValInfo visitExtractExistentialSubtypeWitness(ExtractExistentialSubtypeWitness* witness) - { - auto declRef = witness->declRef; - auto existentialType = lowerType(context, GetType(declRef)); - IRInst* existentialVal = getSimpleVal(context, emitDeclRef(context, declRef, existentialType)); - return LoweredValInfo::simple(getBuilder()->emitExtractExistentialWitnessTable(existentialVal)); - } - - LoweredValInfo visitTaggedUnionType(TaggedUnionType* type) - { - // A tagged union type will lower into an IR `union` over the cases, - // along with an IR `struct` with a field for the union and a tag. - // (Note: we are placing the tag after the payload to avoid padding - // in the case where the payload is more aligned than the tag) - // - // TODO: should we be lowering directly like this, or have - // an IR-level representation of tagged unions? - // - - List irCaseTypes; - for(auto caseType : type->caseTypes) - { - auto irCaseType = lowerType(context, caseType); - irCaseTypes.add(irCaseType); - } - - auto irType = getBuilder()->getTaggedUnionType(irCaseTypes); - if(!irType->findDecoration()) - { - // We need a way for later passes to attach layout information - // to this type, so we will give it a mangled name here. - // - getBuilder()->addExportDecoration( - irType, - getMangledTypeName(type).getUnownedSlice()); - } - return LoweredValInfo::simple(irType); - } - - LoweredValInfo visitExistentialSpecializedType(ExistentialSpecializedType* type) - { - auto irBaseType = lowerType(context, type->baseType); - - List slotArgs; - for(auto arg : type->slots.args) - { - auto irArgType = lowerType(context, arg.type); - auto irArgWitness = lowerSimpleVal(context, arg.witness); - - slotArgs.add(irArgType); - slotArgs.add(irArgWitness); - } - - auto irType = getBuilder()->getBindExistentialsType(irBaseType, slotArgs.getCount(), slotArgs.getBuffer()); - return LoweredValInfo::simple(irType); - } - - // We do not expect to encounter the following types in ASTs that have - // passed front-end semantic checking. -#define UNEXPECTED_CASE(NAME) IRType* visit##NAME(NAME*) { SLANG_UNEXPECTED(#NAME); UNREACHABLE_RETURN(nullptr); } - UNEXPECTED_CASE(GenericDeclRefType) - UNEXPECTED_CASE(TypeType) - UNEXPECTED_CASE(ErrorType) - UNEXPECTED_CASE(InitializerListType) - UNEXPECTED_CASE(OverloadGroupType) -}; - -LoweredValInfo lowerVal( - IRGenContext* context, - Val* val) -{ - ValLoweringVisitor visitor; - visitor.context = context; - return visitor.dispatch(val); -} - -IRType* lowerType( - IRGenContext* context, - Type* type) -{ - ValLoweringVisitor visitor; - visitor.context = context; - return (IRType*) getSimpleVal(context, visitor.dispatchType(type)); -} - -void addVarDecorations( - IRGenContext* context, - IRInst* inst, - Decl* decl) -{ - auto builder = context->irBuilder; - for(RefPtr mod : decl->modifiers) - { - if(as(mod)) - { - builder->addInterpolationModeDecoration(inst, IRInterpolationMode::NoInterpolation); - } - else if(as(mod)) - { - builder->addInterpolationModeDecoration(inst, IRInterpolationMode::NoPerspective); - } - else if(as(mod)) - { - builder->addInterpolationModeDecoration(inst, IRInterpolationMode::Linear); - } - else if(as(mod)) - { - builder->addInterpolationModeDecoration(inst, IRInterpolationMode::Sample); - } - else if(as(mod)) - { - builder->addInterpolationModeDecoration(inst, IRInterpolationMode::Centroid); - } - else if(as(mod)) - { - builder->addSimpleDecoration(inst); - } - else if(as(mod)) - { - builder->addSimpleDecoration(inst); - } - else if(as(mod)) - { - builder->addSimpleDecoration(inst); - } - else if(as(mod)) - { - builder->addSimpleDecoration(inst); - } - else if(as(mod)) - { - builder->addSimpleDecoration(inst); - } - else if(auto formatAttr = as(mod)) - { - builder->addFormatDecoration(inst, formatAttr->format); - } - - // TODO: what are other modifiers we need to propagate through? - } -} - -/// If `decl` has a modifier that should turn into a -/// rate qualifier, then apply it to `inst`. -void maybeSetRate( - IRGenContext* context, - IRInst* inst, - Decl* decl) -{ - auto builder = context->irBuilder; - - if (decl->HasModifier()) - { - inst->setFullType(builder->getRateQualifiedType( - builder->getGroupSharedRate(), - inst->getFullType())); - } -} - -static String getNameForNameHint( - IRGenContext* context, - Decl* decl) -{ - // We will use a bit of an ad hoc convention here for now. - - Name* leafName = decl->getName(); - - // Handle custom name for a global parameter group (e.g., a `cbuffer`) - if(auto reflectionNameModifier = decl->FindModifier()) - { - leafName = reflectionNameModifier->nameAndLoc.name; - } - - // There is no point in trying to provide a name hint for something with no name, - // or with an empty name - if(!leafName) - return String(); - if(leafName->text.getLength() == 0) - return String(); - - - if(auto varDecl = as(decl)) - { - // For an ordinary local variable, global variable, - // parameter, or field, we will just use the name - // as declared, and now work in anything from - // its parent declaration(s). - // - // TODO: consider whether global/static variables should - // follow different rules. - // - return leafName->text; - } - - // For other cases of declaration, we want to consider - // merging its name with the name of its parent declaration. - auto parentDecl = decl->ParentDecl; - - // Skip past a generic parent, if we are a declaration nested in a generic. - if(auto genericParentDecl = as(parentDecl)) - parentDecl = genericParentDecl->ParentDecl; - - // A `ModuleDecl` can have a name too, but in the common case - // we don't want to generate name hints that include the module - // name, simply because they would lead to every global symbol - // getting a much longer name. - // - // TODO: We should probably include the module name for symbols - // being `import`ed, and not for symbols being compiled directly - // (those coming from a module that had no name given to it). - // - // For now we skip past a `ModuleDecl` parent. - // - if(auto moduleParentDecl = as(parentDecl)) - parentDecl = moduleParentDecl->ParentDecl; - - if(!parentDecl) - { - return leafName->text; - } - - auto parentName = getNameForNameHint(context, parentDecl); - if(parentName.getLength() == 0) - { - return leafName->text; - } - - // We will now construct a new `Name` to use as the hint, - // combining the name of the parent and the leaf declaration. - - StringBuilder sb; - sb.append(parentName); - sb.append("."); - sb.append(leafName->text); - - return sb.ProduceString(); -} - -/// Try to add an appropriate name hint to the instruction, -/// that can be used for back-end code emission or debug info. -static void addNameHint( - IRGenContext* context, - IRInst* inst, - Decl* decl) -{ - String name = getNameForNameHint(context, decl); - if(name.getLength() == 0) - return; - context->irBuilder->addNameHintDecoration(inst, name.getUnownedSlice()); -} - -/// Add a name hint based on a fixed string. -static void addNameHint( - IRGenContext* context, - IRInst* inst, - char const* text) -{ - context->irBuilder->addNameHintDecoration(inst, UnownedTerminatedStringSlice(text)); -} - -LoweredValInfo createVar( - IRGenContext* context, - IRType* type, - Decl* decl = nullptr) -{ - auto builder = context->irBuilder; - auto irAlloc = builder->emitVar(type); - - if (decl) - { - maybeSetRate(context, irAlloc, decl); - - addVarDecorations(context, irAlloc, decl); - - builder->addHighLevelDeclDecoration(irAlloc, decl); - - addNameHint(context, irAlloc, decl); - } - - return LoweredValInfo::ptr(irAlloc); -} - -void addArgs( - IRGenContext* context, - List* ioArgs, - LoweredValInfo argInfo) -{ - auto& args = *ioArgs; - switch( argInfo.flavor ) - { - case LoweredValInfo::Flavor::Simple: - case LoweredValInfo::Flavor::Ptr: - case LoweredValInfo::Flavor::SwizzledLValue: - case LoweredValInfo::Flavor::BoundSubscript: - case LoweredValInfo::Flavor::BoundMember: - args.add(getSimpleVal(context, argInfo)); - break; - - default: - SLANG_UNIMPLEMENTED_X("addArgs case"); - break; - } -} - -// - -// When we try to turn a `LoweredValInfo` into an address of some temporary storage, -// we can either do it "aggressively" or not (what we'll call the "default" behavior, -// although it isn't strictly more common). -// -// The case that this is mostly there to address is when somebody writes an operation -// like: -// -// foo[a] = b; -// -// In that case, we might as well just use the `set` accessor if there is one, rather -// than complicate things. However, in more complex cases like: -// -// foo[a].x = b; -// -// there is no way to satisfy the semantics of the code the user wrote (in terms of -// only writing one vector component, and not a full vector) by using the `set` -// accessor, and we need to be "aggressive" in turning the lvalue `foo[a]` into -// an address. -// -// TODO: realistically IR lowering is too early to be binding to this choice, -// because different accessors might be supported on different targets. -// -enum class TryGetAddressMode -{ - Default, - Aggressive, -}; - -/// Try to coerce `inVal` into a `LoweredValInfo::ptr()` with a simple address. -LoweredValInfo tryGetAddress( - IRGenContext* context, - LoweredValInfo const& inVal, - TryGetAddressMode mode); - - -// - -template -struct ExprLoweringVisitorBase : ExprVisitor -{ - IRGenContext* context; - - IRBuilder* getBuilder() { return context->irBuilder; } - - // Lower an expression that should have the same l-value-ness - // as the visitor itself. - LoweredValInfo lowerSubExpr(Expr* expr) - { - IRBuilderSourceLocRAII sourceLocInfo(getBuilder(), expr->loc); - return this->dispatch(expr); - } - - - LoweredValInfo visitVarExpr(VarExpr* expr) - { - LoweredValInfo info = emitDeclRef( - context, - expr->declRef, - lowerType(context, expr->type)); - return info; - } - - LoweredValInfo visitOverloadedExpr(OverloadedExpr* /*expr*/) - { - SLANG_UNEXPECTED("overloaded expressions should not occur in checked AST"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - - LoweredValInfo visitOverloadedExpr2(OverloadedExpr2* /*expr*/) - { - SLANG_UNEXPECTED("overloaded expressions should not occur in checked AST"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - - LoweredValInfo visitIndexExpr(IndexExpr* expr) - { - auto type = lowerType(context, expr->type); - auto baseVal = lowerSubExpr(expr->BaseExpression); - auto indexVal = getSimpleVal(context, lowerRValueExpr(context, expr->IndexExpression)); - - return subscriptValue(type, baseVal, indexVal); - } - - LoweredValInfo visitThisExpr(ThisExpr* /*expr*/) - { - return context->thisVal; - } - - LoweredValInfo visitMemberExpr(MemberExpr* expr) - { - auto loweredType = lowerType(context, expr->type); - auto loweredBase = lowerRValueExpr(context, expr->BaseExpression); - - auto declRef = expr->declRef; - if (auto fieldDeclRef = declRef.as()) - { - // Okay, easy enough: we have a reference to a field of a struct type... - return extractField(loweredType, loweredBase, fieldDeclRef); - } - else if (auto callableDeclRef = declRef.as()) - { - RefPtr boundMemberInfo = new BoundMemberInfo(); - boundMemberInfo->type = nullptr; - boundMemberInfo->base = loweredBase; - boundMemberInfo->declRef = callableDeclRef; - return LoweredValInfo::boundMember(boundMemberInfo); - } - else if(auto constraintDeclRef = declRef.as()) - { - // The code is making use of a "witness" that a value of - // some generic type conforms to an interface. - // - // For now we will just emit the base expression as-is. - // TODO: we may need to insert an explicit instruction - // for a cast here (that could become a no-op later). - return loweredBase; - } - - SLANG_UNIMPLEMENTED_X("codegen for subscript expression"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - - // We will always lower a dereference expression (`*ptr`) - // as an l-value, since that is the easiest way to handle it. - LoweredValInfo visitDerefExpr(DerefExpr* expr) - { - auto loweredBase = lowerRValueExpr(context, expr->base); - - // TODO: handle tupel-type for `base` - - // The type of the lowered base must by some kind of pointer, - // in order for a dereference to make senese, so we just - // need to extract the value type from that pointer here. - // - IRInst* loweredBaseVal = getSimpleVal(context, loweredBase); - IRType* loweredBaseType = loweredBaseVal->getDataType(); - - if (as(loweredBaseType) - || as(loweredBaseType)) - { - // Note that we do *not* perform an actual `load` operation - // here, but rather just use the pointer value to construct - // an appropriate `LoweredValInfo` representing the underlying - // dereference. - // - // This is important so that an expression like `&((*foo).bar)` - // (which is desugared from `&foo->bar`) can be handled; such - // an expression does *not* perform a dereference at runtime, - // and is just a bit of pointer math. - // - return LoweredValInfo::ptr(loweredBaseVal); - } - else - { - SLANG_UNIMPLEMENTED_X("codegen for deref expression"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - } - - LoweredValInfo visitParenExpr(ParenExpr* expr) - { - return lowerSubExpr(expr->base); - } - - LoweredValInfo getSimpleDefaultVal(IRType* type) - { - if(auto basicType = as(type)) - { - switch( basicType->getBaseType() ) - { - default: - SLANG_UNEXPECTED("missing case for getting IR default value"); - UNREACHABLE_RETURN(LoweredValInfo()); - break; - - case BaseType::Bool: - case BaseType::Int8: - case BaseType::Int16: - case BaseType::Int: - case BaseType::Int64: - case BaseType::UInt8: - case BaseType::UInt16: - case BaseType::UInt: - case BaseType::UInt64: - return LoweredValInfo::simple(getBuilder()->getIntValue(type, 0)); - - case BaseType::Half: - case BaseType::Float: - case BaseType::Double: - return LoweredValInfo::simple(getBuilder()->getFloatValue(type, 0.0)); - } - } - - SLANG_UNEXPECTED("missing case for getting IR default value"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - - LoweredValInfo getDefaultVal(Type* type) - { - auto irType = lowerType(context, type); - if (auto basicType = as(type)) - { - return getSimpleDefaultVal(irType); - } - else if (auto vectorType = as(type)) - { - UInt elementCount = (UInt) GetIntVal(vectorType->elementCount); - - auto irDefaultValue = getSimpleVal(context, getDefaultVal(vectorType->elementType)); - - List args; - for(UInt ee = 0; ee < elementCount; ++ee) - { - args.add(irDefaultValue); - } - return LoweredValInfo::simple( - getBuilder()->emitMakeVector(irType, args.getCount(), args.getBuffer())); - } - else if (auto matrixType = as(type)) - { - UInt rowCount = (UInt) GetIntVal(matrixType->getRowCount()); - - auto rowType = matrixType->getRowType(); - - auto irDefaultValue = getSimpleVal(context, getDefaultVal(rowType)); - - List args; - for(UInt rr = 0; rr < rowCount; ++rr) - { - args.add(irDefaultValue); - } - return LoweredValInfo::simple( - getBuilder()->emitMakeMatrix(irType, args.getCount(), args.getBuffer())); - } - else if (auto arrayType = as(type)) - { - UInt elementCount = (UInt) GetIntVal(arrayType->ArrayLength); - - auto irDefaultElement = getSimpleVal(context, getDefaultVal(arrayType->baseType)); - - List args; - for(UInt ee = 0; ee < elementCount; ++ee) - { - args.add(irDefaultElement); - } - - return LoweredValInfo::simple( - getBuilder()->emitMakeArray(irType, args.getCount(), args.getBuffer())); - } - else if (auto declRefType = as(type)) - { - DeclRef declRef = declRefType->declRef; - if (auto aggTypeDeclRef = declRef.as()) - { - List args; - for (auto ff : getMembersOfType(aggTypeDeclRef)) - { - if (ff.getDecl()->HasModifier()) - continue; - - auto irFieldVal = getSimpleVal(context, getDefaultVal(ff)); - args.add(irFieldVal); - } - - return LoweredValInfo::simple( - getBuilder()->emitMakeStruct(irType, args.getCount(), args.getBuffer())); - } - } - - SLANG_UNEXPECTED("unexpected type when creating default value"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - - LoweredValInfo getDefaultVal(VarDeclBase* decl) - { - if(auto initExpr = decl->initExpr) - { - return lowerRValueExpr(context, initExpr); - } - else - { - return getDefaultVal(decl->type); - } - } - - LoweredValInfo visitInitializerListExpr(InitializerListExpr* expr) - { - // Allocate a temporary of the given type - auto type = expr->type; - IRType* irType = lowerType(context, type); - List args; - - UInt argCount = expr->args.getCount(); - - // If the initializer list was empty, then the user was - // asking for default initialization, which should apply - // to (almost) any type. - // - if(argCount == 0) - { - return getDefaultVal(type.type); - } - - // Now for each argument in the initializer list, - // fill in the appropriate field of the result - if (auto arrayType = as(type)) - { - UInt elementCount = (UInt) GetIntVal(arrayType->ArrayLength); - - for (UInt ee = 0; ee < argCount; ++ee) - { - auto argExpr = expr->args[ee]; - LoweredValInfo argVal = lowerRValueExpr(context, argExpr); - args.add(getSimpleVal(context, argVal)); - } - if(elementCount > argCount) - { - auto irDefaultValue = getSimpleVal(context, getDefaultVal(arrayType->baseType)); - for(UInt ee = argCount; ee < elementCount; ++ee) - { - args.add(irDefaultValue); - } - } - - return LoweredValInfo::simple( - getBuilder()->emitMakeArray(irType, args.getCount(), args.getBuffer())); - } - else if (auto vectorType = as(type)) - { - UInt elementCount = (UInt) GetIntVal(vectorType->elementCount); - - for (UInt ee = 0; ee < argCount; ++ee) - { - auto argExpr = expr->args[ee]; - LoweredValInfo argVal = lowerRValueExpr(context, argExpr); - args.add(getSimpleVal(context, argVal)); - } - if(elementCount > argCount) - { - auto irDefaultValue = getSimpleVal(context, getDefaultVal(vectorType->elementType)); - for(UInt ee = argCount; ee < elementCount; ++ee) - { - args.add(irDefaultValue); - } - } - - return LoweredValInfo::simple( - getBuilder()->emitMakeVector(irType, args.getCount(), args.getBuffer())); - } - else if (auto matrixType = as(type)) - { - UInt rowCount = (UInt) GetIntVal(matrixType->getRowCount()); - - for (UInt rr = 0; rr < argCount; ++rr) - { - auto argExpr = expr->args[rr]; - LoweredValInfo argVal = lowerRValueExpr(context, argExpr); - args.add(getSimpleVal(context, argVal)); - } - if(rowCount > argCount) - { - auto rowType = matrixType->getRowType(); - auto irDefaultValue = getSimpleVal(context, getDefaultVal(rowType)); - - for(UInt rr = argCount; rr < rowCount; ++rr) - { - args.add(irDefaultValue); - } - } - - return LoweredValInfo::simple( - getBuilder()->emitMakeMatrix(irType, args.getCount(), args.getBuffer())); - } - else if (auto declRefType = as(type)) - { - DeclRef declRef = declRefType->declRef; - if (auto aggTypeDeclRef = declRef.as()) - { - UInt argCounter = 0; - for (auto ff : getMembersOfType(aggTypeDeclRef)) - { - if (ff.getDecl()->HasModifier()) - continue; - - UInt argIndex = argCounter++; - if (argIndex < argCount) - { - auto argExpr = expr->args[argIndex]; - LoweredValInfo argVal = lowerRValueExpr(context, argExpr); - args.add(getSimpleVal(context, argVal)); - } - else - { - auto irDefaultValue = getSimpleVal(context, getDefaultVal(ff)); - args.add(irDefaultValue); - } - } - - return LoweredValInfo::simple( - getBuilder()->emitMakeStruct(irType, args.getCount(), args.getBuffer())); - } - } - - // If none of the above cases matched, then we had better - // have zero arguments in the initializer list, in which - // case we are just looking for default initialization. - // - SLANG_UNEXPECTED("unhandled case for initializer list codegen"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - - LoweredValInfo visitBoolLiteralExpr(BoolLiteralExpr* expr) - { - return LoweredValInfo::simple(context->irBuilder->getBoolValue(expr->value)); - } - - LoweredValInfo visitIntegerLiteralExpr(IntegerLiteralExpr* expr) - { - auto type = lowerType(context, expr->type); - return LoweredValInfo::simple(context->irBuilder->getIntValue(type, expr->value)); - } - - LoweredValInfo visitFloatingPointLiteralExpr(FloatingPointLiteralExpr* expr) - { - auto type = lowerType(context, expr->type); - return LoweredValInfo::simple(context->irBuilder->getFloatValue(type, expr->value)); - } - - LoweredValInfo visitStringLiteralExpr(StringLiteralExpr* expr) - { - return LoweredValInfo::simple(context->irBuilder->getStringValue(expr->value.getUnownedSlice())); - } - - LoweredValInfo visitAggTypeCtorExpr(AggTypeCtorExpr* /*expr*/) - { - SLANG_UNIMPLEMENTED_X("codegen for aggregate type constructor expression"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - - // 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. - // - // During lowering of the argument list, we build - // up a list of these "fixup" assignments that need - // to be performed. - struct OutArgumentFixup - { - LoweredValInfo dst; - LoweredValInfo src; - }; - - void addDirectCallArgs( - InvokeExpr* expr, - DeclRef funcDeclRef, - List* ioArgs, - List* ioFixups) - { - UInt argCount = expr->Arguments.getCount(); - UInt argCounter = 0; - for (auto paramDeclRef : getMembersOfType(funcDeclRef)) - { - auto paramDecl = paramDeclRef.getDecl(); - IRType* paramType = lowerType(context, GetType(paramDeclRef)); - - UInt argIndex = argCounter++; - RefPtr argExpr; - if(argIndex < argCount) - { - argExpr = expr->Arguments[argIndex]; - } - else - { - // We have run out of arguments supplied at the call site, - // but there are still parameters remaining. This must mean - // that these parameters have default argument expressions - // associated with them. - argExpr = getInitExpr(paramDeclRef); - - // Assert that such an expression must have been present. - SLANG_ASSERT(argExpr); - - // TODO: The approach we are taking here to default arguments - // is simplistic, and has consequences for the front-end as - // well as binary serialization of modules. - // - // We could consider some more refined approaches where, e.g., - // functions with default arguments generate multiple IR-level - // functions, that compute and provide the default values. - // - // Alternatively, each parameter with defaults could be generated - // into its own callable function that provides the default value, - // so that calling modules can call into a pre-generated function. - // - // Each of these options involves trade-offs, and we need to - // make a conscious decision at some point. - } - - if(paramDecl->HasModifier()) - { - // A `ref` qualified parameter must be implemented with by-reference - // parameter passing, so the argument value should be lowered as - // an l-value. - // - LoweredValInfo loweredArg = lowerLValueExpr(context, argExpr); - - // According to our "calling convention" we need to - // pass a pointer into the callee. Unlike the case for - // `out` and `inout` below, it is never valid to do - // copy-in/copy-out for a `ref` parameter, so we just - // pass in the actual pointer. - // - IRInst* argPtr = getAddress(context, loweredArg, argExpr->loc); - (*ioArgs).add(argPtr); - } - else if (paramDecl->HasModifier() - || paramDecl->HasModifier()) - { - // This is a `out` or `inout` parameter, and so - // the argument must be lowered as an l-value. - - LoweredValInfo loweredArg = lowerLValueExpr(context, argExpr); - - // According to our "calling convention" we need to - // pass a pointer into the callee. - // - // A naive approach would be to just take the address - // of `loweredArg` above and pass it in, but that - // has two issues: - // - // 1. The l-value might not be something that has a single - // well-defined "address" (e.g., `foo.xzy`). - // - // 2. The l-value argument might actually alias some other - // storage that the callee will access (e.g., we are - // passing in a global variable, or two `out` parameters - // are being passed the same location in an array). - // - // In each of these cases, the safe option is to create - // a temporary variable to use for argument-passing, - // and then do copy-in/copy-out around the call. - - LoweredValInfo tempVar = createVar(context, paramType); - - // If the parameter is `in out` or `inout`, then we need - // to ensure that we pass in the original value stored - // in the argument, which we accomplish by assigning - // from the l-value to our temp. - if (paramDecl->HasModifier() - || paramDecl->HasModifier()) - { - assign(context, tempVar, loweredArg); - } - - // Now we can pass the address of the temporary variable - // to the callee as the actual argument for the `in out` - SLANG_ASSERT(tempVar.flavor == LoweredValInfo::Flavor::Ptr); - (*ioArgs).add(tempVar.val); - - // Finally, after the call we will need - // to copy in the other direction: from our - // temp back to the original l-value. - OutArgumentFixup fixup; - fixup.src = tempVar; - fixup.dst = loweredArg; - - (*ioFixups).add(fixup); - - } - else - { - // This is a pure input parameter, and so we will - // pass it as an r-value. - LoweredValInfo loweredArg = lowerRValueExpr(context, argExpr); - addArgs(context, ioArgs, loweredArg); - } - } - } - - // Add arguments that appeared directly in an argument list - // to the list of argument values for a call. - void addDirectCallArgs( - InvokeExpr* expr, - DeclRef funcDeclRef, - List* ioArgs, - List* ioFixups) - { - if (auto callableDeclRef = funcDeclRef.as()) - { - addDirectCallArgs(expr, callableDeclRef, ioArgs, ioFixups); - } - else - { - SLANG_UNEXPECTED("callee was not a callable decl"); - } - } - - void addFuncBaseArgs( - LoweredValInfo funcVal, - List* ioArgs) - { - switch (funcVal.flavor) - { - default: - return; - } - } - - void applyOutArgumentFixups(List const& fixups) - { - for (auto fixup : fixups) - { - assign(context, fixup.dst, fixup.src); - } - } - - struct ResolvedCallInfo - { - DeclRef 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 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 = as(funcExpr); - 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 = as(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 function type, though). - return false; - } - - // Now we can look at the specific kinds of declaration references, - // and try to tease them apart. - if (auto memberFuncExpr = as(funcExpr)) - { - outInfo->funcDeclRef = memberFuncExpr->declRef; - outInfo->baseExpr = memberFuncExpr->BaseExpression; - return true; - } - else if (auto staticMemberFuncExpr = as(funcExpr)) - { - outInfo->funcDeclRef = staticMemberFuncExpr->declRef; - return true; - } - else if (auto varExpr = as(funcExpr)) - { - 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 = lowerType(context, expr->type); - - // We are going to look at the syntactic form of - // the "function" expression, so that we can avoid - // a lot of complexity that would come from lowering - // it as a general expression first, and then trying - // to apply it. For example, given `obj.f(a,b)` we - // will try to detect that we are trying to compute - // something like `ObjType::f(obj, a, b)` (in pseudo-code), - // rather than trying to construct a meaningful - // intermediate value for `obj.f` first. - // - // Note that this doe not preclude having support - // for directly generating code from `obj.f` - it - // just may be that such usage is more complicated. - - // Along the way, we may end up collecting additional - // arguments that will be part of the call. - List irArgs; - - // We will also collect "fixup" actions that need - // to be performed after the call, in order to - // copy the final values for `out` parameters - // back to their arguments. - List argFixups; - - auto funcExpr = expr->FunctionExpr; - ResolvedCallInfo resolvedInfo; - if( tryResolveDeclRefForCall(funcExpr, &resolvedInfo) ) - { - // In this case we know exactly what declaration we - // are going to call, and so we can resolve things - // appropriately. - auto funcDeclRef = resolvedInfo.funcDeclRef; - auto baseExpr = resolvedInfo.baseExpr; - - // First 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. - // - auto funcType = lowerType(context, funcExpr->type); - addDirectCallArgs(expr, funcDeclRef, &irArgs, &argFixups); - auto result = emitCallToDeclRef( - context, - type, - funcDeclRef, - funcType, - irArgs); - applyOutArgumentFixups(argFixups); - return result; - } - - // TODO: In this case we should be emitting code for the callee as - // an ordinary expression, then emitting the arguments according - // to the type information on the callee (e.g., which parameters - // are `out` or `inout`, and then finally emitting the `call` - // instruction. - // - // We don't currently have the case of emitting arguments according - // to function type info (instead of declaration info), and really - // this case can't occur unless we start adding first-class functions - // to the source language. - // - // For now we just bail out with an error. - // - SLANG_UNEXPECTED("could not resolve target declaration for call"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - - LoweredValInfo visitCastToInterfaceExpr( - CastToInterfaceExpr* expr) - { - // We have an expression that is "up-casting" some concrete value - // to an existential type (aka interface type), using a subtype witness - // (which will lower as a witness table) to show that the conversion - // is valid. - // - // At the IR level, this will become a `makeExistential` instruction, - // which collects the above information into a single IR-level value. - // A dynamic CPU implementation of Slang might encode an existential - // as a "fat pointer" representation, which includes a pointer to - // data for the concrete value, plus a pointer to the witness table. - // - // Note: if/when Slang supports more general existential types, such - // as compositions of interface (e.g., `IReadable & IWritable`), then - // we should probably extend the AST and IR mechanism here to accept - // a sequence of witness tables. - // - auto existentialType = lowerType(context, expr->type); - auto concreteValue = getSimpleVal(context, lowerRValueExpr(context, expr->valueArg)); - auto witnessTable = lowerSimpleVal(context, expr->witnessArg); - auto existentialValue = getBuilder()->emitMakeExistential(existentialType, concreteValue, witnessTable); - return LoweredValInfo::simple(existentialValue); - } - - LoweredValInfo subscriptValue( - IRType* type, - LoweredValInfo baseVal, - IRInst* indexVal) - { - auto builder = getBuilder(); - - // The `tryGetAddress` operation will take a complex value representation - // and try to turn it into a single pointer, if possible. - // - baseVal = tryGetAddress(context, baseVal, TryGetAddressMode::Aggressive); - - // The `materialize` operation should ensure that we only have to deal - // with the small number of base cases for lowered value representations. - // - baseVal = materialize(context, baseVal); - - switch (baseVal.flavor) - { - case LoweredValInfo::Flavor::Simple: - return LoweredValInfo::simple( - builder->emitElementExtract( - type, - getSimpleVal(context, baseVal), - indexVal)); - - case LoweredValInfo::Flavor::Ptr: - return LoweredValInfo::ptr( - builder->emitElementAddress( - context->irBuilder->getPtrType(type), - baseVal.val, - indexVal)); - - default: - SLANG_UNIMPLEMENTED_X("subscript expr"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - - } - - LoweredValInfo extractField( - IRType* fieldType, - LoweredValInfo base, - DeclRef field) - { - return Slang::extractField(context, fieldType, base, field); - } - - LoweredValInfo visitStaticMemberExpr(StaticMemberExpr* expr) - { - return emitDeclRef(context, expr->declRef, - lowerType(context, expr->type)); - } - - LoweredValInfo visitGenericAppExpr(GenericAppExpr* /*expr*/) - { - SLANG_UNIMPLEMENTED_X("generic application expression during code generation"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - - LoweredValInfo visitSharedTypeExpr(SharedTypeExpr* /*expr*/) - { - SLANG_UNIMPLEMENTED_X("shared type expression during code generation"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - - LoweredValInfo visitTaggedUnionTypeExpr(TaggedUnionTypeExpr* /*expr*/) - { - SLANG_UNIMPLEMENTED_X("tagged union type expression during code generation"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - - LoweredValInfo visitAssignExpr(AssignExpr* expr) - { - // Because our representation of lowered "values" - // can encompass l-values explicitly, we can - // lower assignment easily. We just lower the left- - // and right-hand sides, and then perform an assignment - // based on the resulting values. - // - auto leftVal = lowerLValueExpr(context, expr->left); - auto rightVal = lowerRValueExpr(context, expr->right); - assign(context, leftVal, rightVal); - - // The result value of the assignment expression is - // the value of the left-hand side (and it is expected - // to be an l-value). - return leftVal; - } - - LoweredValInfo visitLetExpr(LetExpr* expr) - { - // TODO: deal with the case where we might want to capture - // a reference to the bound value... - - auto initVal = lowerLValueExpr(context, expr->decl->initExpr); - setGlobalValue(context, expr->decl, initVal); - auto bodyVal = lowerSubExpr(expr->body); - return bodyVal; - } - - LoweredValInfo visitExtractExistentialValueExpr(ExtractExistentialValueExpr* expr) - { - auto existentialType = lowerType(context, GetType(expr->declRef)); - auto existentialVal = getSimpleVal(context, emitDeclRef(context, expr->declRef, existentialType)); - - auto openedType = lowerType(context, expr->type); - - return LoweredValInfo::simple(getBuilder()->emitExtractExistentialValue(openedType, existentialVal)); - } -}; - -struct LValueExprLoweringVisitor : ExprLoweringVisitorBase -{ - // When visiting a swizzle expression in an l-value context, - // we need to construct a "sizzled l-value." - LoweredValInfo visitSwizzleExpr(SwizzleExpr* expr) - { - auto irType = lowerType(context, expr->type); - auto loweredBase = lowerRValueExpr(context, expr->base); - - RefPtr swizzledLValue = new SwizzledLValueInfo(); - swizzledLValue->type = irType; - - UInt elementCount = (UInt)expr->elementCount; - swizzledLValue->elementCount = elementCount; - - // As a small optimization, we will detect if the base expression - // has also lowered into a swizzle and only return a single - // swizzle instead of nested swizzles. - // - // E.g., if we have input like `foo[i].zw.y` we should optimize it - // down to just `foo[i].w`. - // - if(loweredBase.flavor == LoweredValInfo::Flavor::SwizzledLValue) - { - auto baseSwizzleInfo = loweredBase.getSwizzledLValueInfo(); - - // Our new swizzle will use the same base expression (e.g., - // `foo[i]` in our example above), but will need to remap - // the swizzle indices it uses. - // - swizzledLValue->base = baseSwizzleInfo->base; - for (UInt ii = 0; ii < elementCount; ++ii) - { - // First we get the swizzle element of the "outer" swizzle, - // as it was written by the user. In our running example of - // `foo[i].zw.y` this is the `y` element reference. - // - UInt originalElementIndex = UInt(expr->elementIndices[ii]); - - // Next we will use that original element index to figure - // out which of the elements of the original swizzle this - // should map to. - // - // In our example, `y` means index 1, and so we fetch - // element 1 from the inner swizzle sequence `zw`, to get `w`. - // - SLANG_ASSERT(originalElementIndex < baseSwizzleInfo->elementCount); - UInt remappedElementIndex = baseSwizzleInfo->elementIndices[originalElementIndex]; - - swizzledLValue->elementIndices[ii] = remappedElementIndex; - } - } - else - { - // In the default case, we can just copy the indices being - // used for the swizzle over directly from the expression, - // and use the base as-is. - // - swizzledLValue->base = loweredBase; - for (UInt ii = 0; ii < elementCount; ++ii) - { - swizzledLValue->elementIndices[ii] = (UInt) expr->elementIndices[ii]; - } - } - - context->shared->extValues.add(swizzledLValue); - return LoweredValInfo::swizzledLValue(swizzledLValue); - } -}; - -struct RValueExprLoweringVisitor : ExprLoweringVisitorBase -{ - // A swizzle in an r-value context can save time by just - // emitting the swizzle instructions directly. - LoweredValInfo visitSwizzleExpr(SwizzleExpr* expr) - { - auto irType = lowerType(context, expr->type); - auto irBase = getSimpleVal(context, lowerRValueExpr(context, expr->base)); - - auto builder = getBuilder(); - - auto irIntType = getIntType(context); - - UInt elementCount = (UInt)expr->elementCount; - IRInst* irElementIndices[4]; - for (UInt ii = 0; ii < elementCount; ++ii) - { - irElementIndices[ii] = builder->getIntValue( - irIntType, - (IRIntegerValue)expr->elementIndices[ii]); - } - - auto irSwizzle = builder->emitSwizzle( - irType, - irBase, - elementCount, - &irElementIndices[0]); - - return LoweredValInfo::simple(irSwizzle); - } -}; - -LoweredValInfo lowerLValueExpr( - IRGenContext* context, - Expr* expr) -{ - IRBuilderSourceLocRAII sourceLocInfo(context->irBuilder, expr->loc); - - LValueExprLoweringVisitor visitor; - visitor.context = context; - return visitor.dispatch(expr); -} - -LoweredValInfo lowerRValueExpr( - IRGenContext* context, - Expr* expr) -{ - IRBuilderSourceLocRAII sourceLocInfo(context->irBuilder, expr->loc); - - RValueExprLoweringVisitor visitor; - visitor.context = context; - return visitor.dispatch(expr); -} - -struct StmtLoweringVisitor : StmtVisitor -{ - IRGenContext* context; - - IRBuilder* getBuilder() { return context->irBuilder; } - - void visitEmptyStmt(EmptyStmt*) - { - // Nothing to do. - } - - void visitUnparsedStmt(UnparsedStmt*) - { - SLANG_UNEXPECTED("UnparsedStmt not supported by IR"); - } - - void visitCaseStmtBase(CaseStmtBase*) - { - SLANG_UNEXPECTED("`case` or `default` not under `switch`"); - } - - void visitCompileTimeForStmt(CompileTimeForStmt* stmt) - { - // The user is asking us to emit code for the loop - // body for each value in the given integer range. - // For now, we will handle this by repeatedly lowering - // the body statement, with the loop variable bound - // to a different integer literal value each time. - // - // TODO: eventually we might handle this as just an - // ordinary loop, with an `[unroll]` attribute on - // it that we would respect. - - auto rangeBeginVal = GetIntVal(stmt->rangeBeginVal); - auto rangeEndVal = GetIntVal(stmt->rangeEndVal); - - if (rangeBeginVal >= rangeEndVal) - return; - - auto varDecl = stmt->varDecl; - auto varType = lowerType(context, varDecl->type); - - IRGenEnv subEnvStorage; - IRGenEnv* subEnv = &subEnvStorage; - subEnv->outer = context->env; - - IRGenContext subContextStorage = *context; - IRGenContext* subContext = &subContextStorage; - subContext->env = subEnv; - - - - for (IntegerLiteralValue ii = rangeBeginVal; ii < rangeEndVal; ++ii) - { - auto constVal = getBuilder()->getIntValue( - varType, - ii); - - subEnv->mapDeclToValue[varDecl] = LoweredValInfo::simple(constVal); - - lowerStmt(subContext, stmt->body); - } - } - - // Create a basic block in the current function, - // so that it can be used for a label. - IRBlock* createBlock() - { - return getBuilder()->createBlock(); - } - - /// Does the given block have a terminator? - bool isBlockTerminated(IRBlock* block) - { - return block->getTerminator() != nullptr; - } - - /// Emit a branch to the target block if the current - /// block being inserted into is not already terminated. - void emitBranchIfNeeded(IRBlock* targetBlock) - { - auto builder = getBuilder(); - auto currentBlock = builder->getBlock(); - - // Don't emit if there is no current block. - if(!currentBlock) - return; - - // Don't emit if the block already has a terminator. - if(isBlockTerminated(currentBlock)) - return; - - // The block is unterminated, so cap it off with - // a terminator that branches to the target. - builder->emitBranch(targetBlock); - } - - /// Insert a block at the current location (ending - /// the previous block with an unconditional jump - /// if needed). - void insertBlock(IRBlock* block) - { - auto builder = getBuilder(); - - auto prevBlock = builder->getBlock(); - auto parentFunc = prevBlock ? prevBlock->getParent() : builder->getFunc(); - - // If the previous block doesn't already have - // a terminator instruction, then be sure to - // emit a branch to the new block. - emitBranchIfNeeded(block); - - // Add the new block to the function we are building, - // and setit as the block we will be inserting into. - parentFunc->addBlock(block); - builder->setInsertInto(block); - } - - // Start a new block at the current location. - // This is just the composition of `createBlock` - // and `insertBlock`. - IRBlock* startBlock() - { - auto block = createBlock(); - insertBlock(block); - return block; - } - - /// Start a new block if there isn't a current - /// block that we can append to. - /// - /// The `stmt` parameter is the statement we - /// are about to emit. - void startBlockIfNeeded(Stmt* stmt) - { - auto builder = getBuilder(); - auto currentBlock = builder->getBlock(); - - // If there is a current block and it hasn't - // been terminated, then we can just use that. - if(currentBlock && !isBlockTerminated(currentBlock)) - { - return; - } - - // We are about to emit code *after* a terminator - // instruction, and there is no label to allow - // branching into this code, so whatever we are - // about to emit is going to be unreachable. - // - // Let's diagnose that here just to help the user. - // - // TODO: We might want to have a more robust check - // for unreachable code based on IR analysis instead, - // at which point we'd probably disable this check. - // - context->getSink()->diagnose(stmt, Diagnostics::unreachableCode); - - startBlock(); - } - - void visitIfStmt(IfStmt* stmt) - { - auto builder = getBuilder(); - startBlockIfNeeded(stmt); - - auto condExpr = stmt->Predicate; - auto thenStmt = stmt->PositiveStatement; - auto elseStmt = stmt->NegativeStatement; - - auto irCond = getSimpleVal(context, - lowerRValueExpr(context, condExpr)); - - if (elseStmt) - { - auto thenBlock = createBlock(); - auto elseBlock = createBlock(); - auto afterBlock = createBlock(); - - builder->emitIfElse(irCond, thenBlock, elseBlock, afterBlock); - - insertBlock(thenBlock); - lowerStmt(context, thenStmt); - emitBranchIfNeeded(afterBlock); - - insertBlock(elseBlock); - lowerStmt(context, elseStmt); - - insertBlock(afterBlock); - } - else - { - auto thenBlock = createBlock(); - auto afterBlock = createBlock(); - - builder->emitIf(irCond, thenBlock, afterBlock); - - insertBlock(thenBlock); - lowerStmt(context, thenStmt); - - insertBlock(afterBlock); - } - } - - void addLoopDecorations( - IRInst* inst, - Stmt* stmt) - { - if( stmt->FindModifier() ) - { - getBuilder()->addLoopControlDecoration(inst, kIRLoopControl_Unroll); - } - // TODO: handle other cases here - } - - void visitForStmt(ForStmt* stmt) - { - auto builder = getBuilder(); - startBlockIfNeeded(stmt); - - // The initializer clause for the statement - // can always safetly be emitted to the current block. - if (auto initStmt = stmt->InitialStatement) - { - lowerStmt(context, initStmt); - } - - // We will create blocks for the various places - // we need to jump to inside the control flow, - // including the blocks that will be referenced - // by `continue` or `break` statements. - auto loopHead = createBlock(); - auto bodyLabel = createBlock(); - auto breakLabel = createBlock(); - auto continueLabel = createBlock(); - - // Register the `break` and `continue` labels so - // that we can find them for nested statements. - context->shared->breakLabels.Add(stmt, breakLabel); - context->shared->continueLabels.Add(stmt, continueLabel); - - // Emit the branch that will start out loop, - // and then insert the block for the head. - - auto loopInst = builder->emitLoop( - loopHead, - breakLabel, - continueLabel); - - addLoopDecorations(loopInst, stmt); - - insertBlock(loopHead); - - // Now that we are within the header block, we - // want to emit the expression for the loop condition: - if (auto condExpr = stmt->PredicateExpression) - { - auto irCondition = getSimpleVal(context, - lowerRValueExpr(context, stmt->PredicateExpression)); - - // Now we want to `break` if the loop condition is false. - builder->emitLoopTest( - irCondition, - bodyLabel, - breakLabel); - } - - // Emit the body of the loop - insertBlock(bodyLabel); - lowerStmt(context, stmt->Statement); - - // Insert the `continue` block - insertBlock(continueLabel); - if (auto incrExpr = stmt->SideEffectExpression) - { - lowerRValueExpr(context, incrExpr); - } - - // At the end of the body we need to jump back to the top. - emitBranchIfNeeded(loopHead); - - // Finally we insert the label that a `break` will jump to - insertBlock(breakLabel); - } - - void visitWhileStmt(WhileStmt* stmt) - { - // Generating IR for `while` statement is similar to a - // `for` statement, but without a lot of the complications. - - auto builder = getBuilder(); - startBlockIfNeeded(stmt); - - // We will create blocks for the various places - // we need to jump to inside the control flow, - // including the blocks that will be referenced - // by `continue` or `break` statements. - auto loopHead = createBlock(); - auto bodyLabel = createBlock(); - auto breakLabel = createBlock(); - - // A `continue` inside a `while` loop always - // jumps to the head of hte loop. - auto continueLabel = loopHead; - - // Register the `break` and `continue` labels so - // that we can find them for nested statements. - context->shared->breakLabels.Add(stmt, breakLabel); - context->shared->continueLabels.Add(stmt, continueLabel); - - // Emit the branch that will start out loop, - // and then insert the block for the head. - - auto loopInst = builder->emitLoop( - loopHead, - breakLabel, - continueLabel); - - addLoopDecorations(loopInst, stmt); - - insertBlock(loopHead); - - // Now that we are within the header block, we - // want to emit the expression for the loop condition: - if (auto condExpr = stmt->Predicate) - { - auto irCondition = getSimpleVal(context, - lowerRValueExpr(context, condExpr)); - - // Now we want to `break` if the loop condition is false. - builder->emitLoopTest( - irCondition, - bodyLabel, - breakLabel); - } - - // Emit the body of the loop - insertBlock(bodyLabel); - lowerStmt(context, stmt->Statement); - - // At the end of the body we need to jump back to the top. - emitBranchIfNeeded(loopHead); - - // Finally we insert the label that a `break` will jump to - insertBlock(breakLabel); - } - - void visitDoWhileStmt(DoWhileStmt* stmt) - { - // Generating IR for `do {...} while` statement is similar to a - // `while` statement, just with the test in a different place - - auto builder = getBuilder(); - startBlockIfNeeded(stmt); - - // We will create blocks for the various places - // we need to jump to inside the control flow, - // including the blocks that will be referenced - // by `continue` or `break` statements. - auto loopHead = createBlock(); - auto testLabel = createBlock(); - auto breakLabel = createBlock(); - - // A `continue` inside a `do { ... } while ( ... )` loop always - // jumps to the loop test. - auto continueLabel = testLabel; - - // Register the `break` and `continue` labels so - // that we can find them for nested statements. - context->shared->breakLabels.Add(stmt, breakLabel); - context->shared->continueLabels.Add(stmt, continueLabel); - - // Emit the branch that will start out loop, - // and then insert the block for the head. - - auto loopInst = builder->emitLoop( - loopHead, - breakLabel, - continueLabel); - - addLoopDecorations(loopInst, stmt); - - insertBlock(loopHead); - - // Emit the body of the loop - lowerStmt(context, stmt->Statement); - - insertBlock(testLabel); - - // Now that we are within the header block, we - // want to emit the expression for the loop condition: - if (auto condExpr = stmt->Predicate) - { - auto irCondition = getSimpleVal(context, - lowerRValueExpr(context, condExpr)); - - // Now we want to `break` if the loop condition is false, - // otherwise we will jump back to the head of the loop. - builder->emitLoopTest( - irCondition, - loopHead, - breakLabel); - } - - // Finally we insert the label that a `break` will jump to - insertBlock(breakLabel); - } - - void visitExpressionStmt(ExpressionStmt* stmt) - { - startBlockIfNeeded(stmt); - - // The statement evaluates an expression - // (for side effects, one assumes) and then - // discards the result. As such, we simply - // lower the expression, and don't use - // the result. - // - // Note that we lower using the l-value path, - // so that an expression statement that names - // a location (but doesn't load from it) - // will not actually emit a load. - lowerLValueExpr(context, stmt->Expression); - } - - void visitDeclStmt(DeclStmt* stmt) - { - startBlockIfNeeded(stmt); - - // For now, we lower a declaration directly - // into the current context. - // - // TODO: We may want to consider whether - // nested type/function declarations should - // be lowered into the global scope during - // IR generation, or whether they should - // be lifted later (pushing capture analysis - // down to the IR). - // - lowerDecl(context, stmt->decl); - } - - void visitSeqStmt(SeqStmt* stmt) - { - // To lower a sequence of statements, - // just lower each in order - for (auto ss : stmt->stmts) - { - lowerStmt(context, ss); - } - } - - void visitBlockStmt(BlockStmt* stmt) - { - // To lower a block (scope) statement, - // just lower its body. The IR doesn't - // need to reflect the scoping of the AST. - lowerStmt(context, stmt->body); - } - - void visitReturnStmt(ReturnStmt* stmt) - { - startBlockIfNeeded(stmt); - - // A `return` statement turns into a return - // instruction. If the statement had an argument - // expression, then we need to lower that to - // a value first, and then emit the resulting value. - if( auto expr = stmt->Expression ) - { - auto loweredExpr = lowerRValueExpr(context, expr); - - getBuilder()->emitReturn(getSimpleVal(context, loweredExpr)); - } - else - { - getBuilder()->emitReturn(); - } - } - - void visitDiscardStmt(DiscardStmt* stmt) - { - startBlockIfNeeded(stmt); - getBuilder()->emitDiscard(); - } - - void visitBreakStmt(BreakStmt* stmt) - { - startBlockIfNeeded(stmt); - - // Semantic checking is responsible for finding - // the statement taht this `break` breaks out of - auto parentStmt = stmt->parentStmt; - SLANG_ASSERT(parentStmt); - - // We just need to look up the basic block that - // corresponds to the break label for that statement, - // and then emit an instruction to jump to it. - IRBlock* targetBlock = nullptr; - context->shared->breakLabels.TryGetValue(parentStmt, targetBlock); - SLANG_ASSERT(targetBlock); - getBuilder()->emitBreak(targetBlock); - } - - void visitContinueStmt(ContinueStmt* stmt) - { - startBlockIfNeeded(stmt); - - // Semantic checking is responsible for finding - // the loop that this `continue` statement continues - auto parentStmt = stmt->parentStmt; - SLANG_ASSERT(parentStmt); - - - // We just need to look up the basic block that - // corresponds to the continue label for that statement, - // and then emit an instruction to jump to it. - IRBlock* targetBlock = nullptr; - context->shared->continueLabels.TryGetValue(parentStmt, targetBlock); - SLANG_ASSERT(targetBlock); - getBuilder()->emitContinue(targetBlock); - } - - // Lowering a `switch` statement can get pretty involved, - // so we need to track a bit of extra data: - struct SwitchStmtInfo - { - // The block that will be made to contain the `switch` statement - IRBlock* initialBlock = nullptr; - - // The label for the `default` case, if any. - IRBlock* defaultLabel = nullptr; - - // The label of the current "active" case block. - IRBlock* currentCaseLabel = nullptr; - - // Has anything been emitted to the current "active" case block? - bool anythingEmittedToCurrentCaseBlock = false; - - // The collected (value, label) pairs for - // all the `case` statements. - List cases; - }; - - // We need a label to use for a `case` or `default` statement, - // so either create one here, or re-use the current one if - // that is okay. - IRBlock* getLabelForCase(SwitchStmtInfo* info) - { - // Look at the "current" label we are working with. - auto currentCaseLabel = info->currentCaseLabel; - - // If there is a current block, and it is empty, - // then it is still a viable target (we are in - // a case of "trivial fall-through" from the previous - // block). - if(currentCaseLabel && !info->anythingEmittedToCurrentCaseBlock) - { - return currentCaseLabel; - } - - // Othwerise, we need to start a new block and use that. - IRBlock* newCaseLabel = createBlock(); - - // Note: if the previous block failed - // to end with a `break`, then inserting - // this block will append an unconditional - // branch to the end of it that will target - // this block. - insertBlock(newCaseLabel); - - info->currentCaseLabel = newCaseLabel; - info->anythingEmittedToCurrentCaseBlock = false; - return newCaseLabel; - } - - // Given a statement that appears as (or in) the body - // of a `switch` statement - void lowerSwitchCases(Stmt* inStmt, SwitchStmtInfo* info) - { - // TODO: in the general case (e.g., if we were going - // to eventual lower to an unstructured format like LLVM), - // the Right Way to handle C-style `switch` statements - // is just to emit the body directly as "normal" statements, - // and then treat `case` and `default` as special statements - // that start a new block and register a label with the - // enclosing `switch`. - // - // For now we will assume that any `case` and `default` - // statements need to be directly nested under the `switch`, - // and so we can find them with a simpler walk. - - Stmt* stmt = inStmt; - - // Unwrap any surrounding `{ ... }` so we can look - // at the statement inside. - while(auto blockStmt = as(stmt)) - { - stmt = blockStmt->body; - continue; - } - - if(auto seqStmt = as(stmt)) - { - // Walk through teh children and process each. - for(auto childStmt : seqStmt->stmts) - { - lowerSwitchCases(childStmt, info); - } - } - else if(auto caseStmt = as(stmt)) - { - // A full `case` statement has a value we need - // to test against. It is expected to be a - // compile-time constant, so we will emit - // it like an expression here, and then hope - // for the best. - // - // TODO: figure out something cleaner. - - // Actually, one gotcha is that if we ever allow non-constant - // expressions here (or anything that requires instructions - // to be emitted to yield its value), then those instructions - // need to go into an appropriate block. - - IRGenContext subContext = *context; - IRBuilder subBuilder = *getBuilder(); - subBuilder.setInsertInto(info->initialBlock); - subContext.irBuilder = &subBuilder; - auto caseVal = getSimpleVal(context, lowerRValueExpr(&subContext, caseStmt->expr)); - - // Figure out where we are branching to. - auto label = getLabelForCase(info); - - // Add this `case` to the list for the enclosing `switch`. - info->cases.add(caseVal); - info->cases.add(label); - } - else if(auto defaultStmt = as(stmt)) - { - auto label = getLabelForCase(info); - - // We expect to only find a single `default` stmt. - SLANG_ASSERT(!info->defaultLabel); - - info->defaultLabel = label; - } - else if(auto emptyStmt = as(stmt)) - { - // Special-case empty statements so they don't - // mess up our "trivial fall-through" optimization. - } - else - { - // We have an ordinary statement, that needs to get - // emitted to the current case block. - if(!info->currentCaseLabel) - { - // It possible in full C/C++ to have statements - // before the first `case`. Usually these are - // unreachable, unless they start with a label. - // - // We'll ignore them here, figuring they are - // dead. If we ever add `LabelStmt` then we'd - // need to emit these statements to a dummy - // block just in case. - } - else - { - // Emit the code to our current case block, - // and record that we've done so. - lowerStmt(context, stmt); - info->anythingEmittedToCurrentCaseBlock = true; - } - } - } - - void visitSwitchStmt(SwitchStmt* stmt) - { - auto builder = getBuilder(); - startBlockIfNeeded(stmt); - - // Given a statement: - // - // switch( CONDITION ) - // { - // case V0: - // S0; - // break; - // - // case V1: - // default: - // S1; - // break; - // } - // - // we want to generate IR like: - // - // let %c = ; - // switch %c, // value to switch on - // %breakLabel, // join point (and break target) - // %s1, // default label - // %v0, // first case value - // %s0, // first case label - // %v1, // second case value - // %s1 // second case label - // s0: - // - // break %breakLabel - // s1: - // - // break %breakLabel - // breakLabel: - // - - // First emit code to compute the condition: - auto conditionVal = getSimpleVal(context, lowerRValueExpr(context, stmt->condition)); - - // Remember the initial block so that we can add to it - // after we've collected all the `case`s - auto initialBlock = builder->getBlock(); - - // Next, create a block to use as the target for any `break` statements - auto breakLabel = createBlock(); - - // Register the `break` label so - // that we can find it for nested statements. - context->shared->breakLabels.Add(stmt, breakLabel); - - builder->setInsertInto(initialBlock->getParent()); - - // Iterate over the body of the statement, looking - // for `case` or `default` statements: - SwitchStmtInfo info; - info.initialBlock = initialBlock; - info.defaultLabel = nullptr; - lowerSwitchCases(stmt->body, &info); - - // TODO: once we've discovered the cases, we should - // be able to make a quick pass over the list and eliminate - // any cases that have the exact same label as the `default` - // case, since these don't actually need to be represented. - - // If the current block (the end of the last - // `case`) is not terminated, then terminate with a - // `break` operation. - // - // Double check that we aren't in the initial - // block, so we don't get tripped up on an - // empty `switch`. - auto curBlock = builder->getBlock(); - if(curBlock != initialBlock) - { - // Is the block already terminated? - if(!curBlock->getTerminator()) - { - // Not terminated, so add one. - builder->emitBreak(breakLabel); - } - } - - // If there was no `default` statement, then the - // default case will just branch directly to the end. - auto defaultLabel = info.defaultLabel ? info.defaultLabel : breakLabel; - - // Now that we've collected the cases, we are - // prepared to emit the `switch` instruction - // itself. - builder->setInsertInto(initialBlock); - builder->emitSwitch( - conditionVal, - breakLabel, - defaultLabel, - info.cases.getCount(), - info.cases.getBuffer()); - - // Finally we insert the label that a `break` will jump to - // (and that control flow will fall through to otherwise). - // This is the block that subsequent code will go into. - insertBlock(breakLabel); - context->shared->breakLabels.Remove(stmt); - } -}; - -void lowerStmt( - IRGenContext* context, - Stmt* stmt) -{ - IRBuilderSourceLocRAII sourceLocInfo(context->irBuilder, stmt->loc); - - StmtLoweringVisitor visitor; - visitor.context = context; - - try - { - visitor.dispatch(stmt); - } - // Don't emit any context message for an explicit `AbortCompilationException` - // because it should only happen when an error is already emitted. - catch(AbortCompilationException&) { throw; } - catch(...) - { - context->getSink()->noteInternalErrorLoc(stmt->loc); - throw; - } -} - -/// Create and return a mutable temporary initialized with `val` -static LoweredValInfo moveIntoMutableTemp( - IRGenContext* context, - LoweredValInfo const& val) -{ - IRInst* irVal = getSimpleVal(context, val); - auto type = irVal->getDataType(); - auto var = createVar(context, type); - - assign(context, var, LoweredValInfo::simple(irVal)); - return var; -} - -LoweredValInfo tryGetAddress( - IRGenContext* context, - LoweredValInfo const& inVal, - TryGetAddressMode mode) -{ - LoweredValInfo val = inVal; - - switch(val.flavor) - { - case LoweredValInfo::Flavor::Ptr: - // The `Ptr` case means that we already have an IR value with - // the address of our value. Easy! - return val; - - case LoweredValInfo::Flavor::BoundSubscript: - { - // If we are are trying to turn a subscript operation like `buffer[index]` - // into a pointer, then we need to find a `ref` accessor declared - // as part of the subscript operation being referenced. - // - auto subscriptInfo = val.getBoundSubscriptInfo(); - - // We don't want to immediately bind to a `ref` accessor if there is - // a `set` accessor available, unless we are in an "aggressive" mode - // where we really want/need a pointer to be able to make progress. - // - if(mode != TryGetAddressMode::Aggressive - && getMembersOfType(subscriptInfo->declRef).Count()) - { - // There is a setter that we should consider using, - // so don't go and aggressively collapse things just yet. - return val; - } - - auto refAccessors = getMembersOfType(subscriptInfo->declRef); - if(refAccessors.Count()) - { - // The `ref` accessor will return a pointer to the value, so - // we need to reflect that in the type of our `call` instruction. - IRType* ptrType = context->irBuilder->getPtrType(subscriptInfo->type); - - LoweredValInfo refVal = emitCallToDeclRef( - context, - ptrType, - *refAccessors.begin(), - nullptr, - subscriptInfo->args); - - // The result from the call should be a pointer, and it - // is the address that we wanted in the first place. - return LoweredValInfo::ptr(getSimpleVal(context, refVal)); - } - - // Otherwise, there was no `ref` accessor, and so it is not possible - // to materialize this location into a pointer for whatever purpose - // we have in mind (e.g., passing it to an atomic operation). - } - break; - - case LoweredValInfo::Flavor::BoundMember: - { - auto boundMemberInfo = val.getBoundMemberInfo(); - - // If we hit this case, then it means that we have a reference - // to a single field in something, but for whatever reason the - // higher-level logic was not able to turn it into a pointer - // already (maybe the base value for the field reference is - // a `BoundSubscript`, etc.). - // - // We need to read the entire base value out, modify the field - // we care about, and then write it back. - - auto declRef = boundMemberInfo->declRef; - if( auto fieldDeclRef = declRef.as() ) - { - auto baseVal = boundMemberInfo->base; - auto basePtr = tryGetAddress(context, baseVal, TryGetAddressMode::Aggressive); - - return extractField(context, boundMemberInfo->type, basePtr, fieldDeclRef); - } - - } - break; - - case LoweredValInfo::Flavor::SwizzledLValue: - { - auto originalSwizzleInfo = val.getSwizzledLValueInfo(); - auto originalBase = originalSwizzleInfo->base; - - UInt elementCount = originalSwizzleInfo->elementCount; - - auto newBase = tryGetAddress(context, originalBase, TryGetAddressMode::Aggressive); - RefPtr newSwizzleInfo = new SwizzledLValueInfo(); - context->shared->extValues.add(newSwizzleInfo); - - newSwizzleInfo->base = newBase; - newSwizzleInfo->type = originalSwizzleInfo->type; - newSwizzleInfo->elementCount = elementCount; - for(UInt ee = 0; ee < elementCount; ++ee) - newSwizzleInfo->elementIndices[ee] = originalSwizzleInfo->elementIndices[ee]; - - return LoweredValInfo::swizzledLValue(newSwizzleInfo); - } - break; - - // TODO: are there other cases we need to handled here? - - default: - break; - } - - // If none of the special cases above applied, then we werent' able to make - // this value into a pointer, and we should just return it as-is. - return val; -} - -IRInst* getAddress( - IRGenContext* context, - LoweredValInfo const& inVal, - SourceLoc diagnosticLocation) -{ - LoweredValInfo val = tryGetAddress(context, inVal, TryGetAddressMode::Aggressive); - - if( val.flavor == LoweredValInfo::Flavor::Ptr ) - { - return val.val; - } - - context->getSink()->diagnose(diagnosticLocation, Diagnostics::invalidLValueForRefParameter); - return nullptr; -} - -void assign( - IRGenContext* context, - LoweredValInfo const& inLeft, - LoweredValInfo const& inRight) -{ - LoweredValInfo left = inLeft; - LoweredValInfo right = inRight; - - // Before doing the case analysis on the shape of the `left` value, - // we might as well go ahead and see if we can coerce it into - // a simple pointer, since that would make our life a lot easier - // when handling complex cases. - // - left = tryGetAddress(context, left, TryGetAddressMode::Default); - - auto builder = context->irBuilder; - -top: - switch (left.flavor) - { - case LoweredValInfo::Flavor::Ptr: - { - // The `left` value is just a pointer, so we can emit - // a store to it directly. - // - builder->emitStore( - left.val, - getSimpleVal(context, right)); - } - break; - - case LoweredValInfo::Flavor::SwizzledLValue: - { - // The `left` value is of the form `.`. - // How we will handle this depends on what `base` looks like: - auto swizzleInfo = left.getSwizzledLValueInfo(); - auto loweredBase = swizzleInfo->base; - - // Note that the call to `tryGetAddress` at the start should - // ensure that `loweredBase` has been simplified as much as - // possible (e.g., if it is possible to turn it into a - // `LoweredValInfo::ptr()` then that will have been done). - - switch( loweredBase.flavor ) - { - default: - { - // Our fallback position is to lower via a temporary, e.g.: - // - // float4 tmp = ; - // tmp.xyz = float3(...); - // = tmp; - // - - // Load from the base value - IRInst* irLeftVal = getSimpleVal(context, loweredBase); - - // Extract a simple value for the right-hand side - IRInst* irRightVal = getSimpleVal(context, right); - - // Apply the swizzle - IRInst* irSwizzled = builder->emitSwizzleSet( - irLeftVal->getDataType(), - irLeftVal, - irRightVal, - swizzleInfo->elementCount, - swizzleInfo->elementIndices); - - // And finally, store the value back where we got it. - // - // Note: this is effectively a recursive call to - // `assign()`, so we do a simple tail-recursive call here. - left = loweredBase; - right = LoweredValInfo::simple(irSwizzled); - goto top; - } - break; - - case LoweredValInfo::Flavor::Ptr: - { - // We are writing through a pointer, which might be - // pointing into a UAV or other memory resource, so - // we can't introduce use a temporary like the case - // above, because then we would read and write bytes - // that are not strictly required for the store. - // - // Note that the messy case of a "swizzle of a swizzle" - // was handled already in lowering of a `SwizzleExpr`, - // so that we don't need to deal with that case here. - // - // TODO: we may need to consider whether there is - // enough value in a masked store like this to keep - // it around, in comparison to a simpler model where - // we simply form a pointer to each of the vector - // elements and write to them individually. - // - // TODO: we might also consider just special-casing - // single-element swizzles so that the common case - // can turn into a simple `store` instead of a - // `swizzledStore`. - // - IRInst* irRightVal = getSimpleVal(context, right); - builder->emitSwizzledStore( - loweredBase.val, - irRightVal, - swizzleInfo->elementCount, - swizzleInfo->elementIndices); - } - break; - } - } - break; - - case LoweredValInfo::Flavor::BoundSubscript: - { - // The `left` value refers to a subscript operation on - // a resource type, bound to particular arguments, e.g.: - // `someStructuredBuffer[index]`. - // - // When storing to such a value, we need to emit a call - // to the appropriate builtin "setter" accessor, if there - // is one, and then fall back to a `ref` accessor if - // there is no setter. - // - auto subscriptInfo = left.getBoundSubscriptInfo(); - - // Search for an appropriate "setter" declaration - auto setters = getMembersOfType(subscriptInfo->declRef); - if (setters.Count()) - { - auto allArgs = subscriptInfo->args; - addArgs(context, &allArgs, right); - - emitCallToDeclRef( - context, - builder->getVoidType(), - *setters.begin(), - nullptr, - allArgs); - return; - } - - auto refAccessors = getMembersOfType(subscriptInfo->declRef); - if(refAccessors.Count()) - { - // The `ref` accessor will return a pointer to the value, so - // we need to reflect that in the type of our `call` instruction. - IRType* ptrType = context->irBuilder->getPtrType(subscriptInfo->type); - - LoweredValInfo refVal = emitCallToDeclRef( - context, - ptrType, - *refAccessors.begin(), - nullptr, - subscriptInfo->args); - - // The result from the call needs to be implicitly dereferenced, - // so that it can work as an l-value of the desired result type. - left = LoweredValInfo::ptr(getSimpleVal(context, refVal)); - - // Tail-recursively attempt assignment again on the new l-value. - goto top; - } - - // No setter found? Then we have an error! - SLANG_UNEXPECTED("no setter found"); - break; - } - break; - - case LoweredValInfo::Flavor::BoundMember: - { - auto boundMemberInfo = left.getBoundMemberInfo(); - - // If we hit this case, then it means that we are trying to set - // a single field in someting that is not atomically set-able. - // (e.g., an element of a value where the `subscript` operation - // has `get` and `set` but not a `ref` accessor). - // - // We need to read the entire base value out, modify the field - // we care about, and then write it back. - - auto declRef = boundMemberInfo->declRef; - if( auto fieldDeclRef = declRef.as() ) - { - // materialize the base value and move it into - // a mutable temporary if needed - auto baseVal = boundMemberInfo->base; - auto tempVal = moveIntoMutableTemp(context, baseVal); - - // extract the field l-value out of the temporary - auto tempFieldVal = extractField(context, boundMemberInfo->type, tempVal, fieldDeclRef); - - // assign to the field of the temporary l-value - assign(context, tempFieldVal, right); - - // write back the modified temporary to the base l-value - assign(context, baseVal, tempVal); - - return; - } - else - { - SLANG_UNEXPECTED("handled member flavor"); - } - - } - break; - - default: - SLANG_UNIMPLEMENTED_X("assignment"); - break; - } -} - -struct DeclLoweringVisitor : DeclVisitor -{ - IRGenContext* context; - - IRBuilder* getBuilder() - { - return context->irBuilder; - } - - LoweredValInfo visitDeclBase(DeclBase* /*decl*/) - { - SLANG_UNIMPLEMENTED_X("decl catch-all"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - - LoweredValInfo visitDecl(Decl* /*decl*/) - { - SLANG_UNIMPLEMENTED_X("decl catch-all"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - - LoweredValInfo visitExtensionDecl(ExtensionDecl* decl) - { - for (auto & member : decl->Members) - ensureDecl(context, member); - return LoweredValInfo(); - } - - LoweredValInfo visitImportDecl(ImportDecl* /*decl*/) - { - return LoweredValInfo(); - } - - LoweredValInfo visitEmptyDecl(EmptyDecl* /*decl*/) - { - return LoweredValInfo(); - } - - LoweredValInfo visitSyntaxDecl(SyntaxDecl* /*decl*/) - { - return LoweredValInfo(); - } - - LoweredValInfo visitAttributeDecl(AttributeDecl* /*decl*/) - { - return LoweredValInfo(); - } - - LoweredValInfo visitTypeDefDecl(TypeDefDecl* decl) - { - // A type alias declaration may be generic, if it is - // nested under a generic type/function/etc. - // - NestedContext nested(this); - auto subBuilder = nested.getBuilder(); - auto subContext = nested.getContet(); - IRGeneric* outerGeneric = emitOuterGenerics(subContext, decl, decl); - - // TODO: if a type alias declaration can have linkage, - // we will need to lower it to some kind of global - // value in the IR so that we can attach a name to it. - // - // For now, we can only attach a name *if* the type - // alias is somehow generic. - if(outerGeneric) - { - addLinkageDecoration(context, outerGeneric, decl); - } - - auto type = lowerType(subContext, decl->type.type); - - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, type)); - } - - LoweredValInfo visitGenericTypeParamDecl(GenericTypeParamDecl* /*decl*/) - { - return LoweredValInfo(); - } - - LoweredValInfo visitGenericTypeConstraintDecl(GenericTypeConstraintDecl* decl) - { - // This might be a type constraint on an associated type, - // in which case it should lower as the key for that - // interface requirement. - if(auto assocTypeDecl = as(decl->ParentDecl)) - { - // TODO: might need extra steps if we ever allow - // generic associated types. - - - if(auto interfaceDecl = as(assocTypeDecl->ParentDecl)) - { - // Okay, this seems to be an interface rquirement, and - // we should lower it as such. - return LoweredValInfo::simple(getInterfaceRequirementKey(decl)); - } - } - - if(auto globalGenericParamDecl = as(decl->ParentDecl)) - { - // This is a constraint on a global generic type parameters, - // and so it should lower as a parameter of its own. - - auto inst = getBuilder()->emitGlobalGenericParam(); - addLinkageDecoration(context, inst, decl); - return LoweredValInfo::simple(inst); - } - - // Otherwise we really don't expect to see a type constraint - // declaration like this during lowering, because a generic - // should have set up a parameter for any constraints as - // part of being lowered. - - SLANG_UNEXPECTED("generic type constraint during lowering"); - UNREACHABLE_RETURN(LoweredValInfo()); - } - - LoweredValInfo visitGlobalGenericParamDecl(GlobalGenericParamDecl* decl) - { - auto inst = getBuilder()->emitGlobalGenericParam(); - addLinkageDecoration(context, inst, decl); - return LoweredValInfo::simple(inst); - } - - void lowerWitnessTable( - IRGenContext* subContext, - WitnessTable* astWitnessTable, - IRWitnessTable* irWitnessTable, - Dictionary mapASTToIRWitnessTable) - { - auto subBuilder = subContext->irBuilder; - - for(auto entry : astWitnessTable->requirementDictionary) - { - auto requiredMemberDecl = entry.Key; - auto satisfyingWitness = entry.Value; - - auto irRequirementKey = getInterfaceRequirementKey(requiredMemberDecl); - IRInst* irSatisfyingVal = nullptr; - - switch(satisfyingWitness.getFlavor()) - { - case RequirementWitness::Flavor::declRef: - { - auto satisfyingDeclRef = satisfyingWitness.getDeclRef(); - irSatisfyingVal = getSimpleVal(subContext, - emitDeclRef(subContext, satisfyingDeclRef, - // TODO: we need to know what type to plug in here... - nullptr)); - } - break; - - case RequirementWitness::Flavor::val: - { - auto satisfyingVal = satisfyingWitness.getVal(); - irSatisfyingVal = lowerSimpleVal(subContext, satisfyingVal); - } - break; - - case RequirementWitness::Flavor::witnessTable: - { - auto astReqWitnessTable = satisfyingWitness.getWitnessTable(); - IRWitnessTable* irSatisfyingWitnessTable = nullptr; - if(!mapASTToIRWitnessTable.TryGetValue(astReqWitnessTable, irSatisfyingWitnessTable)) - { - // Need to construct a sub-witness-table - irSatisfyingWitnessTable = subBuilder->createWitnessTable(); - - // Recursively lower the sub-table. - lowerWitnessTable( - subContext, - astReqWitnessTable, - irSatisfyingWitnessTable, - mapASTToIRWitnessTable); - - irSatisfyingWitnessTable->moveToEnd(); - } - irSatisfyingVal = irSatisfyingWitnessTable; - } - break; - - default: - SLANG_UNEXPECTED("handled requirement witness case"); - break; - } - - - subBuilder->createWitnessTableEntry( - irWitnessTable, - irRequirementKey, - irSatisfyingVal); - } - } - - LoweredValInfo visitInheritanceDecl(InheritanceDecl* inheritanceDecl) - { - // An inheritance clause inside of an `interface` - // declaration should not give rise to a witness - // table, because it represents something the - // interface requires, and not what it provides. - // - auto parentDecl = inheritanceDecl->ParentDecl; - if (auto parentInterfaceDecl = as(parentDecl)) - { - return LoweredValInfo::simple(getInterfaceRequirementKey(inheritanceDecl)); - } - // - // We also need to cover the case where an `extension` - // declaration is being used to add a conformance to - // an existing `interface`: - // - if(auto parentExtensionDecl = as(parentDecl)) - { - auto targetType = parentExtensionDecl->targetType; - if(auto targetDeclRefType = as(targetType)) - { - if(auto targetInterfaceDeclRef = targetDeclRefType->declRef.as()) - { - return LoweredValInfo::simple(getInterfaceRequirementKey(inheritanceDecl)); - } - } - } - - // Find the type that is doing the inheriting. - // Under normal circumstances it is the type declaration that - // is the parent for the inheritance declaration, but if - // the inheritance declaration is on an `extension` declaration, - // then we need to identify the type being extended. - // - RefPtr subType; - if (auto extParentDecl = as(parentDecl)) - { - subType = extParentDecl->targetType.type; - } - else - { - subType = DeclRefType::Create( - context->getSession(), - makeDeclRef(parentDecl)); - } - - // What is the super-type that we have declared we inherit from? - RefPtr superType = inheritanceDecl->base.type; - - // Construct the mangled name for the witness table, which depends - // on the type that is conforming, and the type that it conforms to. - // - // TODO: This approach doesn't really make sense for generic `extension` conformances. - auto mangledName = getMangledNameForConformanceWitness(subType, superType); - - // A witness table may need to be generic, if the outer - // declaration (either a type declaration or an `extension`) - // is generic. - // - NestedContext nested(this); - auto subBuilder = nested.getBuilder(); - auto subContext = nested.getContet(); - emitOuterGenerics(subContext, inheritanceDecl, inheritanceDecl); - - // Lower the super-type to force its declaration to be lowered. - // - // Note: we are using the "sub-context" here because the - // type being inherited from could reference generic parameters, - // and we need those parameters to lower as references to - // the parameters of our IR-level generic. - // - lowerType(subContext, superType); - - // Create the IR-level witness table - auto irWitnessTable = subBuilder->createWitnessTable(); - addLinkageDecoration(context, irWitnessTable, inheritanceDecl, mangledName.getUnownedSlice()); - - // Register the value now, rather than later, to avoid any possible infinite recursion. - setGlobalValue(context, inheritanceDecl, LoweredValInfo::simple(irWitnessTable)); - - // Make sure that all the entries in the witness table have been filled in, - // including any cases where there are sub-witness-tables for conformances - Dictionary mapASTToIRWitnessTable; - lowerWitnessTable( - subContext, - inheritanceDecl->witnessTable, - irWitnessTable, - mapASTToIRWitnessTable); - - irWitnessTable->moveToEnd(); - - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, irWitnessTable)); - } - - LoweredValInfo visitDeclGroup(DeclGroup* declGroup) - { - // To lower a group of declarations, we just - // lower each one individually. - // - for (auto decl : declGroup->decls) - { - IRBuilderSourceLocRAII sourceLocInfo(context->irBuilder, decl->loc); - - // 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 - // accessors, and these are what should actually - // get lowered (they are effectively functions). - - for (auto accessor : decl->getMembersOfType()) - { - if (accessor->HasModifier()) - continue; - - ensureDecl(context, accessor); - } - - // The subscript declaration itself won't correspond - // to anything in the lowered program, so we don't - // bother creating a representation here. - // - // Note: We may want to have a specific lowered value - // that can represent the combination of callables - // that make up the subscript operation. - return LoweredValInfo(); - } - - bool isGlobalVarDecl(VarDecl* decl) - { - auto parent = decl->ParentDecl; - if (as(parent)) - { - // Variable declared at global scope? -> Global. - return true; - } - else if(as(parent)) - { - if(decl->HasModifier()) - { - // A `static` member variable is effectively global. - return true; - } - } - - return false; - } - - bool isMemberVarDecl(VarDecl* decl) - { - auto parent = decl->ParentDecl; - if (as(parent)) - { - // A variable declared inside of an aggregate type declaration is a member. - return true; - } - - return false; - } - - LoweredValInfo lowerGlobalShaderParam(VarDecl* decl) - { - IRType* paramType = lowerType(context, decl->getType()); - - auto builder = getBuilder(); - - auto irParam = builder->createGlobalParam(paramType); - auto paramVal = LoweredValInfo::simple(irParam); - - addLinkageDecoration(context, irParam, decl); - addNameHint(context, irParam, decl); - maybeSetRate(context, irParam, decl); - addVarDecorations(context, irParam, decl); - - if (decl) - { - builder->addHighLevelDeclDecoration(irParam, decl); - } - - // A global variable's SSA value is a *pointer* to - // the underlying storage. - setGlobalValue(context, decl, paramVal); - - irParam->moveToEnd(); - - return paramVal; - } - - LoweredValInfo lowerGlobalVarDecl(VarDecl* decl) - { - if(isGlobalShaderParameter(decl)) - { - return lowerGlobalShaderParam(decl); - } - - IRType* varType = lowerType(context, decl->getType()); - - auto builder = getBuilder(); - - IRGlobalValueWithCode* irGlobal = nullptr; - LoweredValInfo globalVal; - - // a `static const` global is actually a compile-time constant - if (decl->HasModifier() && decl->HasModifier()) - { - irGlobal = builder->createGlobalConstant(varType); - globalVal = LoweredValInfo::simple(irGlobal); - } - else - { - irGlobal = builder->createGlobalVar(varType); - globalVal = LoweredValInfo::ptr(irGlobal); - } - addLinkageDecoration(context, irGlobal, decl); - addNameHint(context, irGlobal, decl); - - maybeSetRate(context, irGlobal, decl); - - addVarDecorations(context, irGlobal, decl); - - if (decl) - { - builder->addHighLevelDeclDecoration(irGlobal, decl); - } - - // A global variable's SSA value is a *pointer* to - // the underlying storage. - setGlobalValue(context, decl, globalVal); - - if (isImportedDecl(decl)) - { - // Always emit imported declarations as declarations, - // and not definitions. - } - else if( auto initExpr = decl->initExpr ) - { - IRBuilder subBuilderStorage = *getBuilder(); - IRBuilder* subBuilder = &subBuilderStorage; - - subBuilder->setInsertInto(irGlobal); - - IRGenContext subContextStorage = *context; - IRGenContext* subContext = &subContextStorage; - - subContext->irBuilder = subBuilder; - - // TODO: set up a parent IR decl to put the instructions into - - IRBlock* entryBlock = subBuilder->emitBlock(); - subBuilder->setInsertInto(entryBlock); - - LoweredValInfo initVal = lowerLValueExpr(subContext, initExpr); - subContext->irBuilder->emitReturn(getSimpleVal(subContext, initVal)); - } - - irGlobal->moveToEnd(); - - return globalVal; - } - - bool isFunctionStaticVarDecl(VarDeclBase* decl) - { - // Only a variable marked `static` can be static. - if(!decl->FindModifier()) - return false; - - // The immediate parent of a function-scope variable - // declaration will be a `ScopeDecl`. - // - // TODO: right now the parent links for scopes are *not* - // set correctly, so we can't just scan up and look - // for a function in the parent chain... - auto parent = decl->ParentDecl; - if( as(parent) ) - { - return true; - } - - return false; - } - - IRInst* defaultSpecializeOuterGeneric( - IRInst* outerVal, - IRType* type, - GenericDecl* genericDecl) - { - auto builder = getBuilder(); - - // We need to specialize any generics that are further out... - auto specialiedOuterVal = defaultSpecializeOuterGenerics( - outerVal, - builder->getGenericKind(), - genericDecl); - - List genericArgs; - - // Walk the parameters of the generic, and emit an argument for each, - // which will be a reference to binding for that parameter in the - // current scope. - // - // First we start with type and value parameters, - // in the order they were declared. - for (auto member : genericDecl->Members) - { - if (auto typeParamDecl = as(member)) - { - genericArgs.add(getSimpleVal(context, ensureDecl(context, typeParamDecl))); - } - else if (auto valDecl = as(member)) - { - genericArgs.add(getSimpleVal(context, ensureDecl(context, valDecl))); - } - } - // Then we emit constraint parameters, again in - // declaration order. - for (auto member : genericDecl->Members) - { - if (auto constraintDecl = as(member)) - { - genericArgs.add(getSimpleVal(context, ensureDecl(context, constraintDecl))); - } - } - - return builder->emitSpecializeInst(type, specialiedOuterVal, genericArgs.getCount(), genericArgs.getBuffer()); - } - - IRInst* defaultSpecializeOuterGenerics( - IRInst* val, - IRType* type, - Decl* decl) - { - if(!val) return nullptr; - - auto parentVal = val->getParent(); - while(parentVal) - { - if(as(parentVal)) - break; - parentVal = parentVal->getParent(); - } - if(!parentVal) - return val; - - for(auto pp = decl->ParentDecl; pp; pp = pp->ParentDecl) - { - if(auto genericAncestor = as(pp)) - { - return defaultSpecializeOuterGeneric(parentVal, type, genericAncestor); - } - } - - return val; - } - - struct NestedContext - { - IRGenEnv subEnvStorage; - IRBuilder subBuilderStorage; - IRGenContext subContextStorage; - - NestedContext(DeclLoweringVisitor* outer) - : subBuilderStorage(*outer->getBuilder()) - , subContextStorage(*outer->context) - { - auto outerContext = outer->context; - - subEnvStorage.outer = outerContext->env; - - subContextStorage.irBuilder = &subBuilderStorage; - subContextStorage.env = &subEnvStorage; - } - - IRBuilder* getBuilder() { return &subBuilderStorage; } - IRGenContext* getContet() { return &subContextStorage; } - }; - - LoweredValInfo lowerFunctionStaticConstVarDecl( - VarDeclBase* decl) - { - // We need to insert the constant at a level above - // the function being emitted. This will usually - // be the global scope, but it might be an outer - // generic if we are lowering a generic function. - // - NestedContext nestedContext(this); - auto subBuilder = nestedContext.getBuilder(); - auto subContext = nestedContext.getContet(); - - subBuilder->setInsertInto(subBuilder->getFunc()->getParent()); - - IRType* subVarType = lowerType(subContext, decl->getType()); - - IRGlobalConstant* irConstant = subBuilder->createGlobalConstant(subVarType); - addVarDecorations(subContext, irConstant, decl); - addNameHint(context, irConstant, decl); - maybeSetRate(context, irConstant, decl); - subBuilder->addHighLevelDeclDecoration(irConstant, decl); - - LoweredValInfo constantVal = LoweredValInfo::ptr(irConstant); - setValue(context, decl, constantVal); - - if( auto initExpr = decl->initExpr ) - { - NestedContext nestedInitContext(this); - auto initBuilder = nestedInitContext.getBuilder(); - auto initContext = nestedInitContext.getContet(); - - initBuilder->setInsertInto(irConstant); - - IRBlock* entryBlock = initBuilder->emitBlock(); - initBuilder->setInsertInto(entryBlock); - - LoweredValInfo initVal = lowerRValueExpr(initContext, initExpr); - initBuilder->emitReturn(getSimpleVal(initContext, initVal)); - } - - return constantVal; - } - - LoweredValInfo lowerFunctionStaticVarDecl( - VarDeclBase* decl) - { - // We know the variable is `static`, but it might also be `const. - if(decl->HasModifier()) - return lowerFunctionStaticConstVarDecl(decl); - - // A global variable may need to be generic, if one - // of the outer declarations is generic. - NestedContext nestedContext(this); - auto subBuilder = nestedContext.getBuilder(); - auto subContext = nestedContext.getContet(); - subBuilder->setInsertInto(subBuilder->getModule()->getModuleInst()); - emitOuterGenerics(subContext, decl, decl); - - IRType* subVarType = lowerType(subContext, decl->getType()); - - IRGlobalValueWithCode* irGlobal = subBuilder->createGlobalVar(subVarType); - addVarDecorations(subContext, irGlobal, decl); - - addNameHint(context, irGlobal, decl); - maybeSetRate(context, irGlobal, decl); - - subBuilder->addHighLevelDeclDecoration(irGlobal, decl); - - // We are inside of a function, and that function might be generic, - // in which case the `static` variable will be lowered to another - // generic. Let's start with a terrible example: - // - // interface IHasCount { int getCount(); } - // int incrementCounter(T val) { - // static int counter = 0; - // counter += val.getCount(); - // return counter; - // } - // - // In this case, `incrementCounter` will lower to a function - // nested in a generic, while `counter` will be lowered to - // a global variable nested in a *different* generic. - // The net result is something like this: - // - // int counter = 0; - // - // int incrementCounter(T val) { - // counter += val.getCount(); - // return counter; - // - // The references to `counter` inside of `incrementCounter` - // become references to `counter`. - // - // At the IR level, this means that the value we install - // for `decl` needs to be a specialized reference to `irGlobal`, - // for any outer generics. - // - IRType* varType = lowerType(context, decl->getType()); - IRType* varPtrType = getBuilder()->getPtrType(varType); - auto irSpecializedGlobal = defaultSpecializeOuterGenerics(irGlobal, varPtrType, decl); - LoweredValInfo globalVal = LoweredValInfo::ptr(irSpecializedGlobal); - setValue(context, decl, globalVal); - - // A `static` variable with an initializer needs special handling, - // at least if the initializer isn't a compile-time constant. - if( auto initExpr = decl->initExpr ) - { - // We must create an ordinary global `bool isInitialized = false` - // to represent whether we've initialized this before. - // Then emit code like: - // - // if(!isInitialized) { = ; isInitialized = true; } - // - // TODO: we could conceivably optimize this by detecting - // when the `initExpr` lowers to just a reference to a constant, - // and then either deleting the extra code structure there, - // or not generating it in the first place. That is a bit - // more complexity than I'm ready for at the moment. - // - - // Of course, if we are under a generic, then the Boolean - // variable need to be generic as well! - NestedContext nestedBoolContext(this); - auto boolBuilder = nestedBoolContext.getBuilder(); - auto boolContext = nestedBoolContext.getContet(); - boolBuilder->setInsertInto(boolBuilder->getModule()->getModuleInst()); - emitOuterGenerics(boolContext, decl, decl); - - auto irBoolType = boolBuilder->getBoolType(); - auto irBool = boolBuilder->createGlobalVar(irBoolType); - boolBuilder->setInsertInto(irBool); - boolBuilder->setInsertInto(boolBuilder->createBlock()); - boolBuilder->emitReturn(boolBuilder->getBoolValue(false)); - - auto boolVal = LoweredValInfo::ptr(defaultSpecializeOuterGenerics(irBool, irBoolType, decl)); - - - // Okay, with our global Boolean created, we can move on to - // generating the code we actually care about, back in the original function. - - auto builder = getBuilder(); - - auto initBlock = builder->createBlock(); - auto afterBlock = builder->createBlock(); - - builder->emitIfElse(getSimpleVal(context, boolVal), afterBlock, initBlock, afterBlock); - - builder->insertBlock(initBlock); - LoweredValInfo initVal = lowerLValueExpr(context, initExpr); - assign(context, globalVal, initVal); - assign(context, boolVal, LoweredValInfo::simple(builder->getBoolValue(true))); - builder->emitBranch(afterBlock); - - builder->insertBlock(afterBlock); - } - - irGlobal->moveToEnd(); - finishOuterGenerics(subBuilder, irGlobal); - return globalVal; - } - - LoweredValInfo visitGenericValueParamDecl(GenericValueParamDecl* decl) - { - return emitDeclRef(context, makeDeclRef(decl), - lowerType(context, decl->type)); - } - - LoweredValInfo visitVarDecl(VarDecl* decl) - { - // Detect global (or effectively global) variables - // and handle them differently. - if (isGlobalVarDecl(decl)) - { - return lowerGlobalVarDecl(decl); - } - - if(isFunctionStaticVarDecl(decl)) - { - return lowerFunctionStaticVarDecl(decl); - } - - if(isMemberVarDecl(decl)) - { - return lowerMemberVarDecl(decl); - } - - // A user-defined variable declaration will usually turn into - // an `alloca` operation for the variable's storage, - // plus some code to initialize it and then store to the variable. - - IRType* varType = lowerType(context, decl->getType()); - - // As a special case, an immutable local variable with an - // initializer can just lower to the SSA value of its initializer. - // - if(as(decl)) - { - if(auto initExpr = decl->initExpr) - { - auto initVal = lowerRValueExpr(context, initExpr); - initVal = materialize(context, initVal); - setGlobalValue(context, decl, initVal); - return initVal; - } - } - - - LoweredValInfo varVal = createVar(context, varType, decl); - - if( auto initExpr = decl->initExpr ) - { - auto initVal = lowerRValueExpr(context, initExpr); - - assign(context, varVal, initVal); - } - - setGlobalValue(context, decl, varVal); - - return varVal; - } - - IRStructKey* getInterfaceRequirementKey(Decl* requirementDecl) - { - return Slang::getInterfaceRequirementKey(context, requirementDecl); - } - - LoweredValInfo visitInterfaceDecl(InterfaceDecl* decl) - { - // The members of an interface will turn into the keys that will - // be used for lookup operations into witness - // tables that promise conformance to the interface. - // - // TODO: we don't handle the case here of an interface - // with concrete/default implementations for any - // of its members. - // - // TODO: If we want to support using an interface as - // an existential type, then we might need to emit - // a witness table for the interface type's conformance - // to its own interface. - // - for (auto requirementDecl : decl->Members) - { - getInterfaceRequirementKey(requirementDecl); - - // As a special case, any type constraints placed - // on an associated type will *also* need to be turned - // into requirement keys for this interface. - if (auto associatedTypeDecl = as(requirementDecl)) - { - for (auto constraintDecl : associatedTypeDecl->getMembersOfType()) - { - getInterfaceRequirementKey(constraintDecl); - } - } - } - - - NestedContext nestedContext(this); - auto subBuilder = nestedContext.getBuilder(); - auto subContext = nestedContext.getContet(); - - // Emit any generics that should wrap the actual type. - emitOuterGenerics(subContext, decl, decl); - - IRInterfaceType* irInterface = subBuilder->createInterfaceType(); - addNameHint(context, irInterface, decl); - addLinkageDecoration(context, irInterface, decl); - subBuilder->setInsertInto(irInterface); - - // TODO: are there any interface members that should be - // nested inside the interface type itself? - - irInterface->moveToEnd(); - - addTargetIntrinsicDecorations(irInterface, decl); - - - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, irInterface)); - } - - LoweredValInfo visitEnumCaseDecl(EnumCaseDecl* decl) - { - // A case within an `enum` decl will lower to a value - // of the `enum`'s "tag" type. - // - // TODO: a bit more work will be needed if we allow for - // enum cases that have payloads, because then we need - // a function that constructs the value given arguments. - // - NestedContext nestedContext(this); - auto subContext = nestedContext.getContet(); - - // Emit any generics that should wrap the actual type. - emitOuterGenerics(subContext, decl, decl); - - return lowerRValueExpr(subContext, decl->tagExpr); - } - - LoweredValInfo visitEnumDecl(EnumDecl* decl) - { - // Given a declaration of a type, we need to make sure - // to output "witness tables" for any interfaces this - // type has declared conformance to. - for( auto inheritanceDecl : decl->getMembersOfType() ) - { - ensureDecl(context, inheritanceDecl); - } - - NestedContext nestedContext(this); - auto subBuilder = nestedContext.getBuilder(); - auto subContext = nestedContext.getContet(); - emitOuterGenerics(subContext, decl, decl); - - // An `enum` declaration will currently lower directly to its "tag" - // type, so that any references to the `enum` become referenes to - // the tag type instead. - // - // TODO: if we ever support `enum` types with payloads, we would - // need to make the `enum` lower to some kind of custom "tagged union" - // type. - - IRType* loweredTagType = lowerType(subContext, decl->tagType); - - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, loweredTagType)); - } - - LoweredValInfo visitAggTypeDecl(AggTypeDecl* decl) - { - // Don't generate an IR `struct` for intrinsic types - if(decl->FindModifier() || decl->FindModifier()) - { - return LoweredValInfo(); - } - - // Given a declaration of a type, we need to make sure - // to output "witness tables" for any interfaces this - // type has declared conformance to. - for( auto inheritanceDecl : decl->getMembersOfType() ) - { - ensureDecl(context, inheritanceDecl); - } - - // We are going to create nested IR building state - // to use when emitting the members of the type. - // - NestedContext nestedContext(this); - auto subBuilder = nestedContext.getBuilder(); - auto subContext = nestedContext.getContet(); - - // Emit any generics that should wrap the actual type. - emitOuterGenerics(subContext, decl, decl); - - IRStructType* irStruct = subBuilder->createStructType(); - addNameHint(context, irStruct, decl); - addLinkageDecoration(context, irStruct, decl); - - subBuilder->setInsertInto(irStruct); - - for (auto fieldDecl : decl->getMembersOfType()) - { - if (fieldDecl->HasModifier()) - { - // A `static` field is actually a global variable, - // and we should emit it as such. - ensureDecl(context, fieldDecl); - continue; - } - - // Each ordinary field will need to turn into a struct "key" - // that is used for fetching the field. - IRInst* fieldKeyInst = getSimpleVal(context, - ensureDecl(context, fieldDecl)); - auto fieldKey = as(fieldKeyInst); - SLANG_ASSERT(fieldKey); - - // Note: we lower the type of the field in the "sub" - // context, so that any generic parameters that were - // set up for the type can be referenced by the field type. - IRType* fieldType = lowerType( - subContext, - fieldDecl->getType()); - - // Then, the parent `struct` instruction itself will have - // a "field" instruction. - subBuilder->createStructField( - irStruct, - fieldKey, - fieldType); - } - - // There may be members not handled by the above logic (e.g., - // member functions), but we will not immediately force them - // to be emitted here, so as not to risk a circular dependency. - // - // Instead we will force emission of all children of aggregate - // type declarations later, from the top-level emit logic. - - irStruct->moveToEnd(); - addTargetIntrinsicDecorations(irStruct, decl); - - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, irStruct)); - } - - LoweredValInfo lowerMemberVarDecl(VarDecl* fieldDecl) - { - // Each field declaration in the AST translates into - // a "key" that can be used to extract field values - // from instances of struct types that contain the field. - // - // It is correct to say struct *types* because a `struct` - // nested under a generic can be used to realize a number - // of different concrete types, but all of these types - // will use the same space of keys. - - auto builder = getBuilder(); - auto irFieldKey = builder->createStructKey(); - addNameHint(context, irFieldKey, fieldDecl); - - addVarDecorations(context, irFieldKey, fieldDecl); - - addLinkageDecoration(context, irFieldKey, fieldDecl); - - if (auto semanticModifier = fieldDecl->FindModifier()) - { - builder->addSemanticDecoration(irFieldKey, semanticModifier->name.getName()->text.getUnownedSlice()); - } - - // We allow a field to be marked as a target intrinsic, - // so that we can override its mangled name in the - // output for the chosen target. - addTargetIntrinsicDecorations(irFieldKey, fieldDecl); - - - return LoweredValInfo::simple(irFieldKey); - } - - - DeclRef createDefaultSpecializedDeclRefImpl(Decl* decl) - { - DeclRef declRef; - declRef.decl = decl; - declRef.substitutions = createDefaultSubstitutions(context->getSession(), decl); - return declRef; - } - // - // The client should actually call the templated wrapper, to preserve type information. - template - DeclRef createDefaultSpecializedDeclRef(D* decl) - { - DeclRef declRef = createDefaultSpecializedDeclRefImpl(decl); - return declRef.as(); - } - - - // 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 { static int bar(T y) { ... } }; - // - // becomes: - // - // func Foo_bar(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, ///< Copy in - kParameterDirection_Out, ///< Copy out - kParameterDirection_InOut, ///< Copy in, copy out - kParameterDirection_Ref, ///< By-reference - }; - struct ParameterInfo - { - // This AST-level type of the parameter - RefPtr type; - - // The direction (`in` vs `out` vs `in out`) - ParameterDirection direction; - - // The variable/parameter declaration for - // this parameter (if any) - VarDeclBase* decl; - - // Is this the representation of a `this` parameter? - bool isThisParam = false; - }; - // - // We need a way to compute the appropriate `ParameterDirection` for a - // declared parameter: - // - ParameterDirection getParameterDirection(VarDeclBase* paramDecl) - { - if( paramDecl->HasModifier() ) - { - // The AST specified `ref`: - return kParameterDirection_Ref; - } - if( paramDecl->HasModifier() ) - { - // The AST specified `inout`: - return kParameterDirection_InOut; - } - if (paramDecl->HasModifier()) - { - // We saw an `out` modifier, so now we need - // to check if there was a paired `in`. - if(paramDecl->HasModifier()) - 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); - info.isThisParam = false; - return info; - } - // - - // Here's the declaration for the type to hold the lists: - struct ParameterLists - { - List params; - }; - // - // 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`: - ParameterListCollectMode getModeForCollectingParentParameters( - Decl* decl, - ContainerDecl* parentDecl) - { - // If we have a `static` parameter, then it is obvious - // that we should use the `static` mode - if(isEffectivelyStatic(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( - ParameterDirection direction, - Type* type, - ParameterLists* ioParameterLists) - { - ParameterInfo info; - info.type = type; - info.decl = nullptr; - info.direction = direction; - info.isThisParam = true; - - ioParameterLists->params.add(info); - } - void addThisParameter( - ParameterDirection direction, - 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); - RefPtr type = DeclRefType::Create(context->getSession(), declRef); - addThisParameter( - direction, - 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 ) - { - // For now we make any `this` parameter default to `in`. - // - ParameterDirection direction = kParameterDirection_In; - // - // Applications can opt in to a mutable `this` parameter, - // by applying the `[mutating]` attribute to their - // declaration. - // - if( decl->HasModifier() ) - { - direction = kParameterDirection_InOut; - } - - if( auto aggTypeDecl = as(parentDecl) ) - { - addThisParameter(direction, aggTypeDecl, ioParameterLists); - } - else if( auto extensionDecl = as(parentDecl) ) - { - addThisParameter(direction, 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 = as(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)); - } - } - } - } - - bool isImportedDecl(Decl* decl) - { - return Slang::isImportedDecl(context, decl); - } - - bool isConstExprVar(Decl* decl) - { - if( decl->HasModifier() ) - { - return true; - } - else if(decl->HasModifier() && decl->HasModifier()) - { - return true; - } - - return false; - } - - IRType* maybeGetConstExprType(IRType* type, Decl* decl) - { - if(isConstExprVar(decl)) - { - return getBuilder()->getRateQualifiedType( - getBuilder()->getConstExprRate(), - type); - } - - return type; - } - - IRGeneric* emitOuterGeneric( - IRGenContext* subContext, - GenericDecl* genericDecl, - Decl* leafDecl) - { - auto subBuilder = subContext->irBuilder; - - // Of course, a generic might itself be nested inside of other generics... - emitOuterGenerics(subContext, genericDecl, leafDecl); - - // We need to create an IR generic - - auto irGeneric = subBuilder->emitGeneric(); - subBuilder->setInsertInto(irGeneric); - - auto irBlock = subBuilder->emitBlock(); - subBuilder->setInsertInto(irBlock); - - // Now emit any parameters of the generic - // - // First we start with type and value parameters, - // in the order they were declared. - for (auto member : genericDecl->Members) - { - if (auto typeParamDecl = as(member)) - { - // TODO: use a `TypeKind` to represent the - // classifier of the parameter. - auto param = subBuilder->emitParam(nullptr); - addNameHint(context, param, typeParamDecl); - setValue(subContext, typeParamDecl, LoweredValInfo::simple(param)); - } - else if (auto valDecl = as(member)) - { - auto paramType = lowerType(subContext, valDecl->getType()); - auto param = subBuilder->emitParam(paramType); - addNameHint(context, param, valDecl); - setValue(subContext, valDecl, LoweredValInfo::simple(param)); - } - } - // Then we emit constraint parameters, again in - // declaration order. - for (auto member : genericDecl->Members) - { - if (auto constraintDecl = as(member)) - { - // TODO: use a `WitnessTableKind` to represent the - // classifier of the parameter. - auto param = subBuilder->emitParam(nullptr); - addNameHint(context, param, constraintDecl); - setValue(subContext, constraintDecl, LoweredValInfo::simple(param)); - } - } - - return irGeneric; - } - - // If the given `decl` is enclosed in any generic declarations, then - // emit IR-level generics to represent them. - // The `leafDecl` represents the inner-most declaration we are actually - // trying to emit, which is the one that should receive the mangled name. - // - IRGeneric* emitOuterGenerics(IRGenContext* subContext, Decl* decl, Decl* leafDecl) - { - for(auto pp = decl->ParentDecl; pp; pp = pp->ParentDecl) - { - if(auto genericAncestor = as(pp)) - { - return emitOuterGeneric(subContext, genericAncestor, leafDecl); - } - } - - return nullptr; - } - - // If any generic declarations have been created by `emitOuterGenerics`, - // then finish them off by emitting `return` instructions for the - // values that they should produce. - // - // Return the outer-most generic (if there is one), or the original - // value (if there were no generics), which should be the IR-level - // representation of the original declaration. - // - IRInst* finishOuterGenerics( - IRBuilder* subBuilder, - IRInst* val) - { - IRInst* v = val; - for(;;) - { - auto parentBlock = as(v->getParent()); - if (!parentBlock) break; - - auto parentGeneric = as(parentBlock->getParent()); - if (!parentGeneric) break; - - subBuilder->setInsertInto(parentBlock); - subBuilder->emitReturn(v); - parentGeneric->moveToEnd(); - - // There might be more outer generics, - // so we need to loop until we run out. - v = parentGeneric; - } - return v; - } - - // Attach target-intrinsic decorations to an instruction, - // based on modifiers on an AST declaration. - void addTargetIntrinsicDecorations( - IRInst* irInst, - Decl* decl) - { - auto builder = getBuilder(); - - for (auto targetMod : decl->GetModifiersOfType()) - { - String definition; - auto definitionToken = targetMod->definitionToken; - if (definitionToken.type == TokenType::StringLiteral) - { - definition = getStringLiteralTokenValue(definitionToken); - } - else - { - definition = definitionToken.Content; - } - - builder->addTargetIntrinsicDecoration(irInst, targetMod->targetToken.Content, definition.getUnownedSlice()); - } - } - - void addParamNameHint(IRInst* inst, ParameterInfo info) - { - if(auto decl = info.decl) - { - addNameHint(context, inst, decl); - } - else if( info.isThisParam ) - { - addNameHint(context, inst, "this"); - } - } - - LoweredValInfo lowerFuncDecl(FunctionDeclBase* decl) - { - // We are going to use a nested builder, because we will - // change the parent node that things get nested into. - // - NestedContext nestedContext(this); - auto subBuilder = nestedContext.getBuilder(); - auto subContext = nestedContext.getContet(); - - // The actual `IRFunction` that we emit needs to be nested - // inside of one `IRGeneric` for every outer `GenericDecl` - // in the declaration hierarchy. - - emitOuterGenerics(subContext, decl, decl); - - // Collect the parameter lists we will use for our new function. - ParameterLists parameterLists; - collectParameterLists(decl, ¶meterLists, 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). - - // 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 = as(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 = as(parentDecl)) - { - declForReturnType = subscriptDecl; - } - } - - // need to create an IR function here - - IRFunc* irFunc = subBuilder->createFunc(); - addNameHint(context, irFunc, decl); - addLinkageDecoration(context, irFunc, decl); - - List paramTypes; - - for( auto paramInfo : parameterLists.params ) - { - IRType* irParamType = lowerType(subContext, paramInfo.type); - - switch( paramInfo.direction ) - { - case kParameterDirection_In: - // Simple case of a by-value input parameter. - break; - - // If the parameter is declared `out` or `inout`, - // then we will represent it with a pointer type in - // the IR, but we will use a specialized pointer - // type that encodes the parameter direction information. - case kParameterDirection_Out: - irParamType = subBuilder->getOutType(irParamType); - break; - case kParameterDirection_InOut: - irParamType = subBuilder->getInOutType(irParamType); - break; - case kParameterDirection_Ref: - irParamType = subBuilder->getRefType(irParamType); - break; - - default: - SLANG_UNEXPECTED("unknown parameter direction"); - break; - } - - // If the parameter was explicitly marked as being a compile-time - // constant (`constexpr`), then attach that information to its - // IR-level type explicitly. - if( paramInfo.decl ) - { - irParamType = maybeGetConstExprType(irParamType, paramInfo.decl); - } - - paramTypes.add(irParamType); - } - - auto irResultType = lowerType(subContext, declForReturnType->ReturnType); - - if (auto setterDecl = as(decl)) - { - // We are lowering a "setter" accessor inside a subscript - // declaration, which means we don't want to *return* the - // stated return type of the subscript, but instead take - // it as a parameter. - // - IRType* irParamType = irResultType; - paramTypes.add(irParamType); - - // Instead, a setter always returns `void` - // - irResultType = subBuilder->getVoidType(); - } - - if( auto refAccessorDecl = as(decl) ) - { - // A `ref` accessor needs to return a *pointer* to the value - // being accessed, rather than a simple value. - irResultType = subBuilder->getPtrType(irResultType); - } - - auto irFuncType = subBuilder->getFuncType( - paramTypes.getCount(), - paramTypes.getBuffer(), - irResultType); - irFunc->setFullType(irFuncType); - - subBuilder->setInsertInto(irFunc); - - if (isImportedDecl(decl)) - { - // Always emit imported declarations as declarations, - // and not definitions. - } - else 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 - // this case should really only occur for builtin declarations. - } - else - { - // This is a function definition, so we need to actually - // construct IR for the body... - IRBlock* entryBlock = subBuilder->emitBlock(); - subBuilder->setInsertInto(entryBlock); - - UInt paramTypeIndex = 0; - for( auto paramInfo : parameterLists.params ) - { - 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? - - IRParam* irParamPtr = subBuilder->emitParam(irParamType); - if(auto paramDecl = paramInfo.decl) - { - addVarDecorations(context, irParamPtr, paramDecl); - subBuilder->addHighLevelDeclDecoration(irParamPtr, paramDecl); - } - addParamNameHint(irParamPtr, paramInfo); - - 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. - // - // We start by declaring an IR parameter of the same type. - // - auto paramDecl = paramInfo.decl; - IRParam* irParam = subBuilder->emitParam(irParamType); - if( paramDecl ) - { - addVarDecorations(context, irParam, paramDecl); - subBuilder->addHighLevelDeclDecoration(irParam, paramDecl); - } - addParamNameHint(irParam, paramInfo); - paramVal = LoweredValInfo::simple(irParam); - // - // HLSL allows a function parameter to be used as a local - // variable in the function body (just like C/C++), so - // we need to support that case as well. - // - // However, if we notice that the parameter was marked - // `const`, then we can skip this step. - // - // TODO: we should consider having all parameter be implicitly - // immutable except in a specific "compatibility mode." - // - if(paramDecl && paramDecl->FindModifier()) - { - // This parameter was declared to be immutable, - // so there should be no assignment to it in the - // function body, and we don't need a temporary. - } - else - { - // The parameter migth get used as a temporary in - // the function body. We will allocate a mutable - // local variable for is value, and then assign - // from the parameter to the local at the start - // of the function. - // - auto irLocal = subBuilder->emitVar(irParamType); - auto localVal = LoweredValInfo::ptr(irLocal); - assign(subContext, localVal, paramVal); - // - // When code later in the body of the function refers - // to the parameter declaration, it will actually refer - // to the value stored in the local variable. - // - paramVal = localVal; - } - } - break; - } - - if( auto paramDecl = paramInfo.decl ) - { - setValue(subContext, paramDecl, paramVal); - } - - if (paramInfo.isThisParam) - { - subContext->thisVal = paramVal; - } - } - - if (auto setterDecl = as(decl)) - { - // Add the IR parameter for the new value - IRType* irParamType = irResultType; - auto irParam = subBuilder->emitParam(irParamType); - addNameHint(context, irParam, "newValue"); - - // TODO: we need some way to wire this up to the `newValue` - // or whatever name we give for that parameter inside - // the setter body. - } - - { - - auto attr = decl->FindModifier(); - - // I needed to test for patchConstantFuncDecl here - // because it is only set if validateEntryPoint is called with Hull as the required stage - // If I just build domain shader, and then the attribute exists, but patchConstantFuncDecl is not set - // and thus leads to a crash. - if (attr && attr->patchConstantFuncDecl) - { - // We need to lower the function - FuncDecl* patchConstantFunc = attr->patchConstantFuncDecl; - assert(patchConstantFunc); - - // Convert the patch constant function into IRInst - IRInst* irPatchConstantFunc = getSimpleVal(context, ensureDecl(subContext, patchConstantFunc)); - - // Attach a decoration so that our IR function references - // the patch constant function. - // - subContext->irBuilder->addPatchConstantFuncDecoration( - irFunc, - irPatchConstantFunc); - - } - } - - // Lower body - - 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 (!subContext->irBuilder->getBlock()->getTerminator()) - { - if(as(irResultType)) - { - // `void`-returning function can get an implicit - // return on exit of the body statement. - subContext->irBuilder->emitReturn(); - } - else - { - // Value-returning function is expected to `return` - // on every control-flow path. We need to enforce - // this by putting an `unreachable` terminator here, - // and then emit a dataflow error if this block - // can't be eliminated. - subContext->irBuilder->emitMissingReturn(); - } - } - } - - 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. - - getBuilder()->addTargetDecoration(irFunc, targetMod->targetToken.Content); - } - - // If this declaration was marked as having a target-specific lowering - // for a particular target, then handle that here. - addTargetIntrinsicDecorations(irFunc, decl); - - // If this declaration requires certain GLSL extension (or a particular GLSL version) - // for it to be usable, then declare that here. - // - // TODO: We should wrap this an `SpecializedForTargetModifier` together into a single - // case for enumerating the "capabilities" that a declaration requires. - // - for(auto extensionMod : decl->GetModifiersOfType()) - { - getBuilder()->addRequireGLSLExtensionDecoration(irFunc, extensionMod->extensionNameToken.Content); - } - for(auto versionMod : decl->GetModifiersOfType()) - { - getBuilder()->addRequireGLSLVersionDecoration(irFunc, Int(getIntegerLiteralValue(versionMod->versionNumberToken))); - } - - if(decl->FindModifier()) - { - getBuilder()->addSimpleDecoration(irFunc); - } - - if (decl->FindModifier()) - { - getBuilder()->addSimpleDecoration(irFunc); - } - - // For convenience, ensure that any additional global - // values that were emitted while outputting the function - // body appear before the function itself in the list - // of global values. - irFunc->moveToEnd(); - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, irFunc)); - } - - LoweredValInfo visitGenericDecl(GenericDecl * genDecl) - { - // TODO: Should this just always visit/lower the inner decl? - - if (auto innerFuncDecl = as(genDecl->inner)) - return ensureDecl(context, innerFuncDecl); - else if (auto innerStructDecl = as(genDecl->inner)) - { - ensureDecl(context, innerStructDecl); - return LoweredValInfo(); - } - else if( auto extensionDecl = as(genDecl->inner) ) - { - return ensureDecl(context, extensionDecl); - } - SLANG_RELEASE_ASSERT(false); - UNREACHABLE_RETURN(LoweredValInfo()); - } - - 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 declarations 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 = as(primaryDecl); - SLANG_ASSERT(primaryFuncDecl); - LoweredValInfo result = lowerFuncDecl(primaryFuncDecl); - for (auto dd = primaryDecl->nextDecl; dd; dd = dd->nextDecl) - { - auto funcDecl = as(dd); - SLANG_ASSERT(funcDecl); - lowerFuncDecl(funcDecl); - } - return result; - } -}; - -LoweredValInfo lowerDecl( - IRGenContext* context, - DeclBase* decl) -{ - IRBuilderSourceLocRAII sourceLocInfo(context->irBuilder, decl->loc); - - DeclLoweringVisitor visitor; - visitor.context = context; - - try - { - return visitor.dispatch(decl); - } - // Don't emit any context message for an explicit `AbortCompilationException` - // because it should only happen when an error is already emitted. - catch(AbortCompilationException&) { throw; } - catch(...) - { - context->getSink()->noteInternalErrorLoc(decl->loc); - throw; - } -} - -// Ensure that a version of the given declaration has been emitted to the IR -LoweredValInfo ensureDecl( - IRGenContext* context, - Decl* decl) -{ - auto shared = context->shared; - - LoweredValInfo result; - - // Look for an existing value installed in this context - auto env = context->env; - while(env) - { - if(env->mapDeclToValue.TryGetValue(decl, result)) - return result; - - env = env->outer; - } - - IRBuilder subIRBuilder; - subIRBuilder.sharedBuilder = context->irBuilder->sharedBuilder; - subIRBuilder.setInsertInto(subIRBuilder.sharedBuilder->module->getModuleInst()); - - IRGenEnv subEnv; - subEnv.outer = context->env; - - IRGenContext subContext = *context; - subContext.irBuilder = &subIRBuilder; - subContext.env = &subEnv; - - result = lowerDecl(&subContext, decl); - - // By default assume that any value we are lowering represents - // something that should be installed globally. - setGlobalValue(shared, decl, result); - - return result; -} - -IRInst* lowerSubstitutionArg( - IRGenContext* context, - Val* val) -{ - if (auto type = dynamicCast(val)) - { - return lowerType(context, type); - } - else if (auto declaredSubtypeWitness = as(val)) - { - // We need to look up the IR-level representation of the witness (which will be a witness table). - auto irWitnessTable = getSimpleVal( - context, - emitDeclRef( - context, - declaredSubtypeWitness->declRef, - context->irBuilder->getWitnessTableType())); - return irWitnessTable; - } - else - { - SLANG_UNIMPLEMENTED_X("value cases"); - UNREACHABLE_RETURN(nullptr); - } -} - -// Can the IR lowered version of this declaration ever be an `IRGeneric`? -bool canDeclLowerToAGeneric(RefPtr decl) -{ - // A callable decl lowers to an `IRFunc`, and can be generic - if(as(decl)) return true; - - // An aggregate type decl lowers to an `IRStruct`, and can be generic - if(as(decl)) return true; - - // An inheritance decl lowers to an `IRWitnessTable`, and can be generic - if(as(decl)) return true; - - // A `typedef` declaration nested under a generic will turn into - // a generic that returns a type (a simple type-level function). - if(as(decl)) return true; - - return false; -} - -LoweredValInfo emitDeclRef( - IRGenContext* context, - RefPtr decl, - RefPtr subst, - IRType* type) -{ - // We need to proceed by considering the specializations that - // have been put in place. - - // Ignore any global generic type substitutions during lowering. - // Really, we don't even expect these to appear. - while(auto globalGenericSubst = as(subst)) - subst = globalGenericSubst->outer; - - // If the declaration would not get wrapped in a `IRGeneric`, - // even if it is nested inside of an AST `GenericDecl`, then - // we should also ignore any generic substitutions. - if(!canDeclLowerToAGeneric(decl)) - { - while(auto genericSubst = as(subst)) - subst = genericSubst->outer; - } - - // In the simplest case, there is no specialization going - // on, and the decl-ref turns into a reference to the - // lowered IR value for the declaration. - if(!subst) - { - LoweredValInfo loweredDecl = ensureDecl(context, decl); - return loweredDecl; - } - - // Otherwise, we look at the kind of substitution, and let it guide us. - if(auto genericSubst = subst.as()) - { - // A generic substitution means we will need to output - // a `specialize` instruction to specialize the generic. - // - // First we want to emit the value without generic specialization - // applied, to get a correct value for it. - // - // Note: we only "unwrap" a single layer from the - // substitutions here, because the underlying declaration - // might be nested in multiple generics, or it might - // come from an interface. - // - LoweredValInfo genericVal = emitDeclRef( - context, - decl, - genericSubst->outer, - context->irBuilder->getGenericKind()); - - // There's no reason to specialize something that maps to a NULL pointer. - if (genericVal.flavor == LoweredValInfo::Flavor::None) - return LoweredValInfo(); - - // We can only really specialize things that map to single values. - // It would be an error if we got a non-`None` value that - // wasn't somehow a single value. - auto irGenericVal = getSimpleVal(context, genericVal); - - // We have the IR value for the generic we'd like to specialize, - // and now we need to get the value for the arguments. - List irArgs; - for (auto argVal : genericSubst->args) - { - auto irArgVal = lowerSimpleVal(context, argVal); - SLANG_ASSERT(irArgVal); - irArgs.add(irArgVal); - } - - // Once we have both the generic and its arguments, - // we can emit a `specialize` instruction and use - // its value as the result. - auto irSpecializedVal = context->irBuilder->emitSpecializeInst( - type, - irGenericVal, - irArgs.getCount(), - irArgs.getBuffer()); - - return LoweredValInfo::simple(irSpecializedVal); - } - else if(auto thisTypeSubst = subst.as()) - { - if(decl.Ptr() == thisTypeSubst->interfaceDecl) - { - // This is a reference to the interface type itself, - // through the this-type substitution, so it is really - // a reference to the this-type. - return lowerType(context, thisTypeSubst->witness->sub); - } - - // Somebody is trying to look up an interface requirement - // "through" some concrete type. We need to lower this decl-ref - // as a lookup of the corresponding member in a witness table. - // - // The witness table itself is referenced by the this-type - // substitution, so we can just lower that. - // - // Note: unlike the case for generics above, in the interface-lookup - // case, we don't end up caring about any further outer substitutions. - // That is because even if we are naming `ISomething.doIt()`, - // a method inside a generic interface, we don't actually care - // about the substitution of `Foo` for the parameter `T` of - // `ISomething`. That is because we really care about the - // witness table for the concrete type that conforms to `ISomething`. - // - auto irWitnessTable = lowerSimpleVal(context, thisTypeSubst->witness); - // - // The key to use for looking up the interface member is - // derived from the declaration. - // - auto irRequirementKey = getInterfaceRequirementKey(context, decl); - // - // Those two pieces of information tell us what we need to - // do in order to look up the value that satisfied the requirement. - // - auto irSatisfyingVal = context->irBuilder->emitLookupInterfaceMethodInst( - type, - irWitnessTable, - irRequirementKey); - return LoweredValInfo::simple(irSatisfyingVal); - } - else - { - SLANG_UNEXPECTED("uhandled substitution type"); - UNREACHABLE_RETURN(LoweredValInfo()); - } -} - -LoweredValInfo emitDeclRef( - IRGenContext* context, - DeclRef declRef, - IRType* type) -{ - return emitDeclRef( - context, - declRef.decl, - declRef.substitutions.substitutions, - type); -} - -static void lowerFrontEndEntryPointToIR( - IRGenContext* context, - EntryPoint* entryPoint) -{ - // TODO: We should emit an entry point as a dedicated IR function - // (distinct from the IR function used if it were called normally), - // with a mangled name based on the original function name plus - // the stage for which it is being compiled as an entry point (so - // that entry points for distinct stages always have distinct names). - // - // For now we just have an (implicit) constraint that a given - // function should only be used as an entry point for one stage, - // and any such function should *not* be used as an ordinary function. - - auto entryPointFuncDecl = entryPoint->getFuncDecl(); - - auto builder = context->irBuilder; - builder->setInsertInto(builder->getModule()->getModuleInst()); - - auto loweredEntryPointFunc = getSimpleVal(context, - ensureDecl(context, entryPointFuncDecl)); - - // Attach a marker decoration so that we recognize - // this as an entry point. - // - IRInst* instToDecorate = loweredEntryPointFunc; - if(auto irGeneric = as(instToDecorate)) - { - instToDecorate = findGenericReturnVal(irGeneric); - } - builder->addEntryPointDecoration(instToDecorate); -} - -static void lowerProgramEntryPointToIR( - IRGenContext* context, - EntryPoint* entryPoint) -{ - // First, lower the entry point like an ordinary function - - auto session = context->getSession(); - auto entryPointFuncDeclRef = entryPoint->getFuncDeclRef(); - auto entryPointFuncType = lowerType(context, getFuncType(session, entryPointFuncDeclRef)); - - auto builder = context->irBuilder; - builder->setInsertInto(builder->getModule()->getModuleInst()); - - auto loweredEntryPointFunc = getSimpleVal(context, - emitDeclRef(context, entryPointFuncDeclRef, entryPointFuncType)); - - // - if(!loweredEntryPointFunc->findDecoration()) - { - builder->addExportDecoration(loweredEntryPointFunc, getMangledName(entryPointFuncDeclRef).getUnownedSlice()); - } - - // We may have shader parameters of interface/existential type, - // which need us to supply concrete type information for specialization. - // - auto existentialTypeArgCount = entryPoint->getExistentialTypeArgCount(); - if( existentialTypeArgCount ) - { - List existentialSlotArgs; - for( Index ii = 0; ii < existentialTypeArgCount; ++ii ) - { - auto arg = entryPoint->getExistentialTypeArg(ii); - - auto irArgType = lowerType(context, arg.type); - auto irWitnessTable = lowerSimpleVal(context, arg.witness); - - existentialSlotArgs.add(irArgType); - existentialSlotArgs.add(irWitnessTable); - } - - builder->addBindExistentialSlotsDecoration(loweredEntryPointFunc, existentialSlotArgs.getCount(), existentialSlotArgs.getBuffer()); - } - - - -} - - /// Ensure that `decl` and all relevant declarations under it get emitted. -static void ensureAllDeclsRec( - IRGenContext* context, - Decl* decl) -{ - ensureDecl(context, decl); - - // Note: We are checking here for aggregate type declarations, and - // not for `ContainerDecl`s in general. This is because many kinds - // of container declarations will already take responsibility for emitting - // their children directly (e.g., a function declaration is responsible - // for emitting its own parameters). - // - // Aggregate types are the main case where we can emit an outer declaration - // and not the stuff nested inside of it. - // - if(auto containerDecl = as(decl)) - { - for (auto memberDecl : containerDecl->Members) - { - ensureAllDeclsRec(context, memberDecl); - } - } -} - -IRModule* generateIRForTranslationUnit( - TranslationUnitRequest* translationUnit) -{ - auto compileRequest = translationUnit->compileRequest; - - SharedIRGenContext sharedContextStorage( - translationUnit->getSession(), - translationUnit->compileRequest->getSink(), - translationUnit->getModuleDecl()); - SharedIRGenContext* sharedContext = &sharedContextStorage; - - IRGenContext contextStorage(sharedContext); - IRGenContext* context = &contextStorage; - - SharedIRBuilder sharedBuilderStorage; - SharedIRBuilder* sharedBuilder = &sharedBuilderStorage; - sharedBuilder->module = nullptr; - sharedBuilder->session = compileRequest->getSession(); - - IRBuilder builderStorage; - IRBuilder* builder = &builderStorage; - builder->sharedBuilder = sharedBuilder; - - IRModule* module = builder->createModule(); - sharedBuilder->module = module; - - context->irBuilder = builder; - - // We need to emit IR for all public/exported symbols - // in the translation unit. - // - // For now, we will assume that *all* global-scope declarations - // represent public/exported symbols. - - // First, ensure that all entry points have been emitted, - // in case they require special handling. - for (auto entryPoint : translationUnit->entryPoints) - { - lowerFrontEndEntryPointToIR(context, entryPoint); - } - - // - // Next, ensure that all other global declarations have - // been emitted. - for (auto decl : translationUnit->getModuleDecl()->Members) - { - ensureAllDeclsRec(context, decl); - } - -#if 0 - fprintf(stderr, "### GENERATED\n"); - dumpIR(module); - fprintf(stderr, "###\n"); -#endif - - validateIRModuleIfEnabled(compileRequest, module); - - // We will perform certain "mandatory" optimization passes now. - // These passes serve two purposes: - // - // 1. To simplify the code that we use in backend compilation, - // or when serializing/deserializing modules, so that we can - // amortize this effort when we compile multiple entry points - // that use the same module(s). - // - // 2. To ensure certain semantic properties that can't be - // validated without dataflow information. For example, we want - // to detect when a variable might be used before it is initialized. - - // Note: if you need to debug the IR that is created before - // any mandatory optimizations have been applied, then - // uncomment this line while debugging. - - // dumpIR(module); - - // First, attempt to promote local variables to SSA - // temporaries whenever possible. - constructSSA(module); - - // Do basic constant folding and dead code elimination - // using Sparse Conditional Constant Propagation (SCCP) - // - applySparseConditionalConstantPropagation(module); - - // Propagate `constexpr`-ness through the dataflow graph (and the - // call graph) based on constraints imposed by different instructions. - propagateConstExpr(module, compileRequest->getSink()); - - // TODO: give error messages if any `undefined` or - // `unreachable` instructions remain. - - checkForMissingReturns(module, compileRequest->getSink()); - - // TODO: consider doing some more aggressive optimizations - // (in particular specialization of generics) here, so - // that we can avoid doing them downstream. - // - // Note: doing specialization or inlining involving code - // from other modules potentially makes the IR we generate - // "fragile" in that we'd now need to recompile when - // a module we depend on changes. - - validateIRModuleIfEnabled(compileRequest, module); - - // If we are being sked to dump IR during compilation, - // then we can dump the initial IR for the module here. - if(compileRequest->shouldDumpIR) - { - DiagnosticSinkWriter writer(compileRequest->getSink()); - dumpIR(module, &writer); - } - - return module; -} - -RefPtr generateIRForProgram( - Session* session, - Program* program, - DiagnosticSink* sink) -{ -// auto compileRequest = translationUnit->compileRequest; - - SharedIRGenContext sharedContextStorage( - session, - sink); - SharedIRGenContext* sharedContext = &sharedContextStorage; - - IRGenContext contextStorage(sharedContext); - IRGenContext* context = &contextStorage; - - SharedIRBuilder sharedBuilderStorage; - SharedIRBuilder* sharedBuilder = &sharedBuilderStorage; - sharedBuilder->module = nullptr; - sharedBuilder->session = session; - - IRBuilder builderStorage; - IRBuilder* builder = &builderStorage; - builder->sharedBuilder = sharedBuilder; - - RefPtr module = builder->createModule(); - sharedBuilder->module = module; - - context->irBuilder = builder; - - // We need to emit symbols for all of the entry - // points in the program; this is especially - // important in the case where a generic entry - // point is being specialized. - // - for(auto entryPoint : program->getEntryPoints()) - { - lowerProgramEntryPointToIR(context, entryPoint); - } - - - // Now lower all the arguments supplied for global generic - // type parameters. - // - for (RefPtr subst = program->getGlobalGenericSubstitution(); subst; subst = subst->outer) - { - auto gSubst = subst.as(); - if(!gSubst) - continue; - - IRInst* typeParam = getSimpleVal(context, ensureDecl(context, gSubst->paramDecl)); - IRType* typeVal = lowerType(context, gSubst->actualType); - - // bind `typeParam` to `typeVal` - builder->emitBindGlobalGenericParam(typeParam, typeVal); - - for (auto& constraintArg : gSubst->constraintArgs) - { - IRInst* constraintParam = getSimpleVal(context, ensureDecl(context, constraintArg.decl)); - IRInst* constraintVal = lowerSimpleVal(context, constraintArg.val); - - // bind `constraintParam` to `constraintVal` - builder->emitBindGlobalGenericParam(constraintParam, constraintVal); - } - } - - // We may have shader parameters of interface/existential type, - // which need us to supply concrete type information for specialization. - // - auto existentialTypeArgCount = program->getExistentialTypeArgCount(); - if( existentialTypeArgCount ) - { - List existentialSlotArgs; - for( Index ii = 0; ii < existentialTypeArgCount; ++ii ) - { - auto arg = program->getExistentialTypeArg(ii); - - auto irArgType = lowerType(context, arg.type); - auto irWitnessTable = lowerSimpleVal(context, arg.witness); - - existentialSlotArgs.add(irArgType); - existentialSlotArgs.add(irWitnessTable); - } - - builder->emitBindGlobalExistentialSlots(existentialSlotArgs.getCount(), existentialSlotArgs.getBuffer()); - } - - - // TODO: Should we apply any of the validation or - // mandatory optimization passes here? - - return module; -} - -} // namespace Slang -- cgit v1.2.3