summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2020-07-10 09:13:50 -0700
committerGitHub <noreply@github.com>2020-07-10 09:13:50 -0700
commit2503280122c7ac54cc3e42e2e54efff1d002d126 (patch)
treef16d703731801fae2169aa500b6d4cfad2d6fae8 /source
parenta5a67aae981cb54d535089b167d3edcc3a3a2e29 (diff)
Dynamic code gen for generic local variables. (#1434)
* Dynamic code gen for generic local variables. * Fixes to function calls with generic typed `in` argument. * Fixes per code review comments
Diffstat (limited to 'source')
-rw-r--r--source/slang/slang-emit-c-like.cpp15
-rw-r--r--source/slang/slang-emit-c-like.h1
-rw-r--r--source/slang/slang-emit-cpp.cpp76
-rw-r--r--source/slang/slang-emit-cpp.h1
-rw-r--r--source/slang/slang-ir-inst-defs.h9
-rw-r--r--source/slang/slang-ir-insts.h63
-rw-r--r--source/slang/slang-ir-lower-generics.cpp309
-rw-r--r--source/slang/slang-ir.cpp72
-rw-r--r--source/slang/slang-ir.h23
-rw-r--r--source/slang/slang-lower-to-ir.cpp8
10 files changed, 521 insertions, 56 deletions
diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp
index 2f0b0b035..582f5e445 100644
--- a/source/slang/slang-emit-c-like.cpp
+++ b/source/slang/slang-emit-c-like.cpp
@@ -309,6 +309,14 @@ void CLikeSourceEmitter::emitInterface(IRInterfaceType* interfaceType)
// This behavior is overloaded by concrete emitters.
}
+void CLikeSourceEmitter::emitRTTIObject(IRRTTIObject* rttiObject)
+{
+ SLANG_UNUSED(rttiObject);
+ // Ignore rtti object by default.
+ // This is only used in targets that support dynamic dispatching.
+}
+
+
void CLikeSourceEmitter::emitTypeImpl(IRType* type, const StringSliceLoc* nameAndLoc)
{
if (nameAndLoc)
@@ -908,6 +916,7 @@ bool CLikeSourceEmitter::shouldFoldInstIntoUseSites(IRInst* inst)
case kIROp_GlobalParam:
case kIROp_Param:
case kIROp_Func:
+ case kIROp_Alloca:
return false;
// Always fold these in, because they are trivial
@@ -2010,6 +2019,8 @@ void CLikeSourceEmitter::defaultEmitInstExpr(IRInst* inst, const EmitOpInfo& inO
/* Don't need to to output anything for this instruction - it's used for reflecting string literals that
are hashed with 'getStringHash' */
break;
+ case kIROp_RTTIPointerType:
+ break;
case kIROp_undefined:
m_writer->emit(getName(inst));
@@ -3674,6 +3685,10 @@ void CLikeSourceEmitter::emitGlobalInst(IRInst* inst)
emitWitnessTable(cast<IRWitnessTable>(inst));
break;
+ case kIROp_RTTIObject:
+ emitRTTIObject(cast<IRRTTIObject>(inst));
+ break;
+
default:
// We have an "ordinary" instruction at the global
// scope, and we should therefore emit it using the
diff --git a/source/slang/slang-emit-c-like.h b/source/slang/slang-emit-c-like.h
index b04b075c5..7f91a7be4 100644
--- a/source/slang/slang-emit-c-like.h
+++ b/source/slang/slang-emit-c-like.h
@@ -334,6 +334,7 @@ public:
virtual void emitWitnessTable(IRWitnessTable* witnessTable);
virtual void emitInterface(IRInterfaceType* interfaceType);
+ virtual void emitRTTIObject(IRRTTIObject* rttiObject);
virtual void handleCallExprDecorationsImpl(IRInst* funcValue) { SLANG_UNUSED(funcValue); }
diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp
index 1ff5af4fb..54c2257f2 100644
--- a/source/slang/slang-emit-cpp.cpp
+++ b/source/slang/slang-emit-cpp.cpp
@@ -394,6 +394,8 @@ SlangResult CPPSourceEmitter::calcTypeName(IRType* type, CodeGenTarget target, S
{
switch (type->op)
{
+ case kIROp_OutType:
+ case kIROp_InOutType:
case kIROp_PtrType:
{
auto ptrType = static_cast<IRPtrType*>(type);
@@ -496,6 +498,7 @@ SlangResult CPPSourceEmitter::calcTypeName(IRType* type, CodeGenTarget target, S
return SLANG_OK;
}
case kIROp_RawPointerType:
+ case kIROp_RTTIPointerType:
{
out << "void*";
return SLANG_OK;
@@ -510,6 +513,11 @@ SlangResult CPPSourceEmitter::calcTypeName(IRType* type, CodeGenTarget target, S
out << "*";
return SLANG_OK;
}
+ case kIROp_RTTIType:
+ {
+ out << "TypeInfo";
+ return SLANG_OK;
+ }
default:
{
if (isNominalOp(type->op))
@@ -1692,14 +1700,35 @@ void CPPSourceEmitter::_emitWitnessTableWrappers()
else
m_writer->emit(", ");
+ // If the implementation expects a concrete type
+ // (either in the form of a pointer for `out`/`inout` parameters,
+ // or in the form a a value for `in` parameters, while
+ // the interface exposes a raw pointer type (void*),
+ // we need to cast the raw pointer type to the appropriate
+ // concerete type. (void*->Concrete* / void*->Concrete&).
if (reqParamType->op == kIROp_RawPointerType &&
- param->getFullType()->op != kIROp_RawPointerType)
+ param->getDataType()->op != kIROp_RawPointerType)
{
- m_writer->emit("*static_cast<");
- emitType(param->getFullType());
- m_writer->emit("*>(");
- m_writer->emit(getName(param));
- m_writer->emit(")");
+ if (as<IRPtrTypeBase>(param->getFullType()))
+ {
+ // The implementation function expects a pointer to the
+ // concrete type. This is the case for inout/out parameters.
+ m_writer->emit("static_cast<");
+ emitType(param->getFullType());
+ m_writer->emit(">(");
+ m_writer->emit(getName(param));
+ m_writer->emit(")");
+ }
+ else
+ {
+ // The implementation function expects just a value of the
+ // concrete type. We need to insert a dereference in this case.
+ m_writer->emit("*static_cast<");
+ emitType(param->getFullType());
+ m_writer->emit("*>(");
+ m_writer->emit(getName(param));
+ m_writer->emit(")");
+ }
}
else
{
@@ -1773,6 +1802,18 @@ void CPPSourceEmitter::emitInterface(IRInterfaceType* interfaceType)
m_writer->emit(";\n");
}
+void CPPSourceEmitter::emitRTTIObject(IRRTTIObject* rttiObject)
+{
+ m_writer->emit("static TypeInfo ");
+ m_writer->emit(getName(rttiObject));
+ m_writer->emit(" = {");
+ auto typeSizeDecoration = rttiObject->findDecoration<IRRTTITypeSizeDecoration>();
+ SLANG_ASSERT(typeSizeDecoration);
+ m_writer->emit(typeSizeDecoration->getTypeSize());
+ m_writer->emit("};\n");
+}
+
+
/// Emits witness table type definition given a sorted list of witness tables
/// acoording to the order defined by `interfaceType`.
///
@@ -2273,6 +2314,29 @@ bool CPPSourceEmitter::tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOut
m_writer->emit("))");
return true;
}
+ case kIROp_RTTIObject:
+ {
+ m_writer->emit(getName(inst));
+ return true;
+ }
+ case kIROp_Alloca:
+ {
+ m_writer->emit("alloca(");
+ emitOperand(inst->getOperand(0), EmitOpInfo::get(EmitOp::Postfix));
+ m_writer->emit("->typeSize)");
+ return true;
+ }
+ case kIROp_Copy:
+ {
+ m_writer->emit("memcpy(");
+ emitOperand(inst->getOperand(0), EmitOpInfo::get(EmitOp::General));
+ m_writer->emit(", ");
+ emitOperand(inst->getOperand(1), EmitOpInfo::get(EmitOp::General));
+ m_writer->emit(", ");
+ emitOperand(inst->getOperand(2), EmitOpInfo::get(EmitOp::Postfix));
+ m_writer->emit("->typeSize)");
+ return true;
+ }
}
}
diff --git a/source/slang/slang-emit-cpp.h b/source/slang/slang-emit-cpp.h
index 5a18686ec..13f99c19b 100644
--- a/source/slang/slang-emit-cpp.h
+++ b/source/slang/slang-emit-cpp.h
@@ -67,6 +67,7 @@ protected:
virtual void emitParamTypeImpl(IRType* type, String const& name) SLANG_OVERRIDE;
virtual void emitWitnessTable(IRWitnessTable* witnessTable) SLANG_OVERRIDE;
virtual void emitInterface(IRInterfaceType* interfaceType) SLANG_OVERRIDE;
+ virtual void emitRTTIObject(IRRTTIObject* rttiObject) SLANG_OVERRIDE;
virtual bool tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* varType) SLANG_OVERRIDE;
virtual void emitIntrinsicCallExprImpl(IRCall* inst, IRTargetIntrinsicDecoration* targetIntrinsic, EmitOpInfo const& inOuterPrec) SLANG_OVERRIDE;
diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h
index 5a96fd8e8..5cbf7f03b 100644
--- a/source/slang/slang-ir-inst-defs.h
+++ b/source/slang/slang-ir-inst-defs.h
@@ -26,6 +26,7 @@ INST(Nop, nop, 0, 0)
INST(StringType, String, 0, 0)
INST(RawPointerType, RawPointerType, 0, 0)
+ INST(RTTIPointerType, RTTIPointerType, 1, 0)
/* ArrayTypeBase */
INST(ArrayType, Array, 2, 0)
@@ -168,6 +169,7 @@ INST(StructType, struct, 0, PARENT)
INST(InterfaceType, interface, 0, 0)
INST(AssociatedType, associated_type, 0, 0)
INST(ThisType, this_type, 0, 0)
+INST(RTTIType, rtti_type, 0, 0)
// A TypeType-typed IRValue represents a IRType.
// It is used to represent a type parameter/argument in a generics.
@@ -224,6 +226,8 @@ INST(makeStruct, makeStruct, 0, 0)
INST(Call, call, 1, 0)
+INST(RTTIObject, rtti_object, 0, 0)
+INST(Alloca, alloca, 1, 0)
INST(WitnessTableEntry, witness_table_entry, 2, 0)
INST(InterfaceRequirementEntry, interface_req_entry, 2, 0)
@@ -234,6 +238,7 @@ INST(Var, var, 0, 0)
INST(Load, load, 1, 0)
INST(Store, store, 2, 0)
+INST(Copy, copy, 3, 0)
INST(FieldExtract, get_field, 2, 0)
INST(FieldAddress, get_field_addr, 2, 0)
@@ -527,6 +532,10 @@ INST(HighLevelDeclDecoration, highLevelDecl, 1, 0)
/* LinkageDecoration */
INST(ImportDecoration, import, 1, 0)
INST(ExportDecoration, export, 1, 0)
+
+ /* Decorations for RTTI objects */
+ INST(RTTITypeSizeDecoration, RTTI_typeSize, 1, 0)
+
INST_RANGE(LinkageDecoration, ImportDecoration, ExportDecoration)
INST(SemanticDecoration, semantic, 2, 0)
diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h
index 93e709c45..553992406 100644
--- a/source/slang/slang-ir-insts.h
+++ b/source/slang/slang-ir-insts.h
@@ -143,6 +143,19 @@ struct IRNameHintDecoration : IRDecoration
}
};
+/// A decoration on a RTTIObject providing type size information.
+struct IRRTTITypeSizeDecoration : IRDecoration
+{
+ enum { kOp = kIROp_RTTITypeSizeDecoration };
+ IR_LEAF_ISA(RTTITypeSizeDecoration)
+
+ IRIntLit* getTypeSizeOperand() { return cast<IRIntLit>(getOperand(0)); }
+ IRIntegerValue getTypeSize()
+ {
+ return getTypeSizeOperand()->getValue();
+ }
+};
+
#define IR_SIMPLE_DECORATION(NAME) \
struct IR##NAME : IRDecoration \
{ \
@@ -420,6 +433,26 @@ struct IRLookupWitnessTable : IRInst
IRUse interfaceType;
};
+/// Allocates space from local stack.
+///
+struct IRAlloca : IRInst
+{
+ IR_LEAF_ISA(Alloca)
+
+ IRInst* getAllocSize() { return getOperand(0); }
+};
+
+/// Copies `size` bytes from `src` to `dst`.
+///
+struct IRCopy : IRInst
+{
+ IR_LEAF_ISA(Copy)
+
+ IRInst* getDst() { return getOperand(0); }
+ IRInst* getSrc() { return getOperand(1); }
+ IRInst* getSize() { return getOperand(2); }
+};
+
// Layout decorations
/// A decoration that marks a field key as having been associated
@@ -1122,12 +1155,14 @@ struct IRCall : IRInst
struct IRLoad : IRInst
{
IRUse ptr;
+ IR_LEAF_ISA(Load)
};
struct IRStore : IRInst
{
IRUse ptr;
IRUse val;
+ IR_LEAF_ISA(Store)
};
struct IRFieldExtract : IRInst
@@ -1137,6 +1172,8 @@ struct IRFieldExtract : IRInst
IRInst* getBase() { return base.get(); }
IRInst* getField() { return field.get(); }
+ IR_LEAF_ISA(FieldExtract)
+
};
struct IRFieldAddress : IRInst
@@ -1146,6 +1183,8 @@ struct IRFieldAddress : IRInst
IRInst* getBase() { return base.get(); }
IRInst* getField() { return field.get(); }
+ IR_LEAF_ISA(FieldAddress)
+
};
struct IRGetAddress : IRInst
@@ -1439,6 +1478,16 @@ struct IRWitnessTable : IRInst
IR_LEAF_ISA(WitnessTable)
};
+/// Represents an RTTI object.
+/// An IRRTTIObject has 1 operand, specifying the type
+/// this RTTI object provides info for.
+/// All type info are encapsualted as `IRRTTI*Decoration`s attached
+/// to the object.
+struct IRRTTIObject : IRInst
+{
+ IR_LEAF_ISA(RTTIObject)
+};
+
// An instruction that yields an undefined value.
//
// Note that we make this an instruction rather than a value,
@@ -1574,6 +1623,8 @@ struct IRBuilder
IRAssociatedType* getAssociatedType();
IRThisType* getThisType();
IRRawPointerType* getRawPointerType();
+ IRRTTIPointerType* getRTTIPointerType(IRInst* rttiPtr);
+ IRRTTIType* getRTTIType();
IRBasicBlockType* getBasicBlockType();
@@ -1681,6 +1732,10 @@ struct IRBuilder
IRInst* witnessTableVal,
IRInst* interfaceMethodVal);
+ IRInst* emitAlloca(IRInst* type, IRInst* rttiObjPtr);
+
+ IRInst* emitCopy(IRInst* dst, IRInst* src, IRInst* rttiObjPtr);
+
IRInst* emitCallInst(
IRType* type,
IRInst* func,
@@ -1712,6 +1767,9 @@ struct IRBuilder
UInt argCount,
IRInst* const* args);
+ // Creates an RTTI object. Result is of `IRRTTIType`.
+ IRInst* emitMakeRTTIObject(IRInst* typeInst);
+
IRInst* emitMakeVector(
IRType* type,
UInt argCount,
@@ -2243,6 +2301,11 @@ struct IRBuilder
{
addDecoration(inst, kIROp_FormatDecoration, format);
}
+
+ void addRTTITypeSizeDecoration(IRInst* inst, IRIntegerValue value)
+ {
+ addDecoration(inst, kIROp_RTTITypeSizeDecoration, getIntValue(getIntType(), value));
+ }
};
void addHoistableInst(
diff --git a/source/slang/slang-ir-lower-generics.cpp b/source/slang/slang-ir-lower-generics.cpp
index 774836e29..8b003f854 100644
--- a/source/slang/slang-ir-lower-generics.cpp
+++ b/source/slang/slang-ir-lower-generics.cpp
@@ -2,6 +2,7 @@
#include "slang-ir-lower-generics.h"
#include "slang-ir.h"
+#include "slang-ir-layout.h"
#include "slang-ir-clone.h"
#include "slang-ir-insts.h"
@@ -15,6 +16,9 @@ namespace Slang
// we are processing.
IRModule* module;
+ // RTTI objects for each type used to call a generic function.
+ Dictionary<IRInst*, IRInst*> mapTypeToRTTIObject;
+
Dictionary<IRInst*, IRInst*> loweredGenericFunctions;
HashSet<IRInterfaceType*> loweredInterfaceTypes;
@@ -48,7 +52,7 @@ namespace Slang
bool isPolymorphicType(IRInst* typeInst)
{
- if (as<IRParam>(typeInst) && as<IRTypeType>(typeInst->getFullType()))
+ if (as<IRParam>(typeInst) && as<IRTypeType>(typeInst->getDataType()))
return true;
switch (typeInst->op)
{
@@ -57,8 +61,26 @@ namespace Slang
case kIROp_InterfaceType:
return true;
default:
- return false;
+ break;
+ }
+ if (auto ptrType = as<IRPtrTypeBase>(typeInst))
+ {
+ return isPolymorphicType(ptrType->getValueType());
}
+ return false;
+ }
+
+ IRInst* lowerParameterType(IRBuilder* builder, IRInst* paramType)
+ {
+ if (paramType && paramType->op == kIROp_TypeType)
+ {
+ return builder->getPtrType(builder->getRTTIType());
+ }
+ if (isPolymorphicType(paramType))
+ {
+ return builder->getRawPointerType();
+ }
+ return paramType;
}
IRInst* lowerGenericFunction(IRInst* genericValue)
@@ -79,14 +101,27 @@ namespace Slang
IRBuilder builder;
builder.sharedBuilder = &sharedBuilderStorage;
builder.setInsertBefore(genericParent);
- auto loweredFunc = cloneInstAndOperands(&cloneEnv, &builder, func);
+ auto loweredFunc = cast<IRFunc>(cloneInstAndOperands(&cloneEnv, &builder, func));
loweredFunc->setFullType(lowerGenericFuncType(&builder, cast<IRGeneric>(genericParent->getFullType())));
List<IRInst*> clonedParams;
- for (auto genericParam : genericParent->getParams())
+ for (auto genericChild : genericParent->getFirstBlock()->getChildren())
{
- auto clonedParam = cloneInst(&cloneEnv, &builder, genericParam);
- cloneEnv.mapOldValToNew[genericParam] = clonedParam;
- clonedParams.add(clonedParam);
+ if (genericChild == func)
+ continue;
+ if (genericChild->op == kIROp_ReturnVal)
+ continue;
+ // Process all generic parameters and local type definitions.
+ auto clonedChild = cloneInst(&cloneEnv, &builder, genericChild);
+ if (clonedChild->op == kIROp_Param)
+ {
+ auto paramType = clonedChild->getFullType();
+ auto loweredParamType = lowerParameterType(&builder, paramType);
+ if (loweredParamType != paramType)
+ {
+ clonedChild->setFullType((IRType*)loweredParamType);
+ }
+ clonedParams.add(clonedChild);
+ }
}
cloneInstDecorationsAndChildren(&cloneEnv, &sharedBuilderStorage, func, loweredFunc);
auto block = as<IRBlock>(loweredFunc->getFirstChild());
@@ -95,79 +130,73 @@ namespace Slang
param->removeFromParent();
block->addParam(as<IRParam>(param));
}
- loweredGenericFunctions[genericValue] = loweredFunc;
- // Turn generic parameters into void pointers.
- for (auto param : cast<IRFunc>(loweredFunc)->getParams())
+ // Lower generic typed parameters into RTTIPointers.
+ auto firstInst = loweredFunc->getFirstOrdinaryInst();
+ builder.setInsertBefore(firstInst);
+
+ for (IRInst* param = loweredFunc->getFirstParam();
+ param && param->op == kIROp_Param;
+ param = param->getNextInst())
{
- if (isPolymorphicType(param->getFullType()))
+ // Generic typed parameters have a type that is a param itself.
+ if (auto rttiParam = as<IRParam>(param->getDataType()))
{
+ SLANG_ASSERT(isPointerOfType(rttiParam->getDataType(), kIROp_RTTIType));
+ // Lower into a function parameter of raw pointer type.
param->setFullType(builder.getRawPointerType());
+ auto newType = builder.getRTTIPointerType(rttiParam);
+ // Cast the raw pointer parameter into a RTTIPointer with RTTI info from the type parameter.
+ auto typedPtr = builder.emitBitCast(newType, param);
+ // Replace all uses of param with typePtr.
+ param->replaceUsesWith(typedPtr);
+ typedPtr->setOperand(0, param);
}
}
+ loweredGenericFunctions[genericValue] = loweredFunc;
addToWorkList(loweredFunc);
return loweredFunc;
}
IRType* lowerGenericFuncType(IRBuilder* builder, IRGeneric* genericVal)
{
- List<IRInst*> genericParamTypes;
+ ShortList<IRInst*> genericParamTypes;
for (auto genericParam : genericVal->getParams())
{
- if (isPolymorphicType(genericParam->getFullType()))
- {
- genericParamTypes.add(builder->getRawPointerType());
- }
- else
- {
- genericParamTypes.add(genericParam->getFullType());
- }
+ genericParamTypes.add(lowerParameterType(builder, genericParam->getFullType()));
}
auto innerType = (IRFuncType*)lowerFuncType(
builder,
cast<IRFuncType>(findGenericReturnVal(genericVal)),
- genericParamTypes.getCount());
-
- for (int i = 0; i < genericParamTypes.getCount(); i++)
- {
- innerType->setOperand(
- innerType->getOperandCount() - genericParamTypes.getCount() + i,
- genericParamTypes[i]);
- }
+ genericParamTypes.getArrayView().arrayView);
return innerType;
}
- IRType* lowerFuncType(IRBuilder* builder, IRFuncType* funcType, UInt additionalParamCount = 0)
+ IRType* lowerFuncType(IRBuilder* builder, IRFuncType* funcType, ArrayView<IRInst*> additionalParams)
{
List<IRInst*> newOperands;
bool translated = false;
for (UInt i = 0; i < funcType->getOperandCount(); i++)
{
auto paramType = funcType->getOperand(i);
- if (isPolymorphicType(paramType))
+ if (paramType->op == kIROp_Specialize)
{
newOperands.add(builder->getRawPointerType());
translated = true;
}
- else if (paramType->op == kIROp_Specialize)
- {
- // TODO: handle static specialized type here.
- // For now treat all specialized types as dynamic.
- // In the future, we need to turn things like Array<IDynamic> into Array<void*>.
- newOperands.add(builder->getRawPointerType());
- translated = true;
- }
else
{
- newOperands.add(paramType);
+ auto loweredParamType = lowerParameterType(builder, paramType);
+ translated = translated || (loweredParamType != paramType);
+ newOperands.add(loweredParamType);
}
}
- if (!translated && additionalParamCount == 0)
+ if (!translated && additionalParams.getCount() == 0)
return funcType;
- for (UInt i = 0; i < additionalParamCount; i++)
+ for (Index i = 0; i < additionalParams.getCount(); i++)
{
- newOperands.add(nullptr);
+ newOperands.add(additionalParams[i]);
}
auto newFuncType = builder->getFuncType(
newOperands.getCount() - 1,
@@ -195,7 +224,7 @@ namespace Slang
{
if (auto funcType = as<IRFuncType>(entry->getRequirementVal()))
{
- entry->setRequirementVal(lowerFuncType(&builder, funcType));
+ entry->setRequirementVal(lowerFuncType(&builder, funcType, ArrayView<IRInst*>()));
}
else if (auto genericFuncType = as<IRGeneric>(entry->getRequirementVal()))
{
@@ -208,6 +237,151 @@ namespace Slang
return interfaceType;
}
+ void processVarInst(IRInst* varInst)
+ {
+ // We process only var declarations that have type
+ // `Ptr<IRParam>`.
+ // Due to the processing of `lowerGenericFunction`,
+ // A local variable of generic type now appears as
+ // `var X:Ptr<irParam:Ptr<RTTIType>>`
+ // We match this pattern and turn this inst into
+ // `X:RawPtr = alloca(rtti_extract_size(irParam))`
+ auto varTypeInst = varInst->getDataType();
+ if (!varTypeInst)
+ return;
+ auto ptrType = as<IRPtrType>(varTypeInst);
+ if (!ptrType)
+ return;
+
+ // `varTypeParam` represents a pointer to the RTTI object.
+ auto varTypeParam = ptrType->getValueType();
+ if (varTypeParam->op != kIROp_Param)
+ return;
+ if (!varTypeParam->getDataType())
+ return;
+ if (varTypeParam->getDataType()->op != kIROp_PtrType)
+ return;
+ if (as<IRPtrType>(varTypeParam->getDataType())->getValueType()->op != kIROp_RTTIType)
+ return;
+
+
+ // A local variable of generic type has a type that is an IRParam.
+ // This parameter represents the RTTI that tells us the size of the type.
+ // We need to transform the variable into an `alloca` call to allocate its
+ // space based on the provided RTTI object.
+
+ // Initialize IRBuilder for emitting instructions.
+ IRBuilder builderStorage;
+ auto builder = &builderStorage;
+ builder->sharedBuilder = &sharedBuilderStorage;
+ builder->setInsertBefore(varInst);
+
+ // The result of `alloca` is an RTTIPointer(rttiObject).
+ auto type = builder->getRTTIPointerType(varTypeParam);
+ auto newVarInst = builder->emitAlloca(type, varTypeParam);
+ varInst->replaceUsesWith(newVarInst);
+ varInst->removeAndDeallocate();
+ }
+
+ void processStoreInst(IRStore* storeInst)
+ {
+ auto rttiType = as<IRRTTIPointerType>(storeInst->ptr.get()->getDataType());
+ if (!rttiType)
+ return;
+ // All stores of generic typed variables needs to be translated
+ // to `IRCopy`s.
+ auto valPtr = storeInst->val.get();
+ if (valPtr->getDataType()->op == kIROp_RTTIPointerType)
+ {
+ // If `value` of the store is from another generic variable, it should
+ // have already been replaced with the pointer to that variable by now.
+ // So we don't need to do anything here.
+ }
+ else
+ {
+ // If value does not come from another generic variable, then it must be
+ // a param. In this case, the parameter is a bitCast of the parameter to an
+ // RTTIPointer type, so we just use the original parameter pointer and get
+ // rid of the bitcast.
+ SLANG_ASSERT(valPtr->op == kIROp_BitCast);
+ valPtr = valPtr->getOperand(0);
+ SLANG_ASSERT(valPtr->op == kIROp_Param);
+ }
+ IRBuilder builderStorage;
+ auto builder = &builderStorage;
+ builder->sharedBuilder = &sharedBuilderStorage;
+ builder->setInsertBefore(storeInst);
+ auto copy = builder->emitCopy(
+ storeInst->ptr.get(),
+ valPtr,
+ rttiType->getRTTIOperand());
+ storeInst->replaceUsesWith(copy);
+ storeInst->removeAndDeallocate();
+ }
+
+ void processLoadInst(IRLoad* loadInst)
+ {
+ auto rttiType = as<IRRTTIPointerType>(loadInst->ptr.get()->getDataType());
+ if (!rttiType)
+ return;
+ // There are only two possible uses of a load(genericVar):
+ // 1. store(x, load(genVar)), which will be handled by processStoreInst.
+ // 2. call(f, load(genVar)) when calling a generic function or a member function
+ // via an interface witness lookup. In this case, we need to replace with
+ // just `genVar`, since that function has already been lowered to take
+ // raw pointers.
+ // In both cases, we can simply replace the use side with a pointer instead
+ // and never need to represent a "value" typed object explicitly.
+ // However, to preserve the ordering, we must make a copy of every load so
+ // we don't change the meaning of the code if there are `store`s between the
+ // `load` and the use site.
+
+ IRBuilder builderStorage;
+ auto builder = &builderStorage;
+ builder->sharedBuilder = &sharedBuilderStorage;
+ builder->setInsertBefore(loadInst);
+
+ // Allocate a copy of the value.
+ auto allocaInst = builder->emitAlloca(rttiType, rttiType->getRTTIOperand());
+ builder->emitCopy(allocaInst, loadInst->ptr.get(), rttiType->getRTTIOperand());
+
+ // Here we replace all uses of load to just the pointer to the copy.
+ // After this, all arguments in `call`s will be in its correct form.
+ // All `store`s will become `store(x, genVar)`, and still need
+ // to be translated into another `copy`, we leave that step when we get to
+ // process the `store` inst.
+ loadInst->replaceUsesWith(allocaInst);
+ loadInst->removeAndDeallocate();
+ }
+
+ // Emits an IRRTTIObject containing type information for a given type.
+ IRInst* maybeEmitRTTIObject(IRInst* typeInst)
+ {
+ IRInst* result = nullptr;
+ if (mapTypeToRTTIObject.TryGetValue(typeInst, result))
+ return result;
+ IRBuilder builderStorage;
+ auto builder = &builderStorage;
+ builder->sharedBuilder = &sharedBuilderStorage;
+ builder->setInsertBefore(typeInst->next);
+
+ result = builder->emitMakeRTTIObject(typeInst);
+
+ // For now the only type info we encapsualte is type size.
+ IRSizeAndAlignment sizeAndAlignment;
+ getNaturalSizeAndAlignment((IRType*)typeInst, &sizeAndAlignment);
+ builder->addRTTITypeSizeDecoration(result, sizeAndAlignment.size);
+
+ // Give a name to the rtti object.
+ if (auto exportDecoration = typeInst->findDecoration<IRExportDecoration>())
+ {
+ String rttiObjName = String(exportDecoration->getMangledName()) + "_rtti";
+ builder->addExportDecoration(result, rttiObjName.getUnownedSlice());
+ }
+ mapTypeToRTTIObject[typeInst] = result;
+ return result;
+ }
+
void processInst(IRInst* inst)
{
if (auto callInst = as<IRCall>(inst))
@@ -225,7 +399,7 @@ namespace Slang
// The callee is a result of witness table lookup, we will only
// translate the call.
IRInst* callee = nullptr;
- auto witnessTableType = cast<IRWitnessTableType>(interfaceLookup->getWitnessTable()->getFullType());
+ auto witnessTableType = cast<IRWitnessTableType>(interfaceLookup->getWitnessTable()->getDataType());
auto interfaceType = maybeLowerInterfaceType(cast<IRInterfaceType>(witnessTableType->getConformanceType()));
for (UInt i = 0; i < interfaceType->getOperandCount(); i++)
{
@@ -262,11 +436,11 @@ namespace Slang
for (UInt i = 0; i < callInst->getArgCount(); i++)
{
auto arg = callInst->getArg(i);
- if (paramTypes[i] == rawPtrType &&
- arg->getDataType() != rawPtrType)
+ if (as<IRRawPointerType>(paramTypes[i]) &&
+ !as<IRRawPointerType>(arg->getDataType()))
{
// We are calling a generic function that with an argument of
- // concrete type. We need to convert this argument o void*.
+ // concrete type. We need to convert this argument to void*.
// Ideally this should just be a GetElementAddress inst.
// However the current code emitting logic for this instruction
@@ -280,7 +454,34 @@ namespace Slang
args.add(arg);
}
for (UInt i = 0; i < specializeInst->getArgCount(); i++)
- args.add(specializeInst->getArg(i));
+ {
+ auto arg = specializeInst->getArg(i);
+ // Translate Type arguments into RTTI object.
+ if (as<IRType>(arg))
+ {
+ // We are using a simple type to specialize a callee.
+ // Generate RTTI for this type.
+ auto rttiObject = maybeEmitRTTIObject(arg);
+ arg = builder->emitGetAddress(
+ builder->getPtrType(builder->getRTTIType()),
+ rttiObject);
+ }
+ else if (arg->op == kIROp_Specialize)
+ {
+ // The type argument used to specialize a callee is itself a
+ // specialization of some generic type.
+ // TODO: generate RTTI object for specializations of generic types.
+ SLANG_UNIMPLEMENTED_X("RTTI object generation for generic types");
+ }
+ else if (arg->op == kIROp_RTTIObject)
+ {
+ // We are inside a generic function and using a generic parameter
+ // to specialize another callee. The generic parameter of the caller
+ // has already been translated into an RTTI object, so we just need
+ // to pass this object down.
+ }
+ args.add(arg);
+ }
auto newCall = builder->emitCallInst(callInst->getFullType(), loweredFunc, args);
callInst->replaceUsesWith(newCall);
callInst->removeAndDeallocate();
@@ -308,6 +509,18 @@ namespace Slang
{
maybeLowerInterfaceType(interfaceType);
}
+ else if (inst->op == kIROp_Var || inst->op == kIROp_undefined)
+ {
+ processVarInst(inst);
+ }
+ else if (inst->op == kIROp_Load)
+ {
+ processLoadInst(cast<IRLoad>(inst));
+ }
+ else if (inst->op == kIROp_Store)
+ {
+ processStoreInst(cast<IRStore>(inst));
+ }
}
void processModule()
diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp
index 25bf0f9f3..026df865d 100644
--- a/source/slang/slang-ir.cpp
+++ b/source/slang/slang-ir.cpp
@@ -634,6 +634,13 @@ namespace Slang
return entryBlock->getParams();
}
+ IRInst* IRGlobalValueWithParams::getFirstOrdinaryInst()
+ {
+ auto firstBlock = getFirstBlock();
+ if (!firstBlock)
+ return nullptr;
+ return firstBlock->getFirstOrdinaryInst();
+ }
// IRFunc
@@ -1936,7 +1943,7 @@ namespace Slang
keyInst.value.ptrVal = value;
return (IRPtrLit*) findOrEmitConstant(this, keyInst);
}
-
+
IRInst* IRBuilder::findOrEmitHoistableInst(
IRType* type,
IROp op,
@@ -2210,6 +2217,16 @@ namespace Slang
return (IRRawPointerType*)getType(kIROp_RawPointerType);
}
+ IRRTTIPointerType* IRBuilder::getRTTIPointerType(IRInst* rttiPtr)
+ {
+ return (IRRTTIPointerType*)getType(kIROp_RTTIPointerType, rttiPtr);
+ }
+
+ IRRTTIType* IRBuilder::getRTTIType()
+ {
+ return (IRRTTIType*)getType(kIROp_RTTIType);
+ }
+
IRBasicBlockType* IRBuilder::getBasicBlockType()
{
return (IRBasicBlockType*)getType(kIROp_BasicBlockType);
@@ -2537,6 +2554,32 @@ namespace Slang
return inst;
}
+ IRInst* IRBuilder::emitAlloca(IRInst* type, IRInst* rttiObjPtr)
+ {
+ auto inst = createInst<IRAlloca>(
+ this,
+ kIROp_Alloca,
+ (IRType*)type,
+ rttiObjPtr);
+
+ addInst(inst);
+ return inst;
+ }
+
+ IRInst* IRBuilder::emitCopy(IRInst* dst, IRInst* src, IRInst* rttiObjPtr)
+ {
+ IRInst* args[] = { dst, src, rttiObjPtr };
+ auto inst = createInst<IRCopy>(
+ this,
+ kIROp_Copy,
+ getVoidType(),
+ 3,
+ args);
+
+ addInst(inst);
+ return inst;
+ }
+
IRInst* IRBuilder::emitCallInst(
IRType* type,
IRInst* pFunc,
@@ -2600,6 +2643,17 @@ namespace Slang
return inst;
}
+ IRInst* IRBuilder::emitMakeRTTIObject(IRInst* typeInst)
+ {
+ auto inst = createInst<IRRTTIObject>(
+ this,
+ kIROp_RTTIObject,
+ getRTTIType(),
+ typeInst);
+ addInst(inst);
+ return inst;
+ }
+
IRInst* IRBuilder::emitMakeVector(
IRType* type,
UInt argCount,
@@ -5028,6 +5082,9 @@ namespace Slang
// All of the cases for "global values" are side-effect-free.
case kIROp_StructType:
case kIROp_StructField:
+ case kIROp_RTTIPointerType:
+ case kIROp_RTTIObject:
+ case kIROp_RTTIType:
case kIROp_Func:
case kIROp_Generic:
case kIROp_GlobalVar: // Note: the IRGlobalVar represents the *address*, so only a load/store would have side effects
@@ -5043,6 +5100,7 @@ namespace Slang
case kIROp_Nop:
case kIROp_Specialize:
case kIROp_lookup_interface_method:
+ case kIROp_getAddr:
case kIROp_Construct:
case kIROp_makeVector:
case kIROp_MakeMatrix:
@@ -5084,6 +5142,7 @@ namespace Slang
case kIROp_ExtractExistentialValue:
case kIROp_ExtractExistentialWitnessTable:
case kIROp_WrapExistential:
+ case kIROp_BitCast:
return false;
}
}
@@ -5253,4 +5312,15 @@ namespace Slang
builder->getConstExprRate(),
irValue->getDataType()));
}
+
+ bool isPointerOfType(IRInst* ptrType, IRInst* elementType)
+ {
+ return ptrType && ptrType->op == kIROp_PtrType && ptrType->getOperand(0) == elementType;
+ }
+ bool isPointerOfType(IRInst* ptrType, IROp opCode)
+ {
+ return ptrType && ptrType->op == kIROp_PtrType && ptrType->getOperand(0) &&
+ ptrType->getOperand(0)->op == opCode;
+ }
}
+
diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h
index a23d6cfa2..9792f0625 100644
--- a/source/slang/slang-ir.h
+++ b/source/slang/slang-ir.h
@@ -1109,11 +1109,21 @@ SIMPLE_IR_TYPE(OutType, OutTypeBase)
SIMPLE_IR_TYPE(InOutType, OutTypeBase)
SIMPLE_IR_TYPE(ExistentialBoxType, PtrTypeBase)
+ /// Represents a pointer to an object of unknown type.
struct IRRawPointerType : IRType
{
IR_LEAF_ISA(RawPointerType)
};
+ /// Represents a pointer to an object whose type is determined at runtime,
+ /// with type information available through `rttiOperand`.
+ ///
+struct IRRTTIPointerType : IRRawPointerType
+{
+ IRInst* getRTTIOperand() { return getOperand(0); }
+ IR_LEAF_ISA(RTTIPointerType)
+};
+
struct IRGlobalHashedStringLiterals : IRInst
{
IR_LEAF_ISA(GlobalHashedStringLiterals)
@@ -1229,6 +1239,12 @@ struct IRTypeType : IRType
IR_LEAF_ISA(TypeType);
};
+ /// Represents the IR type for an `IRRTTIObject`.
+struct IRRTTIType : IRType
+{
+ IR_LEAF_ISA(RTTIType);
+};
+
struct IRWitnessTableType : IRType
{
IRInst* getConformanceType()
@@ -1276,6 +1292,7 @@ struct IRGlobalValueWithParams : IRGlobalValueWithCode
IRParam* getFirstParam();
IRParam* getLastParam();
IRInstList<IRParam> getParams();
+ IRInst* getFirstOrdinaryInst();
IR_PARENT_ISA(GlobalValueWithParams)
};
@@ -1412,6 +1429,12 @@ IRInst* createEmptyInstWithSize(
/// True if the op type can be handled 'nominally' meaning that pointer identity is applicable.
bool isNominalOp(IROp op);
+ // True if ptrType is a pointer type to elementType
+bool isPointerOfType(IRInst* ptrType, IRInst* elementType);
+
+ // True if ptrType is a pointer type to a type of opCode
+bool isPointerOfType(IRInst* ptrType, IROp opCode);
+
}
#endif
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index adebefc8b..cfed09dd0 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -2198,7 +2198,13 @@ Type* getThisParamTypeForContainer(
IRGenContext* context,
DeclRef<Decl> parentDeclRef)
{
- if( auto aggTypeDeclRef = parentDeclRef.as<AggTypeDecl>() )
+ if (auto interfaceDeclRef = parentDeclRef.as<InterfaceDecl>())
+ {
+ auto thisType = context->astBuilder->create<ThisType>();
+ thisType->interfaceDeclRef = interfaceDeclRef;
+ return thisType;
+ }
+ else if( auto aggTypeDeclRef = parentDeclRef.as<AggTypeDecl>() )
{
return DeclRefType::create(context->astBuilder, aggTypeDeclRef);
}