summaryrefslogtreecommitdiff
path: root/source/slang/slang-ir-lower-generics.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-ir-lower-generics.cpp')
-rw-r--r--source/slang/slang-ir-lower-generics.cpp309
1 files changed, 261 insertions, 48 deletions
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()