diff options
| author | Yong He <yonghe@outlook.com> | 2020-07-10 09:13:50 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-07-10 09:13:50 -0700 |
| commit | 2503280122c7ac54cc3e42e2e54efff1d002d126 (patch) | |
| tree | f16d703731801fae2169aa500b6d4cfad2d6fae8 /source | |
| parent | a5a67aae981cb54d535089b167d3edcc3a3a2e29 (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.cpp | 15 | ||||
| -rw-r--r-- | source/slang/slang-emit-c-like.h | 1 | ||||
| -rw-r--r-- | source/slang/slang-emit-cpp.cpp | 76 | ||||
| -rw-r--r-- | source/slang/slang-emit-cpp.h | 1 | ||||
| -rw-r--r-- | source/slang/slang-ir-inst-defs.h | 9 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.h | 63 | ||||
| -rw-r--r-- | source/slang/slang-ir-lower-generics.cpp | 309 | ||||
| -rw-r--r-- | source/slang/slang-ir.cpp | 72 | ||||
| -rw-r--r-- | source/slang/slang-ir.h | 23 | ||||
| -rw-r--r-- | source/slang/slang-lower-to-ir.cpp | 8 |
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); } |
