summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--source/slang/bytecode.cpp6
-rw-r--r--source/slang/check.cpp809
-rw-r--r--source/slang/decl-defs.h9
-rw-r--r--source/slang/diagnostic-defs.h2
-rw-r--r--source/slang/emit.cpp146
-rw-r--r--source/slang/ir-inst-defs.h3
-rw-r--r--source/slang/ir-insts.h45
-rw-r--r--source/slang/ir.cpp589
-rw-r--r--source/slang/ir.h103
-rw-r--r--source/slang/lookup.cpp78
-rw-r--r--source/slang/lookup.h11
-rw-r--r--source/slang/lower-to-ir.cpp339
-rw-r--r--source/slang/lower.cpp10
-rw-r--r--source/slang/mangle.cpp38
-rw-r--r--source/slang/mangle.h4
-rw-r--r--source/slang/parser.cpp17
-rw-r--r--source/slang/slang.cpp2
-rw-r--r--source/slang/syntax.cpp92
-rw-r--r--source/slang/syntax.h93
-rw-r--r--source/slang/type-defs.h1
-rw-r--r--source/slang/val-defs.h69
-rw-r--r--tests/compute/generics-constrained.slang46
-rw-r--r--tests/compute/generics-constrained.slang.expected.txt4
-rw-r--r--tests/ir/loop.slang.expected124
24 files changed, 2156 insertions, 484 deletions
diff --git a/source/slang/bytecode.cpp b/source/slang/bytecode.cpp
index 085b7303d..f1b23849d 100644
--- a/source/slang/bytecode.cpp
+++ b/source/slang/bytecode.cpp
@@ -728,7 +728,7 @@ BytecodeGenerationPtr<BCSymbol> generateBytecodeSymbolForInst(
paramCount++;
}
- for( auto ii = bb->getFirstInst(); ii; ii = ii->nextInst )
+ for( auto ii = bb->getFirstInst(); ii; ii = ii->getNextInst() )
{
switch( ii->op )
{
@@ -792,7 +792,7 @@ BytecodeGenerationPtr<BCSymbol> generateBytecodeSymbolForInst(
// Now loop over the non-parameter instructions and
// allocate actual register locations to them.
- for( auto ii = bb->getFirstInst(); ii; ii = ii->nextInst )
+ for( auto ii = bb->getFirstInst(); ii; ii = ii->getNextInst() )
{
switch(ii->op)
{
@@ -857,7 +857,7 @@ BytecodeGenerationPtr<BCSymbol> generateBytecodeSymbolForInst(
UInt blockOffset = subContext->currentBytecode.Count();
blockOffsets.Add( blockOffset );
- for( auto ii = bb->getFirstInst(); ii; ii = ii->nextInst )
+ for( auto ii = bb->getFirstInst(); ii; ii = ii->getNextInst() )
{
// What we do with each instruction depends a bit on the
// kind of instruction it is.
diff --git a/source/slang/check.cpp b/source/slang/check.cpp
index 5ebb22999..a42edc331 100644
--- a/source/slang/check.cpp
+++ b/source/slang/check.cpp
@@ -212,9 +212,22 @@ namespace Slang
case LookupResultItem::Breadcrumb::Kind::Member:
bb = ConstructDeclRefExpr(breadcrumb->declRef, bb, loc);
break;
+
case LookupResultItem::Breadcrumb::Kind::Deref:
bb = ConstructDerefExpr(bb, loc);
break;
+
+ case LookupResultItem::Breadcrumb::Kind::Constraint:
+ {
+ // TODO: do we need to make something more
+ // explicit here?
+ bb = ConstructDeclRefExpr(
+ breadcrumb->declRef,
+ bb,
+ loc);
+ }
+ break;
+
default:
SLANG_UNREACHABLE("all cases handle");
}
@@ -425,9 +438,20 @@ namespace Slang
// the name of a non-proper type, and then have the compiler fill
// in the default values for its type arguments (e.g., a variable
// given type `Texture2D` will actually have type `Texture2D<float4>`).
- bool CoerceToProperTypeImpl(TypeExp const& typeExp, RefPtr<Type>* outProperType)
+ bool CoerceToProperTypeImpl(
+ TypeExp const& typeExp,
+ RefPtr<Type>* outProperType,
+ DiagnosticSink* sink)
{
Type* type = typeExp.type.Ptr();
+ if(!type && typeExp.exp)
+ {
+ if(auto typeType = typeExp.exp->type.type.As<TypeType>())
+ {
+ type = typeType->type;
+ }
+ }
+
if (auto genericDeclRefType = type->As<GenericDeclRefType>())
{
// We are using a reference to a generic declaration as a concrete
@@ -447,11 +471,11 @@ namespace Slang
{
if (!typeParam->initType.exp)
{
- if (outProperType)
+ if (sink)
{
if (!isRewriteMode())
{
- getSink()->diagnose(typeExp.exp.Ptr(), Diagnostics::unimplemented, "can't fill in default for generic type parameter");
+ sink->diagnose(typeExp.exp.Ptr(), Diagnostics::unimplemented, "can't fill in default for generic type parameter");
}
*outProperType = getSession()->getErrorType();
}
@@ -466,11 +490,11 @@ namespace Slang
{
if (!valParam->initExpr)
{
- if (outProperType)
+ if (sink)
{
if (!isRewriteMode())
{
- getSink()->diagnose(typeExp.exp.Ptr(), Diagnostics::unimplemented, "can't fill in default for generic type parameter");
+ sink->diagnose(typeExp.exp.Ptr(), Diagnostics::unimplemented, "can't fill in default for generic type parameter");
}
*outProperType = getSession()->getErrorType();
}
@@ -509,13 +533,16 @@ namespace Slang
TypeExp CoerceToProperType(TypeExp const& typeExp)
{
TypeExp result = typeExp;
- CoerceToProperTypeImpl(typeExp, &result.type);
+ CoerceToProperTypeImpl(typeExp, &result.type, getSink());
return result;
}
- bool CanCoerceToProperType(TypeExp const& typeExp)
+ TypeExp tryCoerceToProperType(TypeExp const& typeExp)
{
- return CoerceToProperTypeImpl(typeExp, nullptr);
+ TypeExp result = typeExp;
+ if(!CoerceToProperTypeImpl(typeExp, &result.type, nullptr))
+ return TypeExp();
+ return result;
}
// Check a type, and coerce it to be proper
@@ -1156,16 +1183,13 @@ namespace Slang
}
}
+ genericDecl->SetCheckState(DeclCheckState::CheckedHeader);
+
// check the nested declaration
// TODO: this needs to be done in an appropriate environment...
checkDecl(genericDecl->inner);
}
- void visitInterfaceDecl(InterfaceDecl* /*decl*/)
- {
- // TODO: do some actual checking of members here
- }
-
void visitInheritanceDecl(InheritanceDecl* inheritanceDecl)
{
// check the type being inherited from
@@ -1417,19 +1441,6 @@ namespace Slang
}
}
- void visitClassDecl(ClassDecl * classNode)
- {
- if (classNode->IsChecked(DeclCheckState::Checked))
- return;
- classNode->SetCheckState(DeclCheckState::Checked);
-
- for (auto field : classNode->GetFields())
- {
- field->type = CheckUsableType(field->type);
- field->SetCheckState(DeclCheckState::Checked);
- }
- }
-
void visitStructField(StructField* field)
{
// TODO: bottleneck through general-case variable checking
@@ -1438,16 +1449,298 @@ namespace Slang
field->SetCheckState(DeclCheckState::Checked);
}
- void visitStructDecl(StructDecl * structNode)
+ bool doesSignatureMatchRequirement(
+ CallableDecl* memberDecl,
+ DeclRef<CallableDecl> requiredMemberDeclRef)
+ {
+ // TODO: actually implement matching here. For now we'll
+ // just pretend that things are satisfied in order to make progress.
+ return true;
+ }
+
+ // Does the given `memberDecl` work as an implementation
+ // to satisfy the requirement `requiredMemberDeclRef`
+ // from an interface?
+ bool doesMemberSatisfyRequirement(
+ Decl* memberDecl,
+ DeclRef<Decl> requiredMemberDeclRef)
+ {
+ // At a high level, we want to chack that the
+ // `memberDecl` and the `requiredMemberDeclRef`
+ // have the same AST node class, and then also
+ // check that their signatures match.
+ //
+ // There are a bunch of detailed decisions that
+ // have to be made, though, because we might, e.g.,
+ // allow a function with more general parameter
+ // types to satisfy a requirement with more
+ // specific parameter types.
+ //
+ // If we ever allow for "property" declarations,
+ // then we would probably need to allow an
+ // ordinary field to satisfy a property requirement.
+ //
+ // An associated type requirement should be allowed
+ // to be satisfied by any type declaration:
+ // a typedef, a `struct`, etc.
+
+ if (auto memberFuncDecl = dynamic_cast<FuncDecl*>(memberDecl))
+ {
+ if (auto requiredFuncDeclRef = requiredMemberDeclRef.As<FuncDecl>())
+ {
+ // Check signature match.
+ return doesSignatureMatchRequirement(
+ memberFuncDecl,
+ requiredFuncDeclRef);
+ }
+ }
+ else if (auto memberInitDecl = dynamic_cast<ConstructorDecl*>(memberDecl))
+ {
+ if (auto requiredInitDecl = requiredMemberDeclRef.As<ConstructorDecl>())
+ {
+ // Check signature match.
+ return doesSignatureMatchRequirement(
+ memberInitDecl,
+ requiredInitDecl);
+ }
+ }
+
+ // Default: just assume that thing aren't being satisfied.
+ return false;
+ }
+
+ // Find the appropriate member of a declared type to
+ // satisfy a requirement of an interface the type
+ // claims to conform to.
+ //
+ // The type declaration `typeDecl` has declared that it
+ // conforms to the interface `interfaceDeclRef`, and
+ // `requiredMemberDeclRef` is a required member of
+ // the interface.
+ RefPtr<Decl> findWitnessForInterfaceRequirement(
+ AggTypeDecl* typeDecl,
+ InheritanceDecl* inheritanceDecl,
+ DeclRef<InterfaceDecl> interfaceDeclRef,
+ DeclRef<Decl> requiredMemberDeclRef)
+ {
+ // We will look up members with the same name,
+ // since only same-name members will be able to
+ // satisfy the requirement.
+ //
+ // TODO: this won't work right now for members that
+ // don't have names, which right now includes
+ // initializers/constructors.
+ Name* name = requiredMemberDeclRef.GetName();
+
+ // We are basically looking up members of the
+ // given type, but we need to be a bit careful.
+ // We *cannot* perfom lookup "through" inheritance
+ // declarations for this or other interfaces,
+ // since that would let us satisfy a requirement
+ // with itself.
+ //
+ // There's also an interesting question of whether
+ // we can/should support innterface requirements
+ // being satisfied via `__transparent` members.
+ // This seems like a "clever" idea rather than
+ // a useful one, and IR generation would
+ // need to construct real IR to trampoline over
+ // to the implementation.
+ //
+ // The final case that can't be reduced to just
+ // "a directly declared member with the same name"
+ // is the case where the type inherits a member
+ // that can satisfy the requirement from a base type.
+ // We are ignoring implementation inheritance for
+ // now, so we won't worry about this.
+
+ // Make sure that by-name lookup is possible.
+ buildMemberDictionary(typeDecl);
+
+ Decl* firstMemberOfName = nullptr;
+ typeDecl->memberDictionary.TryGetValue(name, firstMemberOfName);
+
+ if (!firstMemberOfName)
+ {
+ getSink()->diagnose(inheritanceDecl, Diagnostics::typeDoesntImplementInterfaceRequirement, typeDecl, requiredMemberDeclRef);
+ return nullptr;
+ }
+
+ // Iterate over the members and look for one that matches
+ // the expected signature for the requirement.
+ for (auto memberDecl = firstMemberOfName; memberDecl; memberDecl = memberDecl->nextInContainerWithSameName)
+ {
+ if (doesMemberSatisfyRequirement(memberDecl, requiredMemberDeclRef))
+ return memberDecl;
+ }
+
+ // No suitable member found, although there were candidates.
+ //
+ // TODO: Eventually we might want something akin to the current
+ // overload resolution logic, where we keep track of a list
+ // of "candidates" for satisfaction of the requirement,
+ // and if nothing is found we print the candidates
+
+ getSink()->diagnose(inheritanceDecl, Diagnostics::typeDoesntImplementInterfaceRequirement, typeDecl, requiredMemberDeclRef);
+ return nullptr;
+ }
+
+ // Check that the type declaration `typeDecl`, which
+ // declares conformance to the interface `interfaceDeclRef`,
+ // (via the given `inheritanceDecl`) actually provides
+ // members to satisfy all the requirements in the interface.
+ void checkInterfaceConformance(
+ AggTypeDecl* typeDecl,
+ InheritanceDecl* inheritanceDecl,
+ DeclRef<InterfaceDecl> interfaceDeclRef)
+ {
+ // We need to check the declaration of the interface
+ // before we can check that we conform to it.
+ EnsureDecl(interfaceDeclRef.getDecl());
+
+ // TODO: If we ever allow for implementation inheritance,
+ // then we will need to consider the case where a type
+ // declares that it conforms to an interface, but one of
+ // its (non-interface) base types already conforms to
+ // that interface, so that all of the requirements are
+ // already satisfied with inherited implementations...
+
+ for (auto requiredMemberDeclRef : getMembers(interfaceDeclRef))
+ {
+ // Some members of the interface don't actually represent
+ // things that we required of the implementing type.
+ // For example, when the interface declares that
+ // it inherits from another interface, we don't look for
+ // a matching inheritance clause on the type, but
+ // instead require that it also conforms to that
+ // interface.
+ if (auto requiredInheritanceDeclRef = requiredMemberDeclRef.As<InheritanceDecl>())
+ {
+ // Recursively check that the type conforms
+ // to the inherited interface.
+ //
+ // TODO: we *really* need a linearization step here!!!!
+ checkConformanceToType(
+ typeDecl,
+ inheritanceDecl,
+ getBaseType(requiredInheritanceDeclRef));
+ continue;
+ }
+
+ // Look for a member in the type that can satisfy the
+ // interface requirement.
+ auto conformanceWitness = findWitnessForInterfaceRequirement(
+ typeDecl,
+ inheritanceDecl,
+ interfaceDeclRef,
+ requiredMemberDeclRef);
+
+ if (!conformanceWitness)
+ continue;
+
+ // Store that witness into a table stored on the `inheritnaceDecl`
+ // so that it can be used for downstream code generation.
+
+ inheritanceDecl->requirementWitnesses.Add(requiredMemberDeclRef, conformanceWitness);
+ }
+ }
+
+ void checkConformanceToType(
+ AggTypeDecl* typeDecl,
+ InheritanceDecl* inheritanceDecl,
+ Type* baseType)
+ {
+ if (auto baseDeclRefType = baseType->As<DeclRefType>())
+ {
+ auto baseTypeDeclRef = baseDeclRefType->declRef;
+ if (auto baseInterfaceDeclRef = baseTypeDeclRef.As<InterfaceDecl>())
+ {
+ // The type is stating that it conforms to an interface.
+ // We need to check that it provides all of the members
+ // required by that interface.
+ checkInterfaceConformance(
+ typeDecl,
+ inheritanceDecl,
+ baseInterfaceDeclRef);
+ return;
+ }
+ }
+
+ getSink()->diagnose(inheritanceDecl, Diagnostics::unimplemented, "type not supported for inheritance");
+ }
+
+ // Check that the type declaration `typeDecl`, which
+ // declares that it inherits from another type via
+ // `inheritanceDecl` actually does what it needs to
+ // for that inheritance to be valid.
+ void checkConformance(
+ AggTypeDecl* typeDecl,
+ InheritanceDecl* inheritanceDecl)
+ {
+ // Look at the type being inherited from, and validate
+ // appropriately.
+ auto baseType = inheritanceDecl->base.type;
+ checkConformanceToType(typeDecl, inheritanceDecl, baseType);
+ }
+
+ void visitAggTypeDecl(AggTypeDecl* decl)
{
- if (structNode->IsChecked(DeclCheckState::Checked))
+ if (decl->IsChecked(DeclCheckState::Checked))
return;
- structNode->SetCheckState(DeclCheckState::Checked);
- for (auto field : structNode->GetFields())
+ // TODO: we should check inheritance declarations
+ // first, since they need to be validated before
+ // we can make use of the type (e.g., you need
+ // to know that `A` inherits from `B` in order
+ // to check an expression like `aValue.bMethod()`
+ // where `aValue` is of type `A` but `bMethod`
+ // is defined in type `B`.
+ //
+ // TODO: We should also add a pass that takes
+ // all the stated inheritance relationships,
+ // expands them to include implicitic inheritance,
+ // and then linearizes them. This would allow
+ // later passes that need to know everything
+ // a type inherits from to proceed linearly
+ // through the list, rather than having to
+ // recurse (and potentially see the same interface
+ // more than once).
+
+ decl->SetCheckState(DeclCheckState::CheckedHeader);
+
+ // Now check all of the member declarations.
+ for (auto member : decl->Members)
{
- checkDecl(field);
+ checkDecl(member);
}
+
+ // After we've checked members, we need to go through
+ // any inheritance clauses on the type itself, and
+ // confirm that the type actually provides whatever
+ // those clauses require.
+
+ if (auto interfaceDecl = dynamic_cast<InterfaceDecl*>(decl))
+ {
+ // Don't check that an interface conforms to the
+ // things it inherits from.
+ }
+ else
+ {
+ // For non-interface types we need to check conformance.
+ //
+ // TODO: Need to figure out what this should do for
+ // `abstract` types if we ever add them. Should they
+ // be required to implement all interface requirements,
+ // just with `abstract` methods that replicate things?
+ // (That's what C# does).
+
+ for (auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>())
+ {
+ checkConformance(decl, inheritanceDecl);
+ }
+ }
+
+ decl->SetCheckState(DeclCheckState::Checked);
}
void visitDeclGroup(DeclGroup* declGroup)
@@ -2751,38 +3044,34 @@ namespace Slang
// Default behavior is to look at all available `__subscript`
// declarations on the type and try to call one of them.
- if (auto declRefType = baseType->AsDeclRefType())
{
- if (auto aggTypeDeclRef = declRefType->declRef.As<AggTypeDecl>())
+ LookupResult lookupResult = lookUpMember(
+ getSession(),
+ this,
+ getName("operator[]"),
+ baseType);
+ if (!lookupResult.isValid())
{
- // Checking of the type must be complete before we can reference its members safely
- EnsureDecl(aggTypeDeclRef.getDecl(), DeclCheckState::Checked);
-
- // Note(tfoley): The name used for lookup here is a bit magical, since
- // it must match what the parser installed in subscript declarations.
- LookupResult lookupResult = LookUpLocal(
- getSession(),
- this, getName("operator[]"), aggTypeDeclRef);
- if (!lookupResult.isValid())
- {
- goto fail;
- }
-
- RefPtr<Expr> subscriptFuncExpr = createLookupResultExpr(
- lookupResult, subscriptExpr->BaseExpression, subscriptExpr->loc);
+ goto fail;
+ }
- // Now that we know there is at least one subscript member,
- // we will construct a reference to it and try to call it
+ // Now that we know there is at least one subscript member,
+ // we will construct a reference to it and try to call it.
+ //
+ // Note: the expression may be an `OverloadedExpr`, in which
+ // case the attempt to call it will trigger overload
+ // resolution.
+ RefPtr<Expr> subscriptFuncExpr = createLookupResultExpr(
+ lookupResult, subscriptExpr->BaseExpression, subscriptExpr->loc);
- RefPtr<InvokeExpr> subscriptCallExpr = new InvokeExpr();
- subscriptCallExpr->loc = subscriptExpr->loc;
- subscriptCallExpr->FunctionExpr = subscriptFuncExpr;
+ RefPtr<InvokeExpr> subscriptCallExpr = new InvokeExpr();
+ subscriptCallExpr->loc = subscriptExpr->loc;
+ subscriptCallExpr->FunctionExpr = subscriptFuncExpr;
- // TODO(tfoley): This path can support multiple arguments easily
- subscriptCallExpr->Arguments.Add(subscriptExpr->IndexExpression);
+ // TODO(tfoley): This path can support multiple arguments easily
+ subscriptCallExpr->Arguments.Add(subscriptExpr->IndexExpression);
- return CheckInvokeExprWithCheckedOperands(subscriptCallExpr.Ptr());
- }
+ return CheckInvokeExprWithCheckedOperands(subscriptCallExpr.Ptr());
}
fail:
@@ -2987,9 +3276,59 @@ namespace Slang
vectorType->elementCount);
}
- bool DoesTypeConformToInterface(
- RefPtr<Type> type,
- DeclRef<InterfaceDecl> interfaceDeclRef)
+ struct TypeWitnessBreadcrumb
+ {
+ TypeWitnessBreadcrumb* prev;
+ DeclRef<Decl> declRef;
+ };
+
+ RefPtr<Val> createTypeWitness(
+ RefPtr<Type> type,
+ DeclRef<InterfaceDecl> interfaceDeclRef,
+ TypeWitnessBreadcrumb* inBreadcrumbs)
+ {
+ if(!inBreadcrumbs)
+ {
+ // We need to construct a witness to the fact
+ // that `type` has been proven to be equal
+ // to `interfaceDeclRef`.
+ //
+ SLANG_UNEXPECTED("reflexive type witness");
+ return nullptr;
+ }
+
+ auto breadcrumbs = inBreadcrumbs;
+
+ auto bb = breadcrumbs;
+ breadcrumbs = breadcrumbs->prev;
+
+ if(breadcrumbs)
+ {
+ // There are multiple steps in the proof, so
+ // we need a transitive witness to show that
+ // because `A : B` and `B : C` then `A : C`
+ //
+ SLANG_UNEXPECTED("transitive type witness");
+ return nullptr;
+ }
+
+ // Simple case: we have a single declaration
+ // that shows that `type` conforms to `interfaceDeclRef`.
+ //
+
+ RefPtr<DeclaredSubtypeWitness> witness = new DeclaredSubtypeWitness();
+ witness->sub = type;
+ witness->sup = DeclRefType::Create(getSession(), interfaceDeclRef);
+ witness->declRef = bb->declRef;
+ return witness;
+ }
+
+ bool doesTypeConformToInterfaceImpl(
+ RefPtr<Type> originalType,
+ RefPtr<Type> type,
+ DeclRef<InterfaceDecl> interfaceDeclRef,
+ RefPtr<Val>* outWitness,
+ TypeWitnessBreadcrumb* inBreadcrumbs)
{
// for now look up a conformance member...
if(auto declRefType = type->As<DeclRefType>())
@@ -3002,7 +3341,13 @@ namespace Slang
// the interface needs to be "object-safe" for us to
// really make this determination...
if(declRef == interfaceDeclRef)
+ {
+ if(outWitness)
+ {
+ *outWitness = createTypeWitness(originalType, interfaceDeclRef, inBreadcrumbs);
+ }
return true;
+ }
if( auto aggTypeDeclRef = declRef.As<AggTypeDecl>() )
{
@@ -3022,8 +3367,18 @@ namespace Slang
// conformances multiple times.
auto inheritedType = getBaseType(inheritanceDeclRef);
- if(DoesTypeConformToInterface(inheritedType, interfaceDeclRef))
+
+ // We need to ensure that the witness that gets created
+ // is a composite one, reflecting lookup through
+ // the inheritance declaration.
+ TypeWitnessBreadcrumb breadcrumb;
+ breadcrumb.prev = inBreadcrumbs;
+ breadcrumb.declRef = inheritanceDeclRef;
+
+ if(doesTypeConformToInterfaceImpl(originalType, inheritedType, interfaceDeclRef, outWitness, &breadcrumb))
+ {
return true;
+ }
}
}
else if( auto genericTypeParamDeclRef = declRef.As<GenericTypeParamDecl>() )
@@ -3045,8 +3400,18 @@ namespace Slang
if(subDeclRef->declRef != genericTypeParamDeclRef)
continue;
- if(DoesTypeConformToInterface(sup, interfaceDeclRef))
+ // The witness that we create needs to reflect that
+ // it found the needed conformance by lookup through
+ // a generic type constraint.
+
+ TypeWitnessBreadcrumb breadcrumb;
+ breadcrumb.prev = inBreadcrumbs;
+ breadcrumb.declRef = constraintDeclRef;
+
+ if(doesTypeConformToInterfaceImpl(originalType, sup, interfaceDeclRef, outWitness, &breadcrumb))
+ {
return true;
+ }
}
}
}
@@ -3055,6 +3420,22 @@ namespace Slang
return false;
}
+ bool DoesTypeConformToInterface(
+ RefPtr<Type> type,
+ DeclRef<InterfaceDecl> interfaceDeclRef)
+ {
+ return doesTypeConformToInterfaceImpl(type, type, interfaceDeclRef, nullptr, nullptr);
+ }
+
+ RefPtr<Val> tryGetInterfaceConformanceWitness(
+ RefPtr<Type> type,
+ DeclRef<InterfaceDecl> interfaceDeclRef)
+ {
+ RefPtr<Val> result;
+ doesTypeConformToInterfaceImpl(type, type, interfaceDeclRef, &result, nullptr);
+ return result;
+ }
+
RefPtr<Type> TryJoinTypeWithInterface(
RefPtr<Type> type,
DeclRef<InterfaceDecl> interfaceDeclRef)
@@ -3308,6 +3689,7 @@ namespace Slang
ArityChecked,
FixityChecked,
TypeChecked,
+ DirectionChecked,
Appicable,
};
Status status = Status::Unchecked;
@@ -3324,6 +3706,11 @@ namespace Slang
// How much conversion cost should be considered for this overload,
// when ranking candidates.
ConversionCost conversionCostSum = kConversionCost_None;
+
+ // When required, a candidate can store a pre-checked list of
+ // arguments so that we don't have to repeat work across checking
+ // phases. Currently this is only needed for generics.
+ RefPtr<Substitutions> subst;
};
@@ -3541,6 +3928,12 @@ namespace Slang
{
auto genericDeclRef = candidate.item.declRef.As<GenericDecl>();
+ // We will go ahead and hang onto the arguments that we've
+ // already checked, since downstream validation might need
+ // them.
+ candidate.subst = new Substitutions();
+ auto& checkedArgs = candidate.subst->args;
+
int aa = 0;
for (auto memberRef : getMembers(genericDeclRef))
{
@@ -3548,17 +3941,20 @@ namespace Slang
{
auto arg = context.getArg(aa++);
+ TypeExp typeExp;
if (context.mode == OverloadResolveContext::Mode::JustTrying)
{
- if (!CanCoerceToProperType(TypeExp(arg)))
+ typeExp = tryCoerceToProperType(TypeExp(arg));
+ if(!typeExp.type)
{
return false;
}
}
else
{
- TypeExp typeExp = CoerceToProperType(TypeExp(arg));
+ typeExp = CoerceToProperType(TypeExp(arg));
}
+ checkedArgs.Add(typeExp.type);
}
else if (auto valParamRef = memberRef.As<GenericValueParamDecl>())
{
@@ -3573,11 +3969,10 @@ namespace Slang
}
candidate.conversionCostSum += cost;
}
- else
- {
- arg = Coerce(GetType(valParamRef), arg);
- auto val = ExtractGenericArgInteger(arg);
- }
+
+ arg = Coerce(GetType(valParamRef), arg);
+ auto val = ExtractGenericArgInteger(arg);
+ checkedArgs.Add(val);
}
else
{
@@ -3585,6 +3980,7 @@ namespace Slang
}
}
+ // Okay, we've made it!
return true;
}
@@ -3650,6 +4046,100 @@ namespace Slang
return true;
}
+ // Create a witness that attests to the fact that `type`
+ // is equal to itself.
+ RefPtr<Val> createTypeEqualityWitness(
+ Type* type)
+ {
+ SLANG_UNEXPECTED("unimplemented");
+ }
+
+ // If `sub` is a subtype of `sup`, then return a value that
+ // can serve as a "witness" for that fact.
+ RefPtr<Val> tryGetSubtypeWitness(
+ RefPtr<Type> sub,
+ RefPtr<Type> sup)
+ {
+ if(sub->Equals(sup))
+ {
+ // They are the same type, so we just need a witness
+ // for type equality.
+ return createTypeEqualityWitness(sub);
+ }
+
+ if(auto supDeclRefType = sup->As<DeclRefType>())
+ {
+ auto supDeclRef = supDeclRefType->declRef;
+ if(auto supInterfaceDeclRef = supDeclRef.As<InterfaceDecl>())
+ {
+ if(auto witness = tryGetInterfaceConformanceWitness(sub, supInterfaceDeclRef))
+ {
+ return witness;
+ }
+ }
+ }
+
+ return nullptr;
+ }
+
+ // In the case where we are explicitly applying a generic
+ // to arguments (e.g., `G<A,B>`) check that the constraints
+ // on those parameters are satisfied.
+ //
+ // Note: the constraints actually work as additional parameters/arguments
+ // of the generic, and so we need to reify them into the final
+ // argument list.
+ //
+ bool TryCheckOverloadCandidateConstraints(
+ OverloadResolveContext& context,
+ OverloadCandidate const& candidate)
+ {
+ // We only need this step for generics, so always succeed on
+ // everything else.
+ if(candidate.flavor != OverloadCandidate::Flavor::Generic)
+ return true;
+
+ auto genericDeclRef = candidate.item.declRef.As<GenericDecl>();
+ assert(genericDeclRef);
+
+ // We should have the existing arguments to the generic
+ // handy, so that we can construct a substitution list.
+
+ RefPtr<Substitutions> subst = candidate.subst;
+ assert(subst);
+
+ subst->genericDecl = genericDeclRef.getDecl();
+ subst->outer = genericDeclRef.substitutions;
+
+ for( auto constraintDecl : genericDeclRef.getDecl()->getMembersOfType<GenericTypeConstraintDecl>() )
+ {
+ DeclRef<GenericTypeConstraintDecl> constraintDeclRef(
+ constraintDecl,
+ subst);
+
+ auto sub = GetSub(constraintDeclRef);
+ auto sup = GetSup(constraintDeclRef);
+
+ auto subTypeWitness = tryGetSubtypeWitness(sub, sup);
+ if(subTypeWitness)
+ {
+ subst->args.Add(subTypeWitness);
+ }
+ else
+ {
+ if(context.mode != OverloadResolveContext::Mode::JustTrying)
+ {
+ // TODO: diagnose a problem here
+ getSink()->diagnose(context.loc, Diagnostics::unimplemented, "generic constraint not satisfied");
+ }
+ return false;
+ }
+ }
+
+ // Done checking all the constraints, hooray.
+ return true;
+ }
+
// Try to check an overload candidate, but bail out
// if any step fails
void TryCheckOverloadCandidate(
@@ -3671,15 +4161,18 @@ namespace Slang
if (!TryCheckOverloadCandidateDirections(context, candidate))
return;
+ candidate.status = OverloadCandidate::Status::DirectionChecked;
+ if (!TryCheckOverloadCandidateConstraints(context, candidate))
+ return;
+
candidate.status = OverloadCandidate::Status::Appicable;
}
// Create the representation of a given generic applied to some arguments
- RefPtr<Expr> CreateGenericDeclRef(
- RefPtr<Expr> baseExpr,
- RefPtr<Expr> originalExpr,
- UInt argCount,
- RefPtr<Expr> const* args)
+ RefPtr<Expr> createGenericDeclRef(
+ RefPtr<Expr> baseExpr,
+ RefPtr<Expr> originalExpr,
+ RefPtr<Substitutions> subst)
{
auto baseDeclRefExpr = baseExpr.As<DeclRefExpr>();
if (!baseDeclRefExpr)
@@ -3694,16 +4187,9 @@ namespace Slang
return CreateErrorExpr(originalExpr);
}
- RefPtr<Substitutions> subst = new Substitutions();
subst->genericDecl = baseGenericRef.getDecl();
subst->outer = baseGenericRef.substitutions;
- for(UInt aa = 0; aa < argCount; ++aa)
- {
- auto arg = args[aa];
- subst->args.Add(ExtractGenericArgVal(arg));
- }
-
DeclRef<Decl> innerDeclRef(GetInner(baseGenericRef), subst);
return ConstructDeclRefExpr(
@@ -3753,6 +4239,9 @@ namespace Slang
if (!TryCheckOverloadCandidateDirections(context, candidate))
goto error;
+ if (!TryCheckOverloadCandidateConstraints(context, candidate))
+ goto error;
+
{
auto baseExpr = ConstructLookupResultExpr(
candidate.item, context.baseExpr, context.funcLoc);
@@ -3792,9 +4281,10 @@ namespace Slang
break;
case OverloadCandidate::Flavor::Generic:
- return CreateGenericDeclRef(baseExpr, context.originalExpr,
- context.argCount,
- context.args);
+ return createGenericDeclRef(
+ baseExpr,
+ context.originalExpr,
+ candidate.subst);
break;
default:
@@ -5144,7 +5634,7 @@ namespace Slang
expr->type = QualType(getSession()->getErrorType());
- auto lookupResult = LookUp(
+ auto lookupResult = lookUp(
getSession(),
this, expr->name, expr->scope);
if (lookupResult.isValid())
@@ -5449,6 +5939,12 @@ namespace Slang
// Note: Checking for vector types before declaration-reference types,
// because vectors are also declaration reference types...
+ //
+ // Also note: the way this is done right now means that the ability
+ // to swizzle vectors interferes with any chance of looking up
+ // members via extension, for vector or scalar types.
+ //
+ // TODO: Matrix swizzles probably need to be handled at some point.
if (auto baseVecType = baseType->AsVectorType())
{
return CheckSwizzleExpr(
@@ -5473,69 +5969,45 @@ namespace Slang
// TODO: this duplicates a *lot* of logic with the case below.
// We need to fix that.
auto type = typeType->type;
- if(auto declRefType = type->AsDeclRefType())
- {
- if (auto aggTypeDeclRef = declRefType->declRef.As<AggTypeDecl>())
- {
- // Checking of the type must be complete before we can reference its members safely
- EnsureDecl(aggTypeDeclRef.getDecl(), DeclCheckState::Checked);
-
- LookupResult lookupResult = LookUpLocal(
- getSession(),
- this, expr->name, aggTypeDeclRef);
- if (!lookupResult.isValid())
- {
- return lookupResultFailure(expr, baseType);
- }
- // TODO: need to filter for declarations that are valid to refer
- // to in this context...
-
- return createLookupResultExpr(
- lookupResult,
- expr->BaseExpression,
- expr->loc);
- }
- }
- }
- else if (auto declRefType = baseType->AsDeclRefType())
- {
- if (auto aggTypeDeclRef = declRefType->declRef.As<AggTypeDecl>())
+ LookupResult lookupResult = lookUpMember(
+ getSession(),
+ this,
+ expr->name,
+ type);
+ if (!lookupResult.isValid())
{
- // Checking of the type must be complete before we can reference its members safely
- EnsureDecl(aggTypeDeclRef.getDecl(), DeclCheckState::Checked);
-
- LookupResult lookupResult = LookUpLocal(
- getSession(),
- this, expr->name, aggTypeDeclRef);
- if (!lookupResult.isValid())
- {
- return lookupResultFailure(expr, baseType);
- }
-
- return createLookupResultExpr(
- lookupResult,
- expr->BaseExpression,
- expr->loc);
+ return lookupResultFailure(expr, baseType);
}
- // catch-all
- return lookupResultFailure(expr, baseType);
+ // TODO: need to filter for declarations that are valid to refer
+ // to in this context...
+
+ return createLookupResultExpr(
+ lookupResult,
+ expr->BaseExpression,
+ expr->loc);
}
- // All remaining cases assume we have a `BasicType`
- else if (!baseType->AsBasicType())
- expr->type = QualType(getSession()->getErrorType());
else
- expr->type = QualType(getSession()->getErrorType());
- if (!baseType->Equals(getSession()->getErrorType()) &&
- expr->type->Equals(getSession()->getErrorType()))
{
- if (!isRewriteMode())
+ LookupResult lookupResult = lookUpMember(
+ getSession(),
+ this,
+ expr->name,
+ baseType.Ptr());
+ if (!lookupResult.isValid())
{
- getSink()->diagnose(expr, Diagnostics::typeHasNoPublicMemberOfName, baseType, expr->name);
+ return lookupResultFailure(expr, baseType);
}
+
+ // TODO: need to filter for declarations that are valid to refer
+ // to in this context...
+
+ return createLookupResultExpr(
+ lookupResult,
+ expr->BaseExpression,
+ expr->loc);
}
- return expr;
}
SemanticsVisitor & operator = (const SemanticsVisitor &) = delete;
@@ -5810,6 +6282,14 @@ namespace Slang
*outTypeResult = type;
return QualType(getTypeType(type));
}
+ else if (auto constraintDeclRef = declRef.As<GenericTypeConstraintDecl>())
+ {
+ // When we access a constraint or an inheritance decl (as a member),
+ // we are conceptually performing a "cast" to the given super-type,
+ // with the declaration showing that such a cast is legal.
+ auto type = GetSup(constraintDeclRef);
+ return QualType(type);
+ }
else if (auto funcDeclRef = declRef.As<CallableDecl>())
{
auto type = getFuncType(session, funcDeclRef);
@@ -5842,4 +6322,57 @@ namespace Slang
return semantics->ApplyExtensionToType(extDecl, type);
}
+ // Sometimes we need to refer to a declaration the way that it would be specialized
+ // inside the context where it is declared (e.g., with generic parameters filled in
+ // using their archetypes).
+ //
+ RefPtr<Substitutions> createDefaultSubstitutions(
+ Session* session,
+ Decl* decl,
+ Substitutions* parentSubst)
+ {
+ auto dd = decl->ParentDecl;
+ if( auto genericDecl = dynamic_cast<GenericDecl*>(dd) )
+ {
+ // We don't want to specialize references to anything
+ // other than the "inner" declaration itself.
+ if(decl != genericDecl->inner)
+ return parentSubst;
+
+ RefPtr<Substitutions> subst = new Substitutions();
+ subst->genericDecl = genericDecl;
+ subst->outer = parentSubst;
+
+ for( auto mm : genericDecl->Members )
+ {
+ if( auto genericTypeParamDecl = mm.As<GenericTypeParamDecl>() )
+ {
+ subst->args.Add(DeclRefType::Create(session, makeDeclRef(genericTypeParamDecl.Ptr())));
+ }
+ else if( auto genericValueParamDecl = mm.As<GenericValueParamDecl>() )
+ {
+ subst->args.Add(new GenericParamIntVal(makeDeclRef(genericValueParamDecl.Ptr())));
+ }
+ }
+
+ // TODO: need to fill in constraints here...
+
+ return subst;
+ }
+ return parentSubst;
+ }
+
+ RefPtr<Substitutions> createDefaultSubstitutions(
+ Session* session,
+ Decl* decl)
+ {
+ RefPtr<Substitutions> subst;
+ if( auto parentDecl = decl->ParentDecl )
+ {
+ subst = createDefaultSubstitutions(session, parentDecl);
+ }
+ subst = createDefaultSubstitutions(session, decl, subst);
+ return subst;
+ }
+
}
diff --git a/source/slang/decl-defs.h b/source/slang/decl-defs.h
index c9860f9bf..c96fe6d09 100644
--- a/source/slang/decl-defs.h
+++ b/source/slang/decl-defs.h
@@ -96,6 +96,15 @@ SIMPLE_SYNTAX_CLASS(InterfaceDecl, AggTypeDecl)
SYNTAX_CLASS(InheritanceDecl, Decl)
// The type expression as written
SYNTAX_FIELD(TypeExp, base)
+
+RAW(
+ // After checking, this dictionary will map members
+ // required by the base type to their concrete
+ // implementations in the type that contains
+ // this inheritance declaration.
+ Dictionary<DeclRef<Decl>, Decl*> requirementWitnesses;
+)
+
END_SYNTAX_CLASS()
// TODO: may eventually need sub-classes for explicit/direct vs. implicit/indirect inheritance
diff --git a/source/slang/diagnostic-defs.h b/source/slang/diagnostic-defs.h
index 492c0e084..36e68c853 100644
--- a/source/slang/diagnostic-defs.h
+++ b/source/slang/diagnostic-defs.h
@@ -316,6 +316,8 @@ DIAGNOSTIC(38001, Error, ambiguousEntryPoint, "more than one funct
DIAGNOSTIC(38002, Note, entryPointCandidate, "see candidate declaration for entry point '$0'")
DIAGNOSTIC(38003, Error, entryPointSymbolNotAFunction, "entry point '$0' must be declared as a function")
+DIAGNOSTIC(38100, Error, typeDoesntImplementInterfaceRequirement, "type '$0' does not provide required interface member '$1'")
+
//
// 4xxxx - IL code generation.
//
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index f76ab1db1..6b411a25d 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -5172,7 +5172,7 @@ emitDeclImpl(decl, nullptr);
// Start by emitting the non-terminator instructions in the block.
auto terminator = block->getLastInst();
assert(isTerminatorInst(terminator));
- for (auto inst = block->getFirstInst(); inst != terminator; inst = inst->nextInst)
+ for (auto inst = block->getFirstInst(); inst != terminator; inst = inst->getNextInst())
{
emitIRInst(context, inst);
}
@@ -5508,6 +5508,26 @@ emitDeclImpl(decl, nullptr);
EmitContext* context,
IRFunc* func)
{
+ // We don't want to declare generic functions,
+ // because none of our targets actually support them.
+ if(func->genericDecl)
+ return;
+
+ // We also don't want to emit declarations for operations
+ // that only appear in the IR as stand-ins for built-in
+ // operations on that target.
+ if (isTargetIntrinsic(context, func))
+ return;
+
+ // Finally, don't emit a declaration for an entry point,
+ // because it might need meta-data attributes attached
+ // to it, and the HLSL compiler will get upset if the
+ // forward declaration doesn't *also* have those
+ // attributes.
+ if(asEntryPoint(func))
+ return;
+
+
// A function declaration doesn't have any IR basic blocks,
// and as a result it *also* doesn't have the IR `param` instructions,
// so we need to emit a declaration entirely from the type.
@@ -5566,82 +5586,6 @@ emitDeclImpl(decl, nullptr);
return nullptr;
}
-#if 0
- void emitGLSLEntryPointFunc(
- EmitContext* context,
- IRFunc* func)
- {
- auto funcType = func->getType();
- auto resultType = func->getResultType();
-
- auto entryPointLayout = getEntryPointLayout(context, func);
- assert(entryPointLayout);
-
- // TODO: need to deal with decorations on the entry point
- // that should be turned into global-scope `layout` qualifiers.
-
- // TODO: emit kernel inputs and outputs to globals.
-
- // Emit a global `out` declaration to hold the output from our shader
- // kernel.
- //
- // TODO: need to generate unique names beter than this
- //
- // TODO: need to handle the case where the output is
- // a structure (should that be fixed up at the IR level,
- // or here?).
- // Best option might be to translate the entry-point
- // result parameter into an `out` parameter, so that
- // we can handle those uniformly.
- //
- String resultName = getIRName(func) + "_result";
- emitGLSLLayoutQualifiers(entryPointLayout->resultLayout);
- emit("out ");
- emitIRType(context, resultType, resultName);
- emit(";\n");
-
- // Emit global `in` and/or `out` declarations for the
- // parameters of our shader kernel.
- //
- // TODO: We need to make sure these names don't collide with anything.
- //
- // TODO: We need to handle scalarization here.
- //
- auto firstParam = func->getFirstParam();
- for( auto pp = firstParam; pp; pp = pp->getNextParam() )
- {
- // TODO: actually handle `out` parameters here.
-
- auto paramLayout = getVarLayout(context, pp);
- auto paramName = getIRName(pp);
- emitGLSLLayoutQualifiers(paramLayout);
- emit("in ");
- emitIRType(context, pp->getType(), paramName);
- emit(";\n");
- }
-
- // Now that we've emitted our parameter declarations,
- // we can start to emit the body of the entry point:
- //
- emit("void main()\n{\n");
-
- // We had better not be trying to output an entry
- // point from a declaration rather than a definition.
- assert(isDefinition(func));
-
- // At the most basic, we just want to emit the operations in
- // the entry point function directly, but with the small catch
- // that if there was a `return` statement in there somewhere,
- // we need to turn that into a write to our output variable.
- //
- // TODO: yeah, that should get cleared up at the IR level...
- //
- emitIRStmtsForBlocks(context, func->getFirstBlock(), nullptr);
-
- emit("}\n");
- }
-#endif
-
EntryPointLayout* asEntryPoint(IRFunc* func)
{
if (auto layoutDecoration = func->findDecoration<IRLayoutDecoration>())
@@ -5697,26 +5641,11 @@ emitDeclImpl(decl, nullptr);
EmitContext* context,
IRFunc* func)
{
-#if 0
- if( getTarget(context) == CodeGenTarget::GLSL
- && isEntryPoint(func) )
- {
- // We have a shader entry point, and that
- // requires a different strategy for source
- // code generation in GLSL, because the
- // parameters/result of the entry point
- // need to be translated into globals.
- //
-
- emitGLSLEntryPointFunc(context, func);
- }
- else
-#endif
if(func->genericDecl)
{
Emit("/* ");
emitIRFuncDecl(context, func);
- Emit(" */");
+ Emit(" */\n");
return;
}
@@ -6129,12 +6058,6 @@ emitDeclImpl(decl, nullptr);
emitIRGlobalVar(context, (IRGlobalVar*) inst);
break;
-#if 0
- case kIROp_StructType:
- emitIRStruct(context, (IRStructDecl*) inst);
- break;
-#endif
-
case kIROp_Var:
emitIRVar(context, (IRVar*) inst);
break;
@@ -6257,7 +6180,7 @@ emitDeclImpl(decl, nullptr);
emitIRUsedTypesForValue(context, pp);
}
- for( auto ii = bb->getFirstInst(); ii; ii = ii->nextInst )
+ for( auto ii = bb->getFirstInst(); ii; ii = ii->getNextInst() )
{
emitIRUsedTypesForValue(context, ii);
}
@@ -6289,6 +6212,20 @@ emitDeclImpl(decl, nullptr);
{
emitIRUsedTypesForModule(context, module);
+ // Before we emit code, we need to forward-declare
+ // all of our functions so that we don't have to
+ // sort them by dependencies.
+ for( auto gv = module->getFirstGlobalValue(); gv; gv = gv->getNextValue() )
+ {
+ if(gv->op != kIROp_Func)
+ continue;
+
+ auto func = (IRFunc*) gv;
+ emitIRFuncDecl(context, func);
+ }
+
+
+
for( auto gv = module->getFirstGlobalValue(); gv; gv = gv->getNextValue() )
{
emitIRGlobalInst(context, gv);
@@ -6454,9 +6391,12 @@ String emitEntryPoint(
specializeGenerics(lowered);
-// fprintf(stderr, "###\n");
-// dumpIR(lowered);
-// fprintf(stderr, "###\n");
+ // Debugging code for IR transformations...
+#if 0
+ fprintf(stderr, "###\n");
+ dumpIR(lowered);
+ fprintf(stderr, "###\n");
+#endif
//
// TODO: Need to decide whether to do these before or after
diff --git a/source/slang/ir-inst-defs.h b/source/slang/ir-inst-defs.h
index 636eeec16..d2e2c12e8 100644
--- a/source/slang/ir-inst-defs.h
+++ b/source/slang/ir-inst-defs.h
@@ -97,6 +97,7 @@ INST(FloatLit, float_constant, 0, 0)
INST(decl_ref, decl_ref, 0, 0)
INST(specialize, specialize, 2, 0)
+INST(lookup_interface_method, lookup_interface_method, 2, 0)
INST(Construct, construct, 0, 0)
INST(Call, call, 1, 0)
@@ -106,6 +107,8 @@ INST(Func, func, 0, PARENT)
INST(Block, block, 0, PARENT)
INST(global_var, global_var, 0, 0)
+INST(witness_table, witness_table, 0, 0)
+INST(witness_table_entry, witness_table_entry, 2, 0)
INST(Param, param, 0, 0)
INST(StructField, field, 0, 0)
diff --git a/source/slang/ir-insts.h b/source/slang/ir-insts.h
index c8e6ee52b..f7bcc2eb2 100644
--- a/source/slang/ir-insts.h
+++ b/source/slang/ir-insts.h
@@ -74,6 +74,16 @@ struct IRSpecialize : IRInst
IRUse specDeclRefVal;
};
+// An instruction that looks up the implementation
+// of an interface operation identified by `requirementDeclRef`
+// in the witness table `witnessTable` which should
+// hold the conformance information for a specific type.
+struct IRLookupWitnessMethod : IRInst
+{
+ IRUse witnessTable;
+ IRUse requirementDeclRef;
+};
+
//
struct IRCall : IRInst
@@ -251,6 +261,26 @@ struct IRGlobalVar : IRGlobalValue
PtrType* getType() { return type.As<PtrType>(); }
};
+// An entry in a witness table (see below)
+struct IRWitnessTableEntry : IRUser
+{
+ // The AST-level requirement
+ IRUse requirementKey;
+
+ // The IR-level value that satisfies the requirement
+ IRUse satisfyingVal;
+};
+
+// A witness table is a global value that stores
+// information about how a type conforms to some
+// interface. It basically takes the form of a
+// map from the required members of the interface
+// to the IR values that satisfy those requirements.
+struct IRWitnessTable : IRGlobalValue
+{
+ IRValueList<IRWitnessTableEntry> entries;
+};
+
// Description of an instruction to be used for global value numbering
@@ -330,6 +360,16 @@ struct IRBuilder
IRValue* genericVal,
DeclRef<Decl> specDeclRef);
+ IRValue* emitLookupInterfaceMethodInst(
+ IRType* type,
+ IRValue* witnessTableVal,
+ IRValue* interfaceMethodVal);
+
+ IRValue* emitLookupInterfaceMethodInst(
+ IRType* type,
+ DeclRef<Decl> witnessTableDeclRef,
+ DeclRef<Decl> interfaceMethodDeclRef);
+
IRInst* emitCallInst(
IRType* type,
IRValue* func,
@@ -352,6 +392,11 @@ struct IRBuilder
IRFunc* createFunc();
IRGlobalVar* createGlobalVar(
IRType* valueType);
+ IRWitnessTable* createWitnessTable();
+ IRWitnessTableEntry* createWitnessTableEntry(
+ IRWitnessTable* witnessTable,
+ IRValue* requirementKey,
+ IRValue* satisfyingVal);
IRBlock* createBlock();
IRBlock* emitBlock();
diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp
index 14639de7e..5cc73a36b 100644
--- a/source/slang/ir.cpp
+++ b/source/slang/ir.cpp
@@ -41,7 +41,7 @@ namespace Slang
//
- void IRUse::init(IRInst* u, IRValue* v)
+ void IRUse::init(IRUser* u, IRValue* v)
{
user = u;
usedValue = v;
@@ -57,7 +57,7 @@ namespace Slang
//
- IRUse* IRInst::getArgs()
+ IRUse* IRUser::getArgs()
{
// We assume that *all* instructions are laid out
// in memory such that their arguments come right
@@ -154,18 +154,38 @@ namespace Slang
return isTerminatorInst(inst->op);
}
+ //
+
+ void IRValueListBase::addImpl(IRValue* parent, IRChildValue* val)
+ {
+ val->parent = parent;
+ val->prev = last;
+ val->next = nullptr;
+
+ if (last)
+ {
+ last->next = val;
+ }
+ else
+ {
+ first = val;
+ }
+
+ last = val;
+ }
+
//
// Add an instruction to a specific parent
void IRBuilder::addInst(IRBlock* block, IRInst* inst)
{
- inst->parentBlock = block;
+ inst->parent = block;
if (!block->firstInst)
{
- inst->prevInst = nullptr;
- inst->nextInst = nullptr;
+ inst->prev = nullptr;
+ inst->next = nullptr;
block->firstInst = inst;
block->lastInst = inst;
@@ -174,10 +194,10 @@ namespace Slang
{
auto prev = block->lastInst;
- inst->prevInst = prev;
- inst->nextInst = nullptr;
+ inst->prev = prev;
+ inst->next = nullptr;
- prev->nextInst = inst;
+ prev->next = inst;
block->lastInst = inst;
}
}
@@ -402,7 +422,7 @@ namespace Slang
bool operator==(IRInstKey const& left, IRInstKey const& right)
{
if(left.inst->op != right.inst->op) return false;
- if(left.inst->parentBlock != right.inst->parentBlock) return false;
+ if(left.inst->parent != right.inst->parent) return false;
if(left.inst->argCount != right.inst->argCount) return false;
auto argCount = left.inst->argCount;
@@ -420,7 +440,7 @@ namespace Slang
int IRInstKey::GetHashCode()
{
auto code = Slang::GetHashCode(inst->op);
- code = combineHash(code, Slang::GetHashCode(inst->parentBlock));
+ code = combineHash(code, Slang::GetHashCode(inst->parent));
code = combineHash(code, Slang::GetHashCode(inst->argCount));
auto argCount = inst->argCount;
@@ -487,7 +507,7 @@ namespace Slang
// way: we will construct a temporary instruction and
// then use it to look up in a cache of instructions.
- irValue = createInst<IRConstant>(builder, op, type);
+ irValue = createValue<IRConstant>(builder, op, type);
memcpy(&irValue->u, value, valueSize);
key.inst = irValue;
@@ -534,7 +554,7 @@ namespace Slang
DeclRefBase const& declRef)
{
// TODO: we should cache these...
- auto irValue = createInst<IRDeclRef>(
+ auto irValue = createValue<IRDeclRef>(
this,
kIROp_decl_ref,
nullptr);
@@ -573,6 +593,33 @@ namespace Slang
return inst;
}
+ IRValue* IRBuilder::emitLookupInterfaceMethodInst(
+ IRType* type,
+ IRValue* witnessTableVal,
+ IRValue* interfaceMethodVal)
+ {
+ auto inst = createInst<IRLookupWitnessMethod>(
+ this,
+ kIROp_lookup_interface_method,
+ type,
+ witnessTableVal,
+ interfaceMethodVal);
+ addInst(inst);
+ return inst;
+ }
+
+ IRValue* IRBuilder::emitLookupInterfaceMethodInst(
+ IRType* type,
+ DeclRef<Decl> witnessTableDeclRef,
+ DeclRef<Decl> interfaceMethodDeclRef)
+ {
+ auto witnessTableVal = getDeclRefVal(witnessTableDeclRef);
+ auto interfaceMethodVal = getDeclRefVal(interfaceMethodDeclRef);
+ return emitLookupInterfaceMethodInst(type, witnessTableVal, interfaceMethodVal);
+ }
+
+
+
IRInst* IRBuilder::emitCallInst(
IRType* type,
IRValue* func,
@@ -792,6 +839,37 @@ namespace Slang
return globalVar;
}
+ IRWitnessTable* IRBuilder::createWitnessTable()
+ {
+ IRWitnessTable* witnessTable = createValue<IRWitnessTable>(
+ this,
+ kIROp_witness_table,
+ nullptr);
+ addGlobalValue(getModule(), witnessTable);
+ return witnessTable;
+ }
+
+ IRWitnessTableEntry* IRBuilder::createWitnessTableEntry(
+ IRWitnessTable* witnessTable,
+ IRValue* requirementKey,
+ IRValue* satisfyingVal)
+ {
+ IRWitnessTableEntry* entry = createInst<IRWitnessTableEntry>(
+ this,
+ kIROp_witness_table_entry,
+ nullptr,
+ requirementKey,
+ satisfyingVal);
+
+ if (witnessTable)
+ {
+ witnessTable->entries.add(witnessTable, entry);
+ }
+
+ return entry;
+ }
+
+
IRBlock* IRBuilder::createBlock()
{
return createValue<IRBlock>(
@@ -1284,6 +1362,8 @@ namespace Slang
switch(inst->op)
{
case kIROp_Func:
+ case kIROp_global_var:
+ case kIROp_witness_table:
{
auto irFunc = (IRFunc*) inst;
dump(context, "@");
@@ -1312,6 +1392,10 @@ namespace Slang
IRDumpContext* context,
IRType* type);
+ static void dumpDeclRef(
+ IRDumpContext* context,
+ DeclRef<Decl> const& declRef);
+
static void dumpOperand(
IRDumpContext* context,
IRValue* inst)
@@ -1341,6 +1425,12 @@ namespace Slang
dumpType(context, (IRType*)inst);
return;
+ case kIROp_decl_ref:
+ dump(context, "$\"");
+ dumpDeclRef(context, ((IRDeclRef*)inst)->declRef);
+ dump(context, "\"");
+ return;
+
default:
break;
}
@@ -1355,11 +1445,6 @@ namespace Slang
dump(context, getText(name).Buffer());
}
-
- static void dumpDeclRef(
- IRDumpContext* context,
- DeclRef<Decl> const& declRef);
-
static void dumpVal(
IRDumpContext* context,
Val* val)
@@ -1376,6 +1461,20 @@ namespace Slang
{
dumpDeclRef(context, genericParamVal->declRef);
}
+ else if(auto declaredSubtypeWitness = dynamic_cast<DeclaredSubtypeWitness*>(val))
+ {
+ dump(context, "DeclaredSubtypeWitness(");
+ dumpType(context, declaredSubtypeWitness->sub);
+ dump(context, ", ");
+ dumpType(context, declaredSubtypeWitness->sup);
+ dump(context, ", ");
+ dumpDeclRef(context, declaredSubtypeWitness->declRef);
+ dump(context, ")");
+ }
+ else if (auto proxyVal = dynamic_cast<IRProxyVal*>(val))
+ {
+ dumpOperand(context, proxyVal->inst);
+ }
else
{
dump(context, "???");
@@ -1417,6 +1516,20 @@ namespace Slang
dump(context, ".");
}
dump(context, decl->getName());
+ if (auto genericTypeConstraintDecl = dynamic_cast<GenericTypeConstraintDecl*>(decl))
+ {
+ dump(context, "{");
+ dumpType(context, genericTypeConstraintDecl->sub);
+ dump(context, " : ");
+ dumpType(context, genericTypeConstraintDecl->sup);
+ dump(context, "}");
+ }
+ else if (auto inheritanceDecl = dynamic_cast<InheritanceDecl*>(decl))
+ {
+ dump(context, "{ _ : ");
+ dumpType(context, inheritanceDecl->base);
+ dump(context, "}");
+ }
if(genericParentDeclRef)
{
@@ -1538,7 +1651,7 @@ namespace Slang
IRDumpContext* context,
IRBlock* block)
{
- for (auto ii = block->firstInst; ii; ii = ii->nextInst)
+ for (auto ii = block->firstInst; ii; ii = ii->getNextInst())
{
dumpInst(context, ii);
}
@@ -1775,15 +1888,16 @@ namespace Slang
bool first = true;
for (auto mm : genericDecl->Members)
{
- if (!first) dump(context, ", ");
if( auto typeParamDecl = mm.As<GenericTypeParamDecl>() )
{
+ if (!first) dump(context, ", ");
dumpDeclRef(context, makeDeclRef(typeParamDecl.Ptr()));
first = false;
}
else if( auto valueParamDecl = mm.As<GenericTypeParamDecl>() )
{
+ if (!first) dump(context, ", ");
dumpDeclRef(context, makeDeclRef(valueParamDecl.Ptr()));
first = false;
}
@@ -1791,11 +1905,11 @@ namespace Slang
first = true;
for (auto mm : genericDecl->Members)
{
- if (!first) dump(context, ", ");
- else dump(context, " where ");
-
if( auto constraintDecl = mm.As<GenericTypeConstraintDecl>() )
{
+ if (!first) dump(context, ", ");
+ else dump(context, " where ");
+
dumpType(context, constraintDecl->sub);
dump(context, " : ");
dumpType(context, constraintDecl->sup);
@@ -1882,6 +1996,37 @@ namespace Slang
dump(context, ";\n");
}
+ void dumpIRWitnessTableEntry(
+ IRDumpContext* context,
+ IRWitnessTableEntry* entry)
+ {
+ dump(context, "witness_table_entry(");
+ dumpOperand(context, entry->requirementKey.usedValue);
+ dump(context, ",");
+ dumpOperand(context, entry->satisfyingVal.usedValue);
+ dump(context, ")\n");
+ }
+
+ void dumpIRWitnessTable(
+ IRDumpContext* context,
+ IRWitnessTable* witnessTable)
+ {
+ dump(context, "\n");
+ dumpIndent(context);
+ dump(context, "ir_witness_table ");
+ dumpID(context, witnessTable);
+ dump(context, "\n{\n");
+ context->indent++;
+
+ for (auto entry : witnessTable->entries)
+ {
+ dumpIRWitnessTableEntry(context, entry);
+ }
+
+ context->indent--;
+ dump(context, "}\n");
+ }
+
void dumpIRGlobalValue(
IRDumpContext* context,
IRGlobalValue* value)
@@ -1896,6 +2041,10 @@ namespace Slang
dumpIRGlobalVar(context, (IRGlobalVar*)value);
break;
+ case kIROp_witness_table:
+ dumpIRWitnessTable(context, (IRWitnessTable*)value);
+ break;
+
default:
dump(context, "???\n");
break;
@@ -2000,11 +2149,17 @@ namespace Slang
void IRValue::deallocate()
{
+#if 0
+ // I'm going to intentionally leak here,
+ // just to test that this is the source
+ // of my heap-corruption crashes.
+#else
// Run destructor to be sure...
this->~IRValue();
// And then free the memory
free((void*) this);
+#endif
}
// Insert this instruction into the same basic block
@@ -2014,24 +2169,24 @@ namespace Slang
// Make sure this instruction has been removed from any previous parent
this->removeFromParent();
- auto bb = other->parentBlock;
+ auto bb = other->getParentBlock();
assert(bb);
- auto pp = other->prevInst;
+ auto pp = other->getPrevInst();
if( pp )
{
- pp->nextInst = this;
+ pp->next = this;
}
else
{
bb->firstInst = this;
}
- this->prevInst = pp;
- this->nextInst = other;
- this->parentBlock = bb;
+ this->prev = pp;
+ this->next = other;
+ this->parent = bb;
- other->prevInst = this;
+ other->prev = this;
}
// Remove this instruction from its parent block,
@@ -2040,17 +2195,17 @@ namespace Slang
{
// If we don't currently have a parent, then
// we are doing fine.
- if(!parentBlock)
+ if(!getParentBlock())
return;
- auto bb = parentBlock;
- auto pp = prevInst;
- auto nn = nextInst;
+ auto bb = getParentBlock();
+ auto pp = getPrevInst();
+ auto nn = getNextInst();
if(pp)
{
- SLANG_ASSERT(pp->parentBlock == bb);
- pp->nextInst = nn;
+ SLANG_ASSERT(pp->getParentBlock() == bb);
+ pp->next = nn;
}
else
{
@@ -2059,17 +2214,17 @@ namespace Slang
if(nn)
{
- SLANG_ASSERT(nn->parentBlock == bb);
- nn->prevInst = pp;
+ SLANG_ASSERT(nn->getParentBlock() == bb);
+ nn->prev = pp;
}
else
{
bb->lastInst = pp;
}
- prevInst = nullptr;
- nextInst = nullptr;
- parentBlock = nullptr;
+ prev = nullptr;
+ next = nullptr;
+ parent = nullptr;
}
void IRInst::removeArguments()
@@ -2552,7 +2707,7 @@ namespace Slang
// TODO: This is silly, because we are looking at every instruction,
// when we know that a `returnVal` should only ever appear as a
// terminator...
- for( auto ii = bb->getFirstInst(); ii; ii = ii->nextInst )
+ for( auto ii = bb->getFirstInst(); ii; ii = ii->getNextInst() )
{
if(ii->op != kIROp_ReturnVal)
continue;
@@ -2720,7 +2875,7 @@ namespace Slang
// Finally, we need to patch up the type of the entry point,
// because it is no longer accurate.
- auto voidFuncType = new FuncType();
+ RefPtr<FuncType> voidFuncType = new FuncType();
voidFuncType->setSession(session);
voidFuncType->resultType = session->getVoidType();
func->type = voidFuncType;
@@ -2797,6 +2952,12 @@ namespace Slang
{
return originalType;
}
+
+ // A callback used to clone (or not) a declaration reference
+ virtual DeclRef<Decl> maybeCloneDeclRef(DeclRef<Decl> const& declRef)
+ {
+ return declRef;
+ }
};
void registerClonedValue(
@@ -2844,11 +3005,16 @@ namespace Slang
// Override the "maybe clone" logic so that we always clone
virtual IRValue* maybeCloneValue(IRValue* originalVal) override;
+
+ // Override teh "maybe clone" logic so that we carefully
+ // clone any IR proxy values inside substitutions
+ virtual DeclRef<Decl> maybeCloneDeclRef(DeclRef<Decl> const& declRef) override;
};
IRGlobalVar* cloneGlobalVar(IRSpecContext* context, IRGlobalVar* originalVar);
IRFunc* cloneFunc(IRSpecContext* context, IRFunc* originalFunc);
+ IRWitnessTable* cloneWitnessTable(IRSpecContext* context, IRWitnessTable* originalVar);
IRValue* IRSpecContext::maybeCloneValue(IRValue* originalValue)
{
@@ -2862,6 +3028,10 @@ namespace Slang
return cloneFunc(this, (IRFunc*)originalValue);
break;
+ case kIROp_witness_table:
+ return cloneWitnessTable(this, (IRWitnessTable*)originalValue);
+ break;
+
case kIROp_boolConst:
{
IRConstant* c = (IRConstant*)originalValue;
@@ -2887,7 +3057,8 @@ namespace Slang
case kIROp_decl_ref:
{
IRDeclRef* od = (IRDeclRef*)originalValue;
- return builder->getDeclRefVal(od->declRef);
+ auto declRef = maybeCloneDeclRef(od->declRef);
+ return builder->getDeclRefVal(declRef);
}
break;
@@ -2897,6 +3068,66 @@ namespace Slang
}
}
+ RefPtr<Val> cloneSubstitutionArg(
+ IRSpecContext* context,
+ Val* val)
+ {
+ if (auto proxyVal = dynamic_cast<IRProxyVal*>(val))
+ {
+ auto newIRVal = context->maybeCloneValue(proxyVal->inst);
+
+ RefPtr<IRProxyVal> newProxyVal = new IRProxyVal();
+ newProxyVal->inst = newIRVal;
+ return newProxyVal;
+ }
+ else if (auto type = dynamic_cast<Type*>(val))
+ {
+ return context->maybeCloneType(type);
+ }
+ else
+ {
+ return val;
+ }
+ }
+
+ RefPtr<Substitutions> cloneSubstitutions(
+ IRSpecContext* context,
+ Substitutions* subst)
+ {
+ if (!subst)
+ return nullptr;
+
+ RefPtr<Substitutions> newSubst = new Substitutions();
+ newSubst->outer = cloneSubstitutions(context, subst->outer);
+ newSubst->genericDecl = subst->genericDecl;
+
+ for (auto arg : subst->args)
+ {
+ auto newArg = cloneSubstitutionArg(context, arg);
+ newSubst->args.Add(arg);
+ }
+
+ return newSubst;
+ }
+
+ DeclRef<Decl> IRSpecContext::maybeCloneDeclRef(DeclRef<Decl> const& declRef)
+ {
+ // Un-specialized decl? Nothing to do.
+ if (!declRef.substitutions)
+ return declRef;
+
+ DeclRef<Decl> newDeclRef = declRef;
+
+ // Scan through substitutions and clone as needed.
+ //
+ // TODO: this is wasteful since we clone *everything*
+ newDeclRef.substitutions = cloneSubstitutions(this, declRef.substitutions);
+
+ return newDeclRef;
+
+ }
+
+
IRValue* cloneValue(
IRSpecContextBase* context,
IRValue* originalValue)
@@ -2970,6 +3201,30 @@ namespace Slang
return clonedVar;
}
+ IRWitnessTable* cloneWitnessTable(IRSpecContext* context, IRWitnessTable* originalTable)
+ {
+ auto clonedTable = context->builder->createWitnessTable();
+ registerClonedValue(context, clonedTable, originalTable);
+
+ auto mangledName = originalTable->mangledName;
+ clonedTable->mangledName = mangledName;
+
+ cloneDecorations(context, clonedTable, originalTable);
+
+ // Clone the entries in the witness table as well
+ for( auto originalEntry : originalTable->entries )
+ {
+ auto clonedKey = context->maybeCloneValue(originalEntry->requirementKey.usedValue);
+ auto clonedVal = context->maybeCloneValue(originalEntry->satisfyingVal.usedValue);
+ auto clonedEntry = context->builder->createWitnessTableEntry(
+ clonedTable,
+ clonedKey,
+ clonedVal);
+ }
+
+ return clonedTable;
+ }
+
void cloneFunctionCommon(
IRSpecContextBase* context,
IRFunc* clonedFunc,
@@ -3023,7 +3278,7 @@ namespace Slang
assert(cb);
builder->block = cb;
- for (auto oi = ob->getFirstInst(); oi; oi = oi->nextInst)
+ for (auto oi = ob->getFirstInst(); oi; oi = oi->getNextInst())
{
cloneInst(context, builder, oi);
}
@@ -3444,6 +3699,90 @@ namespace Slang
virtual RefPtr<Type> maybeCloneType(Type* originalType) override;
};
+ // Convert a type-level value into an IR-level equivalent.
+ IRValue* getIRValue(
+ IRGenericSpecContext* context,
+ Val* val)
+ {
+ if( auto subtypeWitness = dynamic_cast<SubtypeWitness*>(val) )
+ {
+ // We need to look up the IR value that represents the
+ // given subtype witness.
+ String mangledName = getMangledNameForConformanceWitness(
+ subtypeWitness->sub,
+ subtypeWitness->sup);
+ RefPtr<IRSpecSymbol> symbol;
+
+ if( !context->getSymbols().TryGetValue(mangledName, symbol) )
+ {
+ SLANG_UNEXPECTED("couldn't find symbol for conformance!");
+ return nullptr;
+ }
+
+ return symbol->irGlobalValue;
+ }
+ else if (auto proxyVal = dynamic_cast<IRProxyVal*>(val))
+ {
+ // The type-level value actually references an IR-level value,
+ // so we need to make sure to emit as if we were referencing
+ // the pointed-to value and not the proxy type-level `Val`
+ // instead.
+
+ return context->maybeCloneValue(proxyVal->inst);
+ }
+ else
+ {
+ SLANG_UNEXPECTED("unimplemented");
+ return nullptr;
+ }
+ }
+
+ IRValue* getSubstValue(
+ IRGenericSpecContext* context,
+ DeclRef<Decl> declRef)
+ {
+ auto subst = context->subst;
+ auto genericDecl = subst->genericDecl;
+
+ UInt orinaryParamCount = 0;
+ for( auto mm : genericDecl->Members )
+ {
+ if(mm.As<GenericTypeParamDecl>())
+ orinaryParamCount++;
+ else if(mm.As<GenericValueParamDecl>())
+ orinaryParamCount++;
+ }
+
+ if( auto constraintDeclRef = declRef.As<GenericTypeConstraintDecl>() )
+ {
+ // We have a constraint, but we need to find its index in the
+ // argument list of the substitutions.
+ UInt constraintIndex = 0;
+ bool found = false;
+ for( auto cd : genericDecl->getMembersOfType<GenericTypeConstraintDecl>() )
+ {
+ if( cd.Ptr() == constraintDeclRef.getDecl() )
+ {
+ found = true;
+ break;
+ }
+
+ constraintIndex++;
+ }
+ assert(found);
+
+ UInt argIndex = orinaryParamCount + constraintIndex;
+ assert(argIndex < subst->args.Count());
+
+ return getIRValue(context, subst->args[argIndex]);
+ }
+ else
+ {
+ SLANG_UNEXPECTED("unhandled case");
+ return nullptr;
+ }
+ }
+
IRValue* IRGenericSpecContext::maybeCloneValue(IRValue* originalVal)
{
switch( originalVal->op )
@@ -3451,6 +3790,17 @@ namespace Slang
case kIROp_decl_ref:
{
auto declRefVal = (IRDeclRef*) originalVal;
+ auto declRef = declRefVal->declRef;
+
+ // We may have a direct reference to one of the parameters
+ // of the generic we are specializing, and in that case
+ // we nee to translate it over to the equiavalent of
+ // the `Val` we have been given.
+ if(declRef.getDecl()->ParentDecl == subst->genericDecl)
+ {
+ return getSubstValue(this, declRef);
+ }
+
int diff = 0;
auto substDeclRef = declRefVal->declRef.SubstituteImpl(subst, &diff);
if(!diff)
@@ -3539,6 +3889,41 @@ namespace Slang
return specFunc;
}
+ // Find the value in the given witness table that
+ // satisfies the given requirement (or return
+ // null if not found).
+ IRValue* findWitnessVal(
+ IRWitnessTable* witnessTable,
+ DeclRef<Decl> const& requirementDeclRef)
+ {
+ // For now we will do a dumb linear search
+ for( auto entry : witnessTable->entries )
+ {
+ // We expect the key on the entry to be a decl-ref,
+ // but lets go ahead and check, just to be sure.
+ auto requirementKey = entry->requirementKey.usedValue;
+ if(requirementKey->op != kIROp_decl_ref)
+ continue;
+ auto keyDeclRef = ((IRDeclRef*) requirementKey)->declRef;
+
+ // If the keys don't match, continue with the next entry.
+ if(!keyDeclRef.Equals(requirementDeclRef))
+ continue;
+
+ // If the keys matched, then we use the value from
+ // this entry.
+ auto satisfyingVal = entry->satisfyingVal.usedValue;
+ return satisfyingVal;
+ }
+
+ // No matching entry found.
+ return nullptr;
+ }
+
+ // Go through the code in the module and try to identify
+ // calls to generic functions where the generic arguments
+ // are known, and specialize the callee based on those
+ // known values.
void specializeGenerics(
IRModule* module)
{
@@ -3601,40 +3986,100 @@ namespace Slang
IRInst* nextInst = nullptr;
for( auto ii = bb->getFirstInst(); ii; ii = nextInst )
{
- nextInst = ii->nextInst;
+ nextInst = ii->getNextInst();
- // We only care about `specialize` instructions.
- if(ii->op != kIROp_specialize)
+ // We want to handle both `specialize` instructions,
+ // which trigger specialization, and also `lookup_interface_method`
+ // instructions, which may allow us to "de-virtualize"
+ // calls.
+
+ switch( ii->op )
+ {
+ default:
+ // Most instructions are ones we don't care about here.
continue;
- IRSpecialize* specInst = (IRSpecialize*) ii;
+ case kIROp_specialize:
+ {
+ // We have a `specialize` instruction, so lets see
+ // whether we have an opportunity to perform the
+ // specialization here and now.
+ IRSpecialize* specInst = (IRSpecialize*) ii;
+
+ // We need to check that the value being specialized is
+ // a generic function.
+ auto genericVal = specInst->genericVal.usedValue;
+ if(genericVal->op != kIROp_Func)
+ continue;
+ auto genericFunc = (IRFunc*) genericVal;
+ if(!genericFunc->genericDecl)
+ continue;
+
+ // Now we extract the specialized decl-ref that will
+ // tell us how to specialize things.
+ auto specDeclRefVal = (IRDeclRef*) specInst->specDeclRefVal.usedValue;
+ auto specDeclRef = specDeclRefVal->declRef;
+
+ // Okay, we have a candidate for specialization here.
+ //
+ // We will first find or construct a specialized version
+ // of the callee funciton/
+ auto specFunc = getSpecializedFunc(sharedContext, genericFunc, specDeclRef);
+ //
+ // Then we will replace the use sites for the `specialize`
+ // instruction with uses of the specialized function.
+ //
+ specInst->replaceUsesWith(specFunc);
+
+ specInst->removeAndDeallocate();
+ }
+ break;
- // We need to check that the value being specialized is
- // a generic function.
- auto genericVal = specInst->genericVal.usedValue;
- if(genericVal->op != kIROp_Func)
- continue;
- auto genericFunc = (IRFunc*) genericVal;
- if(!genericFunc->genericDecl)
+ case kIROp_lookup_interface_method:
+ {
+ // We have a `lookup_interface_method` instruction,
+ // so let's see whether it is a lookup in a known
+ // witness table.
+ IRLookupWitnessMethod* lookupInst = (IRLookupWitnessMethod*) ii;
+
+ // We only want to deal with the case where the witness-table
+ // argument points to a concrete global table.
+ auto witnessTableArg = lookupInst->witnessTable.usedValue;
+ if(witnessTableArg->op != kIROp_witness_table)
+ continue;
+ IRWitnessTable* witnessTable = (IRWitnessTable*)witnessTableArg;
+
+ // We also need to be sure that the requirement we
+ // are trying to look up is identified via a decl-ref:
+ auto requirementArg = lookupInst->requirementDeclRef.usedValue;
+ if(requirementArg->op != kIROp_decl_ref)
+ continue;
+ auto requirementDeclRef = ((IRDeclRef*) requirementArg)->declRef;
+
+ // Use the witness table to look up the value that
+ // satisfies the requirement.
+ auto satisfyingVal = findWitnessVal(witnessTable, requirementDeclRef);
+
+ // We expect to always find something, but lets just
+ // be careful here.
+ if(!satisfyingVal)
+ continue;
+
+ // If we get through all of the above checks, then we
+ // have a (more) concrete method that implements the interface,
+ // and so we should dispatch to that directly, rather than
+ // use the `lookup_interface_method` instruction.
+ lookupInst->replaceUsesWith(satisfyingVal);
+ lookupInst->removeAndDeallocate();
+ }
+ break;
+ }
+
+
+ // We only care about `specialize` instructions.
+ if(ii->op != kIROp_specialize)
continue;
- // Now we extract the specialized decl-ref that will
- // tell us how to specialize things.
- auto specDeclRefVal = (IRDeclRef*) specInst->specDeclRefVal.usedValue;
- auto specDeclRef = specDeclRefVal->declRef;
-
- // Okay, we have a candidate for specialization here.
- //
- // We will first find or construct a specialized version
- // of the callee funciton/
- auto specFunc = getSpecializedFunc(sharedContext, genericFunc, specDeclRef);
- //
- // Then we will replace the use sites for the `specialize`
- // instruction with uses of the specialized function.
- //
- specInst->replaceUsesWith(specFunc);
-
- specInst->removeAndDeallocate();
}
}
}
diff --git a/source/slang/ir.h b/source/slang/ir.h
index 2477c987f..2e15e39d6 100644
--- a/source/slang/ir.h
+++ b/source/slang/ir.h
@@ -21,6 +21,7 @@ class Session;
struct IRFunc;
struct IRInst;
struct IRModule;
+struct IRUser;
struct IRValue;
typedef unsigned int IROpFlags;
@@ -82,7 +83,7 @@ struct IRUse
IRValue* usedValue;
// The value that is doing the using.
- IRInst* user;
+ IRUser* user;
// The next use of the same value
IRUse* nextUse;
@@ -91,7 +92,7 @@ struct IRUse
// so that we can simplify updates.
IRUse** prevLink;
- void init(IRInst* user, IRValue* usedValue);
+ void init(IRUser* user, IRValue* usedValue);
};
enum IRDecorationOp : uint16_t
@@ -156,9 +157,83 @@ struct IRValue
void deallocate();
};
-// Instructions are values that can be executed,
-// and which take other values as operands
-struct IRInst : IRValue
+// Values that are contained in a doubly-linked
+// list inside of some parent.
+//
+// TODO: consider merging this into `IRValue` so
+// that *all* values have a parent.
+struct IRChildValue : IRValue
+{
+ // The parent of this value.
+ IRValue* parent;
+
+ // The next and previous values in the same
+ // list on teh same parent.
+ IRChildValue* next;
+ IRChildValue* prev;
+};
+
+// Helper for storing linked lists of child values.
+struct IRValueListBase
+{
+ IRChildValue* first = 0;
+ IRChildValue* last = 0;
+
+protected:
+ void addImpl(IRValue* parent, IRChildValue* val);
+};
+template<typename T>
+struct IRValueList : IRValueListBase
+{
+ T* getFirst() { return (T*)first; }
+ T* getLast() { return (T*)last; }
+
+ void add(IRValue* parent, T* val)
+ {
+ addImpl(parent, val);
+ }
+
+ struct Iterator
+ {
+ T* val;
+
+ Iterator() : val(0) {}
+ Iterator(T* val) : val(val) {}
+
+ void operator++()
+ {
+ if (val)
+ {
+ val = (T*)val->next;
+ }
+ }
+
+ T* operator*()
+ {
+ return val;
+ }
+
+ bool operator!=(Iterator const& i)
+ {
+ return val != i.val;
+ }
+ };
+
+ Iterator begin() { return Iterator(getFirst()); }
+ Iterator end() { return Iterator(nullptr); }
+};
+
+// Values that can use other values. These always
+// have their operands "tail allocated" after
+// the fields of this type, so derived types must
+// either:
+//
+// - Add no new fields, or
+// - Add only fields that represent the `IRUse` operands
+// - Add a fixed number of `IRUse` operand fields and
+// then any additional data after them.
+//
+struct IRUser : IRChildValue
{
// The total number of arguments of this instruction.
//
@@ -169,13 +244,6 @@ struct IRInst : IRValue
// pointer.
uint32_t argCount;
- // The basic block that contains this instruction.
- IRBlock* parentBlock;
-
- // The next and previous instructions in the same parent block
- IRInst* nextInst;
- IRInst* prevInst;
-
UInt getArgCount()
{
return argCount;
@@ -187,6 +255,17 @@ struct IRInst : IRValue
{
return getArgs()[index].usedValue;
}
+};
+
+// Instructions are values that are children of a basic block,
+// and can actually be executed.
+struct IRInst : IRUser
+{
+ IRBlock* getParentBlock() { return (IRBlock*)parent; }
+
+ IRInst* getPrevInst() { return (IRInst*)prev; }
+ IRInst* getNextInst() { return (IRInst*)next; }
+
// Insert this instruction into the same basic block
// as `other`, right before it.
diff --git a/source/slang/lookup.cpp b/source/slang/lookup.cpp
index 95a1b3326..cf51ae720 100644
--- a/source/slang/lookup.cpp
+++ b/source/slang/lookup.cpp
@@ -323,6 +323,11 @@ void DoLookupImpl(
// at the parameters themselves, so provide a fully-resolved
// declaration reference for lookup.
RefPtr<Substitutions> subst = nullptr;
+#if 1
+ // Actually, the above rationale seems bogus. If we are looking
+ // up from "inside" a generic declaration, we don't want to
+ // get its members pre-specialized, right?
+#else
if(auto parentGenericDecl = dynamic_cast<GenericDecl*>(containerDecl->ParentDecl))
{
subst = new Substitutions();
@@ -342,6 +347,7 @@ void DoLookupImpl(
}
}
}
+#endif
DeclRef<ContainerDecl> containerRef = DeclRef<Decl>(containerDecl, subst).As<ContainerDecl>();
DoLocalLookupImpl(
@@ -370,7 +376,7 @@ LookupResult DoLookup(
return result;
}
-LookupResult LookUp(
+LookupResult lookUp(
Session* session,
SemanticsVisitor* semantics,
Name* name,
@@ -384,7 +390,7 @@ LookupResult LookUp(
// perform lookup within the context of a particular container declaration,
// and do *not* look further up the chain
-LookupResult LookUpLocal(
+LookupResult lookUpLocal(
Session* session,
SemanticsVisitor* semantics,
Name* name,
@@ -398,4 +404,72 @@ LookupResult LookUpLocal(
return result;
}
+void lookUpMemberImpl(
+ Session* session,
+ SemanticsVisitor* semantics,
+ Name* name,
+ Type* type,
+ LookupResult& ioResult,
+ BreadcrumbInfo* inBreadcrumbs)
+{
+ if (auto declRefType = type->As<DeclRefType>())
+ {
+ auto declRef = declRefType->declRef;
+ if (auto aggTypeDeclRef = declRef.As<AggTypeDecl>())
+ {
+ LookupRequest request;
+ request.semantics = semantics;
+
+ DoLocalLookupImpl(session, name, aggTypeDeclRef, request, ioResult, inBreadcrumbs);
+ }
+ else if (auto genericTypeParamDeclRef = declRef.As<GenericTypeParamDecl>())
+ {
+ auto genericDeclRef = genericTypeParamDeclRef.GetParent().As<GenericDecl>();
+ assert(genericDeclRef);
+
+ for(auto constraintDeclRef : getMembersOfType<GenericTypeConstraintDecl>(genericDeclRef))
+ {
+ // Does this constraint pertain to the type we are working on?
+ //
+ // We want constraints of the form `T : Foo` where `T` is the
+ // generic parameter in question, and `Foo` is whatever we are
+ // constraining it to.
+ auto subType = GetSub(constraintDeclRef);
+ auto subDeclRefType = subType->As<DeclRefType>();
+ if(!subDeclRefType)
+ continue;
+ if(!subDeclRefType->declRef.Equals(genericTypeParamDeclRef))
+ continue;
+
+ // The super-type in the constraint (e.g., `Foo` in `T : Foo`)
+ // will tell us a type we should use for lookup.
+ auto bound = GetSup(constraintDeclRef);
+
+ // Go ahead and use the target type, with an appropriate breadcrumb
+ // to indicate that we indirected through a type constraint.
+
+ BreadcrumbInfo breadcrumb;
+ breadcrumb.prev = inBreadcrumbs;
+ breadcrumb.kind = LookupResultItem::Breadcrumb::Kind::Constraint;
+ breadcrumb.declRef = constraintDeclRef;
+
+ // TODO: Need to consider case where this might recurse infinitely.
+ lookUpMemberImpl(session, semantics, name, bound, ioResult, &breadcrumb);
+ }
+ }
+ }
+}
+
+LookupResult lookUpMember(
+ Session* session,
+ SemanticsVisitor* semantics,
+ Name* name,
+ Type* type)
+{
+ LookupResult result;
+ lookUpMemberImpl(session, semantics, name, type, result, nullptr);
+ return result;
+}
+
+
}
diff --git a/source/slang/lookup.h b/source/slang/lookup.h
index be7cb30bc..473ecaf96 100644
--- a/source/slang/lookup.h
+++ b/source/slang/lookup.h
@@ -17,7 +17,7 @@ void buildMemberDictionary(ContainerDecl* decl);
// Look up a name in the given scope, proceeding up through
// parent scopes as needed.
-LookupResult LookUp(
+LookupResult lookUp(
Session* session,
SemanticsVisitor* semantics,
Name* name,
@@ -25,12 +25,19 @@ LookupResult LookUp(
// perform lookup within the context of a particular container declaration,
// and do *not* look further up the chain
-LookupResult LookUpLocal(
+LookupResult lookUpLocal(
Session* session,
SemanticsVisitor* semantics,
Name* name,
DeclRef<ContainerDecl> containerDeclRef);
+// Perform member lookup in the context of a type
+LookupResult lookUpMember(
+ Session* session,
+ SemanticsVisitor* semantics,
+ Name* name,
+ Type* type);
+
// TODO: this belongs somewhere else
QualType getTypeForDeclRef(
diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp
index 327902e4a..2f0ea810e 100644
--- a/source/slang/lower-to-ir.cpp
+++ b/source/slang/lower-to-ir.cpp
@@ -443,12 +443,62 @@ LoweredValInfo emitPostOp(
return LoweredValInfo::ptr(argPtr);
}
+// Emit a reference to a function, where we have concluded
+// that the original AST referenced `funcDeclRef`. The
+// optional expression `funcExpr` can provide additional
+// detail that might modify how we go about looking up
+// the actual value to call.
+LoweredValInfo emitFuncRef(
+ IRGenContext* context,
+ DeclRef<Decl> funcDeclRef,
+ Expr* funcExpr)
+{
+ if( !funcExpr )
+ {
+ return emitDeclRef(context, funcDeclRef);
+ }
+
+ // Let's look at the expression to see what additional
+ // information it gives us.
+
+ if(auto funcMemberExpr = dynamic_cast<MemberExpr*>(funcExpr))
+ {
+ auto baseExpr = funcMemberExpr->BaseExpression;
+ if(auto baseMemberExpr = baseExpr.As<MemberExpr>())
+ {
+ auto baseMemberDeclRef = baseMemberExpr->declRef;
+ if(auto baseConstraintDeclRef = baseMemberDeclRef.As<GenericTypeConstraintDecl>())
+ {
+ // We are calling a method "through" a generic type
+ // parameter that was constrained to some type.
+ // That means `funcDeclRef` is a reference to the method
+ // on the `interface` type (which doesn't actually have
+ // a body, so we don't want to emit or call it), and
+ // we actually want to perform a lookup step to
+ // find the corresponding member on our chosen type.
+
+ RefPtr<Type> type = funcExpr->type;
+
+ return LoweredValInfo::simple(context->irBuilder->emitLookupInterfaceMethodInst(
+ type,
+ baseMemberDeclRef,
+ funcDeclRef));
+ }
+ }
+ }
+
+ // We didn't trigger a special case, so just emit a reference
+ // to the function itself.
+ return emitDeclRef(context, funcDeclRef);
+}
+
// 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<Decl> funcDeclRef,
+ Expr* funcExpr,
UInt argCount,
IRValue* const* args)
{
@@ -572,7 +622,7 @@ LoweredValInfo emitCallToDeclRef(
}
// Fallback case is to emit an actual call.
- LoweredValInfo funcVal = emitDeclRef(context, funcDeclRef);
+ LoweredValInfo funcVal = emitFuncRef(context, funcDeclRef, funcExpr);
return emitCallToVal(context, type, funcVal, argCount, args);
}
@@ -580,9 +630,10 @@ LoweredValInfo emitCallToDeclRef(
IRGenContext* context,
IRType* type,
DeclRef<Decl> funcDeclRef,
+ Expr* funcExpr,
List<IRValue*> const& args)
{
- return emitCallToDeclRef(context, type, funcDeclRef, args.Count(), args.Buffer());
+ return emitCallToDeclRef(context, type, funcDeclRef, funcExpr, args.Count(), args.Buffer());
}
IRValue* getSimpleVal(IRGenContext* context, LoweredValInfo lowered)
@@ -611,6 +662,7 @@ top:
context,
boundSubscriptInfo->type,
getter,
+ nullptr,
boundSubscriptInfo->args);
goto top;
}
@@ -660,7 +712,7 @@ struct LoweredTypeInfo
}
};
-IRType* getSimpleType(LoweredTypeInfo lowered)
+RefPtr<Type> getSimpleType(LoweredTypeInfo lowered)
{
switch(lowered.flavor)
{
@@ -700,7 +752,7 @@ static LoweredTypeInfo lowerType(
}
// Lower a type and expect the result to be simple
-IRType* lowerSimpleType(
+RefPtr<Type> lowerSimpleType(
IRGenContext* context,
Type* type)
{
@@ -708,7 +760,7 @@ IRType* lowerSimpleType(
return getSimpleType(lowered);
}
-IRType* lowerSimpleType(
+RefPtr<Type> lowerSimpleType(
IRGenContext* context,
QualType const& type)
{
@@ -819,7 +871,15 @@ struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, Lower
LoweredTypeInfo visitDeclRefType(DeclRefType* type)
{
- // TODO: is there actually anything to be done at this point?
+ // If the type in question comes from the module we are
+ // trying to lower, then we need to make sure to
+ // emit everything relevant to its declaration.
+
+ // TODO: actually test what module the type is coming from.
+
+ lowerDecl(context, type->declRef);
+
+
return LoweredTypeInfo(type);
}
@@ -966,6 +1026,16 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
boundMemberInfo->declRef = callableDeclRef;
return LoweredValInfo::boundMember(boundMemberInfo);
}
+ else if(auto constraintDeclRef = declRef.As<GenericTypeConstraintDecl>())
+ {
+ // 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");
}
@@ -1315,7 +1385,12 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
// require "fixup" work on the other side.
//
addDirectCallArgs(expr, funcDeclRef, &irArgs, &argFixups);
- auto result = emitCallToDeclRef(context, type, funcDeclRef, irArgs);
+ auto result = emitCallToDeclRef(
+ context,
+ type,
+ funcDeclRef,
+ funcExpr,
+ irArgs);
applyOutArgumentFixups(argFixups);
return result;
}
@@ -1937,6 +2012,7 @@ top:
context,
context->getSession()->getVoidType(),
setterDeclRef,
+ nullptr,
allArgs);
return;
}
@@ -1972,6 +2048,72 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
SLANG_UNIMPLEMENTED_X("decl catch-all");
}
+ LoweredValInfo visitGenericTypeParamDecl(GenericTypeParamDecl* decl)
+ {
+ return LoweredValInfo();
+ }
+
+ LoweredValInfo visitInheritanceDecl(InheritanceDecl* inheritanceDecl)
+ {
+ // Construct a type for the parent declaration.
+ //
+ // TODO: if this inheritance declaration is under an extension,
+ // then we should construct the type that is being extended,
+ // and not a reference to the extension itself.
+ auto parentDecl = inheritanceDecl->ParentDecl;
+ RefPtr<Type> type = DeclRefType::Create(
+ context->getSession(),
+ makeDeclRef(parentDecl));
+
+
+ // TODO: if the parent type is generic, then I suppose these
+ // need to be *generic* witness tables?
+
+ // What is the super-type that we have declared we inherit from?
+ RefPtr<Type> 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.
+ String mangledName = getMangledNameForConformanceWitness(
+ type,
+ superType);
+
+ // Build an IR level witness table, which will represent the
+ // conformance of the type to its super-type.
+ auto witnessTable = context->irBuilder->createWitnessTable();
+ witnessTable->mangledName = mangledName;
+
+ // Register the value now, rather than later, to avoid
+ // infinite recursion.
+ context->shared->declValues[inheritanceDecl] = LoweredValInfo::simple(witnessTable);
+
+
+ // Semantic checking will have filled in a dictionary of
+ // witnesses for requirements in the interface, and we
+ // will now navigate that dictionary to fill in the witness table.
+ for (auto entry : inheritanceDecl->requirementWitnesses)
+ {
+ auto requiredMemberDeclRef = entry.Key;
+ auto satisfyingMemberDecl = entry.Value;
+
+ auto irRequirement = context->irBuilder->getDeclRefVal(requiredMemberDeclRef);
+ auto irSatisfyingVal = getSimpleVal(context, ensureDecl(context, satisfyingMemberDecl));
+
+ auto witnessTableEntry = context->irBuilder->createWitnessTableEntry(
+ witnessTable,
+ irRequirement,
+ irSatisfyingVal);
+ }
+
+ witnessTable->moveToEnd();
+
+ // A direct reference to this inheritance relationship (e.g.,
+ // as a subtype witness) will take the form of a reference to
+ // the witness table in the IR.
+ return LoweredValInfo::simple(witnessTable);
+ }
+
+
LoweredValInfo visitDeclGroup(DeclGroup* declGroup)
{
// To lowere a group of declarations, we just
@@ -2141,91 +2283,25 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
LoweredValInfo visitAggTypeDecl(AggTypeDecl* decl)
{
-#if 0
- // User-defined aggregate type: need to translate into
- // a corresponding IR aggregate type.
-
- auto builder = getBuilder();
- IRStructDecl* irStruct = builder->createStructType();
-
- for (auto fieldDecl : decl->GetFields())
+ // 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<InheritanceDecl>() )
{
- // TODO: need to track relationship to original fields...
-
- // TODO: need to be prepared to deal with tuple-ness of fields here
- auto fieldType = lowerType(context, fieldDecl->getType());
-
- switch (fieldType.flavor)
- {
- case LoweredTypeInfo::Flavor::Simple:
- {
- auto irField = builder->createStructField(getSimpleType(fieldType));
- builder->addInst(irStruct, irField);
-
- builder->addHighLevelDeclDecoration(irField, fieldDecl);
-
- context->shared->declValues.Add(
- DeclRef<StructField>(fieldDecl, nullptr),
- LoweredValInfo::simple(irField));
- }
- break;
-
- default:
- SLANG_UNIMPLEMENTED_X("struct field type");
- }
+ ensureDecl(context, inheritanceDecl);
}
- builder->addHighLevelDeclDecoration(irStruct, decl);
-
- builder->addInst(irStruct);
-
- return LoweredValInfo::simple(irStruct);
-#else
- // TODO: What is there to do with a `struct` type?
+ // For now, we don't have an IR-level representation
+ // for the type itself.
return LoweredValInfo();
-#endif
}
- // Sometimes we need to refer to a declaration the way that it would be specialized
- // inside the context where it is declared (e.g., with generic parameters filled in
- // using their archetypes).
- //
- RefPtr<Substitutions> createDefaultSubstitutions(Decl* decl)
- {
- auto dd = decl->ParentDecl;
- while( dd )
- {
- if( auto genericDecl = dynamic_cast<GenericDecl*>(dd) )
- {
- auto session = context->getSession();
-
- RefPtr<Substitutions> subst = new Substitutions();
- subst->genericDecl = genericDecl;
- subst->outer = createDefaultSubstitutions(genericDecl);
-
- for( auto mm : genericDecl->Members )
- {
- if( auto genericTypeParamDecl = mm.As<GenericTypeParamDecl>() )
- {
- subst->args.Add(DeclRefType::Create(session, makeDeclRef(genericTypeParamDecl.Ptr())));
- }
- else if( auto genericValueParamDecl = mm.As<GenericValueParamDecl>() )
- {
- subst->args.Add(new GenericParamIntVal(makeDeclRef(genericValueParamDecl.Ptr())));
- }
- }
- return subst;
- }
- dd = dd->ParentDecl;
- }
- return nullptr;
- }
DeclRef<Decl> createDefaultSpecializedDeclRefImpl(Decl* decl)
{
DeclRef<Decl> declRef;
declRef.decl = decl;
- declRef.substitutions = createDefaultSubstitutions(decl);
+ declRef.substitutions = createDefaultSubstitutions(context->getSession(), decl);
return declRef;
}
//
@@ -2595,7 +2671,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
for( auto paramInfo : parameterLists.params )
{
- IRType* irParamType = lowerSimpleType(context, paramInfo.type);
+ RefPtr<Type> irParamType = lowerSimpleType(context, paramInfo.type);
switch( paramInfo.direction )
{
@@ -2854,6 +2930,97 @@ LoweredValInfo ensureDecl(
return result;
}
+IRWitnessTable* findWitnessTable(
+ IRGenContext* context,
+ DeclRef<Decl> declRef)
+{
+ IRValue* irVal = getSimpleVal(context, emitDeclRef(context, declRef));
+ if (!irVal)
+ {
+ SLANG_UNEXPECTED("expected a witness table");
+ return nullptr;
+ }
+
+ if (irVal->op != kIROp_witness_table)
+ {
+ // TODO: We might eventually have cases of `specialize` called
+ // on a witness table...
+ SLANG_UNEXPECTED("expected a witness table");
+ return nullptr;
+ }
+
+ return (IRWitnessTable*)irVal;
+}
+
+RefPtr<Val> lowerSubstitutionArg(
+ IRGenContext* context,
+ Val* val)
+{
+ if (auto type = dynamic_cast<Type*>(val))
+ {
+ return lowerSimpleType(context, type);
+ }
+ else if (auto declaredSubtypeWitness = dynamic_cast<DeclaredSubtypeWitness*>(val))
+ {
+ // We need to look up the IR-level representation of the witness
+ // (which is a witness table).
+
+ auto irWitnessTable = findWitnessTable(context, declaredSubtypeWitness->declRef);
+
+ // We have an IR-level value, but we need to embed it into an AST-level
+ // type, so we will use a proxy `Val` that wraps up an `IRValue` as
+ // an AST-level value.
+ //
+ // TODO: This proxy value currently doesn't enter into use-def chaining,
+ // and so Bad Things could happen quite easily. We need to fix that
+ // up in a reasonably clean fashion.
+ //
+ RefPtr<IRProxyVal> proxyVal = new IRProxyVal();
+ proxyVal->inst = irWitnessTable;
+ return proxyVal;
+ }
+ else
+ {
+ // For now, jsut assume that all other values
+ // lower to themselves.
+ //
+ // TODO: we should probably handle the case of
+ // a `Val` that references an AST-level `constexpr`
+ // variable, since that would need to be lowered
+ // to a `Val` that references the IR equivalent.
+ return val;
+ }
+}
+
+// Given a set of substitutions, make sure that we have
+// lowered the arguments being used into a form that
+// is suitable for use in the IR.
+RefPtr<Substitutions> lowerSubstitutions(
+ IRGenContext* context,
+ Substitutions* subst)
+{
+ if(!subst)
+ return nullptr;
+
+ RefPtr<Substitutions> newSubst = new Substitutions();
+ if (subst->outer)
+ {
+ newSubst->outer = lowerSubstitutions(
+ context,
+ subst->outer);
+ }
+
+ newSubst->genericDecl = subst->genericDecl;
+
+ for (auto arg : subst->args)
+ {
+ auto newArg = lowerSubstitutionArg(context, arg);
+ newSubst->args.Add(newArg);
+ }
+
+ return newSubst;
+}
+
LoweredValInfo emitDeclRef(
IRGenContext* context,
DeclRef<Decl> declRef)
@@ -2869,6 +3036,14 @@ LoweredValInfo emitDeclRef(
auto val = getSimpleVal(context, loweredDecl);
+ // We have the "raw" substitutions from the AST, but we may
+ // need to walk through those and replace things in
+ // cases where the `Val`s used for substitution should
+ // lower to something other than their original form.
+ RefPtr<Substitutions> newSubst = lowerSubstitutions(context, declRef.substitutions);
+ declRef.substitutions = newSubst;
+
+
RefPtr<Type> type;
if(auto declType = val->getType())
{
diff --git a/source/slang/lower.cpp b/source/slang/lower.cpp
index 94738307b..0f16a8ad7 100644
--- a/source/slang/lower.cpp
+++ b/source/slang/lower.cpp
@@ -688,6 +688,11 @@ struct LoweringVisitor
return val;
}
+ RefPtr<Witness> visitWitness(Witness* witness)
+ {
+ return witness;
+ }
+
//
// Types
//
@@ -797,6 +802,11 @@ struct LoweringVisitor
return lowerType(type);
}
+ RefPtr<Val> visitIRProxyVal(IRProxyVal* val)
+ {
+ return val;
+ }
+
//
// Expressions
//
diff --git a/source/slang/mangle.cpp b/source/slang/mangle.cpp
index 71c0605a9..63e54d065 100644
--- a/source/slang/mangle.cpp
+++ b/source/slang/mangle.cpp
@@ -131,6 +131,27 @@ namespace Slang
{
emitType(context, type);
}
+ else if( auto witness = dynamic_cast<Witness*>(val) )
+ {
+ // We don't emit witnesses as part of a mangled
+ // name, because the way that the front-end
+ // arrived at the witness is not important;
+ // what matters is that the type constraint
+ // was satisfied.
+ //
+ // TODO: make sure we can't get name collisions
+ // between specializations of declarations
+ // with the same numbers of generic parameters,
+ // but different constraints. We might have
+ // to mangle in the constraints even when
+ // the whole thing is specialized...
+ }
+ else if (auto proxyVal = dynamic_cast<IRProxyVal*>(val))
+ {
+ // This is a proxy standing in for some IR-level
+ // value, so we certainly don't want to include
+ // it in the mangling.
+ }
else
{
SLANG_UNEXPECTED("unimplemented case in mangling");
@@ -316,4 +337,21 @@ namespace Slang
{
return getMangledName(makeDeclRef(decl));
}
+
+ String getMangledNameForConformanceWitness(
+ Type* sub,
+ Type* sup)
+ {
+ // The mangled form for a witness that `sub`
+ // conforms to `sup` will be named:
+ //
+ // {Conforms(sub,sup)} => _SW{sub}{sup}
+ //
+ ManglingContext context;
+ emitRaw(&context, "_SW");
+ emitType(&context, sub);
+ emitType(&context, sup);
+ return context.sb.ProduceString();
+ }
+
}
diff --git a/source/slang/mangle.h b/source/slang/mangle.h
index 11196f496..60a1dff9e 100644
--- a/source/slang/mangle.h
+++ b/source/slang/mangle.h
@@ -11,6 +11,10 @@ namespace Slang
String getMangledName(Decl* decl);
String getMangledName(DeclRef<Decl> const & declRef);
String getMangledName(DeclRefBase const & declRef);
+
+ String getMangledNameForConformanceWitness(
+ Type* sub,
+ Type* sup);
}
#endif \ No newline at end of file
diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp
index 2b6535bef..3a8c5b362 100644
--- a/source/slang/parser.cpp
+++ b/source/slang/parser.cpp
@@ -655,7 +655,7 @@ namespace Slang
{
// Let's look up the name and see what we find.
- auto lookupResult = LookUp(
+ auto lookupResult = lookUp(
parser->getSession(),
nullptr, // no semantics visitor available yet
name,
@@ -2080,6 +2080,7 @@ namespace Slang
auto inheritanceDecl = new InheritanceDecl();
inheritanceDecl->loc = base.exp->loc;
+ inheritanceDecl->nameAndLoc.name = getName(parser, "$inheritance");
inheritanceDecl->base = base;
AddMember(decl, inheritanceDecl);
@@ -2106,6 +2107,16 @@ namespace Slang
RefPtr<ConstructorDecl> decl = new ConstructorDecl();
parser->FillPosition(decl.Ptr());
+ // TODO: we need to make sure that all initializers have
+ // the same name, but that this name doesn't conflict
+ // with any user-defined names.
+ // Giving them a name (rather than leaving it null)
+ // ensures that we can use name-based lookup to find
+ // all of the initializers on a type (and has
+ // the potential to unify initializer lookup with
+ // ordinary member lookup).
+ decl->nameAndLoc.name = getName(parser, "$init");
+
parseParameterList(parser, decl);
if( AdvanceIf(parser, TokenType::Semicolon) )
@@ -2560,7 +2571,7 @@ namespace Slang
static bool isGenericName(Parser* parser, Name* name)
{
- auto lookupResult = LookUp(
+ auto lookupResult = lookUp(
parser->getSession(),
nullptr, // no semantics visitor available yet
name,
@@ -2582,7 +2593,7 @@ namespace Slang
static bool isTypeName(Parser* parser, Name* name)
{
- auto lookupResult = LookUp(
+ auto lookupResult = lookUp(
parser->getSession(),
nullptr, // no semantics visitor available yet
name,
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index da7601d41..f1d1d6ba2 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -900,7 +900,7 @@ SLANG_API int spCompile(
{
auto req = REQ(request);
-#if 1
+#if 0
// By default we'd like to catch as many internal errors as possible,
// and report them to the user nicely (rather than just crash their
// application). Internally Slang currently uses exceptions for this.
diff --git a/source/slang/syntax.cpp b/source/slang/syntax.cpp
index 54a4a79b6..6cf3fd7c9 100644
--- a/source/slang/syntax.cpp
+++ b/source/slang/syntax.cpp
@@ -479,6 +479,25 @@ void Type::accept(IValVisitor* visitor, void* extra)
Session* session,
DeclRef<Decl> declRef)
{
+ // It is possible that `declRef` refers to a generic type,
+ // but does not specify arguments for its generic parameters.
+ // (E.g., this happens when referring to a generic type from
+ // within its own member functions). To handle this case,
+ // we will construct a default specialization at the use
+ // site if needed.
+
+ if( auto genericParent = declRef.GetParent().As<GenericDecl>() )
+ {
+ auto subst = declRef.substitutions;
+ if( !subst || subst->genericDecl != genericParent.decl )
+ {
+ declRef.substitutions = createDefaultSubstitutions(
+ session,
+ declRef.decl,
+ subst);
+ }
+ }
+
if (auto builtinMod = declRef.getDecl()->FindModifier<BuiltinTypeModifier>())
{
auto type = new BasicExpressionType(builtinMod->tag);
@@ -1155,6 +1174,9 @@ void Type::accept(IValVisitor* visitor, void* extra)
DeclRefBase DeclRefBase::GetParent() const
{
auto parentDecl = decl->ParentDecl;
+ if (!parentDecl)
+ return DeclRefBase();
+
if (auto parentGeneric = dynamic_cast<GenericDecl*>(parentDecl))
{
if (substitutions && substitutions->genericDecl == parentDecl)
@@ -1350,7 +1372,7 @@ void Type::accept(IValVisitor* visitor, void* extra)
Session* session,
DeclRef<CallableDecl> const& declRef)
{
- auto funcType = new FuncType();
+ RefPtr<FuncType> funcType = new FuncType();
funcType->setSession(session);
funcType->resultType = GetResultType(declRef);
@@ -1379,4 +1401,72 @@ void Type::accept(IValVisitor* visitor, void* extra)
return samplerStateType;
}
+ // TODO: should really have a `type.cpp` and a `witness.cpp`
+
+ bool DeclaredSubtypeWitness::EqualsVal(Val* val)
+ {
+ auto otherWitness = dynamic_cast<DeclaredSubtypeWitness*>(val);
+ if(!otherWitness)
+ return false;
+
+ return sub->Equals(otherWitness->sub)
+ && sup->Equals(otherWitness->sup)
+ && declRef.Equals(otherWitness->declRef);
+ }
+
+ String DeclaredSubtypeWitness::ToString()
+ {
+ StringBuilder sb;
+ sb << "DeclaredSubtypeWitness(";
+ sb << this->sub->ToString();
+ sb << ", ";
+ sb << this->sup->ToString();
+ sb << ", ";
+ sb << this->declRef.toString();
+ sb << ")";
+ return sb.ProduceString();
+ }
+
+ int DeclaredSubtypeWitness::GetHashCode()
+ {
+ auto hash = sub->GetHashCode();
+ hash = combineHash(hash, sup->GetHashCode());
+ hash = combineHash(hash, declRef.GetHashCode());
+ return hash;
+ }
+
+ // IRProxyVal
+
+ bool IRProxyVal::EqualsVal(Val* val)
+ {
+ auto otherProxy = dynamic_cast<IRProxyVal*>(val);
+ if(!otherProxy)
+ return false;
+
+ return this->inst == otherProxy->inst;
+ }
+
+ String IRProxyVal::ToString()
+ {
+ return "IRProxyVal(...)";
+ }
+
+ int IRProxyVal::GetHashCode()
+ {
+ auto hash = Slang::GetHashCode(inst);
+ return hash;
+ }
+
+ //
+
+ String DeclRefBase::toString() const
+ {
+ StringBuilder sb;
+ sb << this->getDecl()->getName()->text;
+ // TODO: need to print out substitutions too!
+ return sb.ProduceString();
+ }
+
+
+
}
diff --git a/source/slang/syntax.h b/source/slang/syntax.h
index f5e22d9b5..d32c4de15 100644
--- a/source/slang/syntax.h
+++ b/source/slang/syntax.h
@@ -12,6 +12,7 @@
namespace Slang
{
+ struct IRValue;
class Name;
class Session;
class Substitutions;
@@ -450,6 +451,9 @@ namespace Slang
DeclRefBase GetParent() const;
int GetHashCode() const;
+
+ // Debugging:
+ String toString() const;
};
template<typename T>
@@ -787,17 +791,92 @@ namespace Slang
//
// We build up a list of these "breadcrumbs" while doing
// lookup, and store them alongside each item found.
+ //
+ // As an example, suppose we have an HLSL `cbuffer` declaration:
+ //
+ // cbuffer C { float4 f; }
+ //
+ // This is syntax sugar for a global-scope variable of
+ // type `ConstantBuffer<T>` where `T` is a `struct` containing
+ // all the members:
+ //
+ // struct Anon0 { float4 f; };
+ // __transparent ConstantBuffer<Anon0> anon1;
+ //
+ // The `__transparent` modifier there captures the fact that
+ // when somebody writes `f` in their code, they expect it to
+ // "see through" the `cbuffer` declaration (or the global variable,
+ // in this case) and find the member inside.
+ //
+ // But when the user writes `f` we can't just create a simple
+ // `VarExpr` that refers directly to that field, because that
+ // doesn't actually reflect the required steps in a way that
+ // code generation can use.
+ //
+ // Instead we need to construct an expression like `(*anon1).f`,
+ // where there is are two additional steps in the process:
+ //
+ // 1. We needed to dereference the pointer-like type `ConstantBuffer<Anon0>`
+ // to get at a value of type `Anon0`
+ // 2. We needed to access a sub-field of the aggregate type `Anon0`
+ //
+ // We *could* just create these full-formed expressions during
+ // lookup, but this might mean creating a large number of
+ // AST nodes in cases where the user calls an overloaded function.
+ // At the very least we'd rather not heap-allocate in the common
+ // case where no "extra" steps need to be performed to get to
+ // the declarations.
+ //
+ // This is where "breadcrumbs" come in. A breadcrumb represents
+ // an extra "step" that must be performed to turn a declaration
+ // found by lookup into a valid expression to splice into the
+ // AST. Most of the time lookup result items don't have any
+ // breadcrumbs, so that no extra heap allocation takes place.
+ // When an item does have breadcrumbs, and it is chosen as
+ // the unique result (perhaps by overload resolution), then
+ // we can walk the list of breadcrumbs to create a full
+ // expression.
class Breadcrumb : public RefObject
{
public:
enum class Kind
{
- Member, // A member was references
- Deref, // A value with pointer(-like) type was dereferenced
+ // The lookup process looked "through" an in-scope
+ // declaration to the fields inside of it, so that
+ // even if lookup started with a simple name `f`,
+ // it needs to result in a member expression `obj.f`.
+ Member,
+
+ // The lookup process took a pointer(-like) value, and then
+ // proceeded to derefence it and look at the thing(s)
+ // it points to instead, so that the final expression
+ // needs to have `(*obj)`
+ Deref,
+
+ // The lookup process saw a value `obj` of type `T` and
+ // took into account an in-scope constraint that says
+ // `T` is a subtype of some other type `U`, so that
+ // lookup was able to find a member through type `U`
+ // instead.
+ Constraint,
};
+ // The kind of lookup step that was performed
Kind kind;
+
+ // As needed, a reference to the declaration that faciliated
+ // the lookup step.
+ //
+ // For a `Member` lookup step, this is the declaration whose
+ // members were implicitly pulled into scope.
+ //
+ // For a `Constraint` lookup step, this is the `ConstraintDecl`
+ // that serves to witness the subtype relationship.
+ //
DeclRef<Decl> declRef;
+
+ // The next implicit step that the lookup process took to
+ // arrive at a final value.
RefPtr<Breadcrumb> next;
Breadcrumb(Kind kind, DeclRef<Decl> declRef, RefPtr<Breadcrumb> next)
@@ -1060,6 +1139,16 @@ namespace Slang
}
}
+ // TODO: where should this live?
+ RefPtr<Substitutions> createDefaultSubstitutions(
+ Session* session,
+ Decl* decl,
+ Substitutions* parentSubst);
+
+ RefPtr<Substitutions> createDefaultSubstitutions(
+ Session* session,
+ Decl* decl);
+
} // namespace Slang
#endif \ No newline at end of file
diff --git a/source/slang/type-defs.h b/source/slang/type-defs.h
index 7ec801a14..2010d07b4 100644
--- a/source/slang/type-defs.h
+++ b/source/slang/type-defs.h
@@ -491,4 +491,3 @@ protected:
virtual Type* CreateCanonicalType() override;
)
END_SYNTAX_CLASS()
-
diff --git a/source/slang/val-defs.h b/source/slang/val-defs.h
index 63abf973c..4513873e3 100644
--- a/source/slang/val-defs.h
+++ b/source/slang/val-defs.h
@@ -40,3 +40,72 @@ SYNTAX_CLASS(GenericParamIntVal, IntVal)
virtual RefPtr<Val> SubstituteImpl(Substitutions* subst, int* ioDiff) override;
)
END_SYNTAX_CLASS()
+
+// A witness to the fact that some proposition is true, encoded
+// at the level of the type system.
+//
+// Given a generic like:
+//
+// void example<L>(L light)
+// where L : ILight
+// { ... }
+//
+// a call to `example()` needs two things for us to be sure
+// it is valid:
+//
+// 1. We need a type `X` to use as the argument for the
+// parameter `L`. We might supply this explicitly, or
+// via inference.
+//
+// 2. We need a *proof* that whatever `X` we chose conforms
+// to the `ILight` interface.
+//
+// The easiest way to make such a proof is by construction,
+// and a `Witness` represents such a constructive proof.
+// Conceptually a proposition like `X : ILight` can be
+// seen as a type, and witness prooving that proposition
+// is a value of that type.
+//
+// We construct and store witnesses explicitly during
+// semantic checking because they can help us with
+// generating downstream code. By following the structure
+// of a witness (the structure of a proof) we can, e.g.,
+// navigate from the knowledge that `X : ILight` to
+// the concrete declarations that provide the implementation
+// of `ILight` for `X`.
+//
+ABSTRACT_SYNTAX_CLASS(Witness, Val)
+END_SYNTAX_CLASS()
+
+// A witness that one type is a subtype of another
+// (where by "subtype" we include both inheritance
+// relationships and type-conforms-to-interface relationships)
+//
+// TODO: we may need to tease those apart.
+ABSTRACT_SYNTAX_CLASS(SubtypeWitness, Witness)
+ FIELD(RefPtr<Type>, sub)
+ FIELD(RefPtr<Type>, sup)
+END_SYNTAX_CLASS()
+
+// A witness that one type is a subtype of another
+// because some in-scope declaration says so
+SYNTAX_CLASS(DeclaredSubtypeWitness, SubtypeWitness)
+ FIELD(DeclRef<Decl>, declRef);
+RAW(
+ virtual bool EqualsVal(Val* val) override;
+ virtual String ToString() override;
+ virtual int GetHashCode() override;
+)
+END_SYNTAX_CLASS()
+
+// A value that is used as a proxy when we need to
+// put an IR-level value into AST types
+SYNTAX_CLASS(IRProxyVal, Val)
+ FIELD(IRValue*, inst)
+RAW(
+ virtual bool EqualsVal(Val* val) override;
+ virtual String ToString() override;
+ virtual int GetHashCode() override;
+)
+END_SYNTAX_CLASS()
+
diff --git a/tests/compute/generics-constrained.slang b/tests/compute/generics-constrained.slang
new file mode 100644
index 000000000..669674376
--- /dev/null
+++ b/tests/compute/generics-constrained.slang
@@ -0,0 +1,46 @@
+//TEST(smoke,compute):COMPARE_COMPUTE:-xslang -use-ir
+//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):dxbinding(0),glbinding(0),out
+
+// Use interface constraints on a generic parameter
+
+interface Helper
+{
+ float getHelp();
+}
+
+struct A : Helper
+{
+ float a;
+
+ float getHelp()
+ {
+ // TODO: we should be able to reference a member variable here,
+ // but the front-end isn't handling references through `this`
+ // properly yet.
+// return a;
+
+ return 1.0f;
+ }
+};
+
+__generic<T : Helper>
+float testHelp(T helper)
+{
+ return helper.getHelp();
+}
+
+RWStructuredBuffer<float> outputBuffer : register(u0);
+
+
+[numthreads(4, 1, 1)]
+void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
+{
+ uint tid = dispatchThreadID.x;
+ float inVal = float(tid);
+
+ A a;
+ a.a = inVal;
+ float outVal = testHelp<A>(a);
+
+ outputBuffer[tid] = outVal;
+} \ No newline at end of file
diff --git a/tests/compute/generics-constrained.slang.expected.txt b/tests/compute/generics-constrained.slang.expected.txt
new file mode 100644
index 000000000..cc5e55ab6
--- /dev/null
+++ b/tests/compute/generics-constrained.slang.expected.txt
@@ -0,0 +1,4 @@
+3F800000
+3F800000
+3F800000
+3F800000
diff --git a/tests/ir/loop.slang.expected b/tests/ir/loop.slang.expected
index 390fd80e0..212ce1913 100644
--- a/tests/ir/loop.slang.expected
+++ b/tests/ir/loop.slang.expected
@@ -1,79 +1,79 @@
result code = 0
standard error = {
-ir_global_var %1 : Ptr<@ThreadGroup vector<float,4>[64]>;
+ir_global_var @_SV01s : Ptr<@ThreadGroup vector<float,4>[64]>;
-ir_global_var %2 : Ptr<StructuredBuffer<vector<float,4>>>;
+ir_global_var @_SV05input : Ptr<StructuredBuffer<vector<float,4>>>;
-ir_global_var %3 : Ptr<RWStructuredBuffer<vector<float,4>>>;
+ir_global_var @_SV06output : Ptr<RWStructuredBuffer<vector<float,4>>>;
ir_func @_S04mainp3uuuV : (uint, uint, uint) -> void
{
-block %4(
- param %5 : uint,
- param %6 : uint,
- param %7 : uint):
- let %8 : Ptr<uint> = var()
- store(%8, %5)
- let %9 : Ptr<uint> = var()
- store(%9, %6)
- let %10 : Ptr<uint> = var()
- store(%10, %7)
- let %11 : uint = load(%9)
- let %12 : Ptr<vector<float,4>> = getElementPtr(%1, %11)
- let %13 : StructuredBuffer<vector<float,4>> = load(%2)
- let %14 : uint = load(%8)
- let %15 : vector<float,4> = bufferLoad(%13, %14)
- store(%12, %15)
- let %16 : Ptr<uint> = var()
- let %17 : uint = construct(1)
- store(%16, %17)
- loop(%18, %19, %20)
+block %1(
+ param %2 : uint,
+ param %3 : uint,
+ param %4 : uint):
+ let %5 : Ptr<uint> = var()
+ store(%5, %2)
+ let %6 : Ptr<uint> = var()
+ store(%6, %3)
+ let %7 : Ptr<uint> = var()
+ store(%7, %4)
+ let %8 : uint = load(%6)
+ let %9 : Ptr<vector<float,4>> = getElementPtr(@_SV01s, %8)
+ let %10 : StructuredBuffer<vector<float,4>> = load(@_SV05input)
+ let %11 : uint = load(%5)
+ let %12 : vector<float,4> = bufferLoad(%10, %11)
+ store(%9, %12)
+ let %13 : Ptr<uint> = var()
+ let %14 : uint = construct(1)
+ store(%13, %14)
+ loop(%15, %16, %17)
-block %18:
- let %21 : uint = load(%16)
- let %22 : uint = construct(64)
- let %23 : bool = cmpLT(%21, %22)
- loopTest(%23, %24, %19)
+block %15:
+ let %18 : uint = load(%13)
+ let %19 : uint = construct(64)
+ let %20 : bool = cmpLT(%18, %19)
+ loopTest(%20, %21, %16)
-block %24:
+block %21:
GroupMemoryBarrierWithGroupSync()
- let %25 : uint = load(%9)
- let %26 : Ptr<vector<float,4>> = getElementPtr(%1, %25)
- let %27 : Ptr<vector<float,4>> = var()
- let %28 : vector<float,4> = load(%26)
- store(%27, %28)
- let %29 : uint = load(%9)
- let %30 : uint = load(%16)
- let %31 : uint = sub(%29, %30)
- let %32 : Ptr<vector<float,4>> = getElementPtr(%1, %31)
- let %33 : vector<float,4> = load(%32)
- let %34 : vector<float,4> = load(%27)
- let %35 : vector<float,4> = add(%34, %33)
- store(%27, %35)
- let %36 : vector<float,4> = load(%27)
- store(%26, %36)
- unconditionalBranch(%20)
+ let %22 : uint = load(%6)
+ let %23 : Ptr<vector<float,4>> = getElementPtr(@_SV01s, %22)
+ let %24 : Ptr<vector<float,4>> = var()
+ let %25 : vector<float,4> = load(%23)
+ store(%24, %25)
+ let %26 : uint = load(%6)
+ let %27 : uint = load(%13)
+ let %28 : uint = sub(%26, %27)
+ let %29 : Ptr<vector<float,4>> = getElementPtr(@_SV01s, %28)
+ let %30 : vector<float,4> = load(%29)
+ let %31 : vector<float,4> = load(%24)
+ let %32 : vector<float,4> = add(%31, %30)
+ store(%24, %32)
+ let %33 : vector<float,4> = load(%24)
+ store(%23, %33)
+ unconditionalBranch(%17)
-block %20:
- let %37 : Ptr<uint> = var()
- let %38 : uint = load(%16)
- store(%37, %38)
- let %39 : uint = construct(1)
- let %40 : uint = load(%37)
- let %41 : uint = shl(%40, %39)
- store(%37, %41)
- let %42 : uint = load(%37)
- store(%16, %42)
- unconditionalBranch(%18)
+block %17:
+ let %34 : Ptr<uint> = var()
+ let %35 : uint = load(%13)
+ store(%34, %35)
+ let %36 : uint = construct(1)
+ let %37 : uint = load(%34)
+ let %38 : uint = shl(%37, %36)
+ store(%34, %38)
+ let %39 : uint = load(%34)
+ store(%13, %39)
+ unconditionalBranch(%15)
-block %19:
+block %16:
GroupMemoryBarrierWithGroupSync()
- let %43 : RWStructuredBuffer<vector<float,4>> = load(%3)
- let %44 : uint = load(%8)
- let %45 : Ptr<vector<float,4>> = getElementPtr(%1, 0)
- let %46 : vector<float,4> = load(%45)
- bufferStore(%43, %44, %46)
+ let %40 : RWStructuredBuffer<vector<float,4>> = load(@_SV06output)
+ let %41 : uint = load(%5)
+ let %42 : Ptr<vector<float,4>> = getElementPtr(@_SV01s, 0)
+ let %43 : vector<float,4> = load(%42)
+ bufferStore(%40, %41, %43)
return_void()
}