summaryrefslogtreecommitdiff
path: root/source/slang/ir.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/ir.cpp')
-rw-r--r--source/slang/ir.cpp589
1 files changed, 517 insertions, 72 deletions
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();
}
}
}