diff options
| author | Yong He <yonghe@outlook.com> | 2020-08-05 10:32:52 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-08-05 10:32:52 -0700 |
| commit | 6fb2aa70a2681bffbac7e8de67e9598105389945 (patch) | |
| tree | f89cb433e7498bf74292bf460096f1129ef3ac13 /source | |
| parent | 092337a67e7ef8ec108cab9cb6679e59bb2ff791 (diff) | |
`AnyValue` based dynamic dispatch code gen (#1477)
* AnyValue based dynamic code gen
* Fix aarch64 build error
Diffstat (limited to 'source')
24 files changed, 594 insertions, 436 deletions
diff --git a/source/slang/slang-check-modifier.cpp b/source/slang/slang-check-modifier.cpp index d8877b9d1..9734df4db 100644 --- a/source/slang/slang-check-modifier.cpp +++ b/source/slang/slang-check-modifier.cpp @@ -284,6 +284,31 @@ namespace Slang numThreadsAttr->y = values[1]; numThreadsAttr->z = values[2]; } + else if (auto anyValueSizeAttr = as<AnyValueSizeAttribute>(attr)) + { + // This case handles GLSL-oriented layout attributes + // that take a single integer argument. + + if (attr->args.getCount() != 1) + { + return false; + } + + auto value = checkConstantIntVal(attr->args[0]); + if (value == nullptr) + { + return false; + } + + const IRIntegerValue kMaxAnyValueSize = 0x7FFF; + if (value->value > kMaxAnyValueSize) + { + getSink()->diagnose(anyValueSizeAttr->loc, Diagnostics::anyValueSizeExceedsLimit, kMaxAnyValueSize); + return false; + } + + anyValueSizeAttr->size = int32_t(value->value); + } else if (auto bindingAttr = as<GLSLBindingAttribute>(attr)) { // This must be vk::binding or gl::binding (as specified in core.meta.slang under vk_binding/gl_binding) diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index 4ef2aa7be..ed224c30b 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -288,6 +288,7 @@ DIAGNOSTIC(31102, Error, nonPositiveNumThreads, "expected a positive integer in DIAGNOSTIC(31120, Error, invalidAttributeTarget, "invalid syntax target for user defined attribute") +DIAGNOSTIC(31121, Error, anyValueSizeExceedsLimit, "'anyValueSize' cannot exceed $0") // Enums DIAGNOSTIC(32000, Error, invalidEnumTagType, "invalid tag type for 'enum': '$0'") @@ -482,6 +483,12 @@ DIAGNOSTIC(40007, Internal, irValidationFailed, "IR validation failed: $0") DIAGNOSTIC(40008, Error, invalidLValueForRefParameter, "the form of this l-value argument is not valid for a `ref` parameter") +DIAGNOSTIC(40009, Error, dynamicInterfaceLacksAnyValueSizeAttribute, "interface '$0' is being used in dynamic dispatch code but has no [anyValueSize] attribute defined.") +DIAGNOSTIC(40010, Note, seeInterfaceUsage, "see usage of interface '$0'.") + +DIAGNOSTIC(40011, Error, unconstrainedGenericParameterNotAllowedInDynamicFunction, "unconstrained generic paramter '$0' is not allowed in a dynamic function.") + + // 41000 - IR-level validation issues DIAGNOSTIC(41000, Warning, unreachableCode, "unreachable code detected") diff --git a/source/slang/slang-diagnostics.h b/source/slang/slang-diagnostics.h index caaba4033..deec57b7c 100644 --- a/source/slang/slang-diagnostics.h +++ b/source/slang/slang-diagnostics.h @@ -92,7 +92,9 @@ namespace Slang void printDiagnosticArg(StringBuilder& sb, TokenType tokenType); void printDiagnosticArg(StringBuilder& sb, Token const& token); - + + struct IRInst; + void printDiagnosticArg(StringBuilder& sb, IRInst* irObject); template<typename T> void printDiagnosticArg(StringBuilder& sb, RefPtr<T> ptr) diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index a7637a63e..4ed7c2fac 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -2403,6 +2403,28 @@ void CLikeSourceEmitter::defaultEmitInstExpr(IRInst* inst, const EmitOpInfo& inO m_writer->emit(")"); } break; + case kIROp_PackAnyValue: + { + m_writer->emit("packAnyValue<"); + m_writer->emit(getIntVal(cast<IRAnyValueType>(inst->getDataType())->getSize())); + m_writer->emit(","); + emitType(inst->getOperand(0)->getDataType()); + m_writer->emit(">("); + emitOperand(inst->getOperand(0), getInfo(EmitOp::General)); + m_writer->emit(")"); + break; + } + case kIROp_UnpackAnyValue: + { + m_writer->emit("unpackAnyValue<"); + m_writer->emit(getIntVal(cast<IRAnyValueType>(inst->getOperand(0)->getDataType())->getSize())); + m_writer->emit(","); + emitType(inst->getDataType()); + m_writer->emit(">("); + emitOperand(inst->getOperand(0), getInfo(EmitOp::General)); + m_writer->emit(")"); + break; + } default: diagnoseUnhandledInst(inst); break; diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp index bac71f1d8..ebb6bda2d 100644 --- a/source/slang/slang-emit-cpp.cpp +++ b/source/slang/slang-emit-cpp.cpp @@ -509,6 +509,14 @@ SlangResult CPPSourceEmitter::calcTypeName(IRType* type, CodeGenTarget target, S out << "void*"; return SLANG_OK; } + case kIROp_AnyValueType: + { + out << "AnyValue<"; + auto anyValueType = static_cast<IRAnyValueType*>(type); + out << getIntVal(anyValueType->getSize()); + out << ">"; + return SLANG_OK; + } case kIROp_ConstantBufferType: case kIROp_ParameterBlockType: { diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index af01e4b0d..31ff3c4f9 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -311,12 +311,18 @@ Result linkAndOptimizeIR( // generics / interface types to ordinary functions and types using // function pointers. if (compileRequest->allowDynamicCode) - lowerGenerics(irModule); + { + lowerGenerics(irModule, sink); + dumpIRIfEnabled(compileRequest, irModule, "LOWER-GENERICS"); + } break; default: break; } + if (sink->getErrorCount() != 0) + return SLANG_FAIL; + // TODO(DG): There are multiple DCE steps here, which need to be changed // so that they don't just throw out any non-entry point code // Debugging code for IR transformations... @@ -775,13 +781,13 @@ SlangResult emitEntryPointsSourceFromIR( break; } - linkAndOptimizeIR( + SLANG_RETURN_ON_FAIL(linkAndOptimizeIR( compileRequest, entryPointIndices, target, targetRequest, linkingAndOptimizationOptions, - linkedIR); + linkedIR)); auto irModule = linkedIR.module; @@ -862,13 +868,13 @@ SlangResult emitSPIRVForEntryPointsDirectly( // Outside because we want to keep IR in scope whilst we are processing emits LinkedIR linkedIR; LinkingAndOptimizationOptions linkingAndOptimizationOptions; - linkAndOptimizeIR( + SLANG_RETURN_ON_FAIL(linkAndOptimizeIR( compileRequest, entryPointIndices, targetRequest->getTarget(), targetRequest, linkingAndOptimizationOptions, - linkedIR); + linkedIR)); auto irModule = linkedIR.module; auto irEntryPoints = linkedIR.entryPoints; diff --git a/source/slang/slang-ir-generics-lowering-context.cpp b/source/slang/slang-ir-generics-lowering-context.cpp index 0fcb85378..9edc81b6a 100644 --- a/source/slang/slang-ir-generics-lowering-context.cpp +++ b/source/slang/slang-ir-generics-lowering-context.cpp @@ -43,9 +43,8 @@ namespace Slang switch (typeInst->op) { case kIROp_TypeType: + case kIROp_TypeKind: return true; - case kIROp_lookup_interface_method: - return typeInst->getDataType()->op == kIROp_TypeKind; default: return false; } @@ -99,4 +98,108 @@ namespace Slang (*dict)[entry->getRequirementKey()] = entry->getRequirementVal(); } } + + IRType* SharedGenericsLoweringContext::lowerAssociatedType(IRBuilder* builder, IRInst* type) + { + if (type->op != kIROp_AssociatedType) + return (IRType*)type; + IRIntegerValue anyValueSize = kInvalidAnyValueSize; + for (UInt i = 0; i < type->getOperandCount(); i++) + { + anyValueSize = Math::Min( + anyValueSize, + getInterfaceAnyValueSize(type->getOperand(i), type->sourceLoc)); + } + if (anyValueSize == kInvalidAnyValueSize) + { + sink->diagnose(type->sourceLoc, Diagnostics::dynamicInterfaceLacksAnyValueSizeAttribute, type); + } + return builder->getAnyValueType(anyValueSize); + } + + IRType* SharedGenericsLoweringContext::lowerType(IRBuilder* builder, IRInst* paramType, const Dictionary<IRInst*, IRInst*>& typeMapping) + { + if (!paramType) + return nullptr; + + IRInst* resultType; + if (typeMapping.TryGetValue(paramType, resultType)) + return (IRType*)resultType; + + if (isTypeValue(paramType)) + { + return builder->getPtrType(builder->getRTTIType()); + } + + IRIntegerValue anyValueSize = kInvalidAnyValueSize; + switch (paramType->op) + { + case kIROp_WitnessTableType: + // Do not translate witness table type. + return (IRType*)paramType; + case kIROp_Param: + { + if (auto anyValueSizeDecor = paramType->findDecoration<IRTypeConstraintDecoration>()) + { + anyValueSize = getInterfaceAnyValueSize(anyValueSizeDecor->getConstraintType(), paramType->sourceLoc); + return builder->getAnyValueType(anyValueSize); + } + sink->diagnose(paramType, Diagnostics::unconstrainedGenericParameterNotAllowedInDynamicFunction, paramType); + return builder->getAnyValueType(kInvalidAnyValueSize); + } + case kIROp_ThisType: + anyValueSize = getInterfaceAnyValueSize( + cast<IRThisType>(paramType)->getConstraintType(), + paramType->sourceLoc); + return builder->getAnyValueType(anyValueSize); + case kIROp_AssociatedType: + { + return lowerAssociatedType(builder, paramType); + } + case kIROp_InterfaceType: + anyValueSize = getInterfaceAnyValueSize(paramType, paramType->sourceLoc); + return builder->getAnyValueType(anyValueSize); + case kIROp_lookup_interface_method: + { + auto lookupInterface = static_cast<IRLookupWitnessMethod*>(paramType); + auto interfaceType = cast<IRInterfaceType>(cast<IRWitnessTableType>( + lookupInterface->getWitnessTable()->getDataType())->getConformanceType()); + // Make sure we are looking up inside the original interface type (prior to lowering). + // Only in the original interface type will an associated type entry have an IRAssociatedType value. + // We need to extract AnyValueSize from this IRAssociatedType. + // In lowered interface type, that entry is lowered into an Ptr(RTTIType) and this info is lost. + mapLoweredInterfaceToOriginal.TryGetValue(interfaceType, interfaceType); + auto reqVal = findInterfaceRequirementVal( + interfaceType, + lookupInterface->getRequirementKey()); + SLANG_ASSERT(reqVal && reqVal->op == kIROp_AssociatedType); + return lowerType(builder, reqVal, typeMapping); + } + default: + { + bool translated = false; + List<IRInst*> loweredOperands; + for (UInt i = 0; i < paramType->getOperandCount(); i++) + { + loweredOperands.add(lowerType(builder, paramType->getOperand(i), typeMapping)); + if (loweredOperands.getLast() != paramType->getOperand(i)) + translated = true; + } + if (translated) + return builder->getType(paramType->op, loweredOperands.getCount(), loweredOperands.getBuffer()); + return (IRType*)paramType; + } + } + } + + IRIntegerValue SharedGenericsLoweringContext::getInterfaceAnyValueSize(IRInst* type, SourceLoc usageLocation) + { + if (auto decor = type->findDecoration<IRAnyValueSizeDecoration>()) + { + return decor->getSize(); + } + sink->diagnose(type->sourceLoc, Diagnostics::dynamicInterfaceLacksAnyValueSizeAttribute, type); + sink->diagnose(usageLocation, Diagnostics::seeInterfaceUsage, type); + return kInvalidAnyValueSize; + } } diff --git a/source/slang/slang-ir-generics-lowering-context.h b/source/slang/slang-ir-generics-lowering-context.h index 11aef34aa..8c23cad44 100644 --- a/source/slang/slang-ir-generics-lowering-context.h +++ b/source/slang/slang-ir-generics-lowering-context.h @@ -8,17 +8,23 @@ namespace Slang { struct IRModule; + constexpr IRIntegerValue kInvalidAnyValueSize = 0xFFFFFFFF; + struct SharedGenericsLoweringContext { // For convenience, we will keep a pointer to the module // we are processing. IRModule* module; + DiagnosticSink* sink; + // RTTI objects for each type used to call a generic function. Dictionary<IRInst*, IRInst*> mapTypeToRTTIObject; Dictionary<IRInst*, IRInst*> loweredGenericFunctions; - HashSet<IRInterfaceType*> loweredInterfaceTypes; + Dictionary<IRInterfaceType*, IRInterfaceType*> loweredInterfaceTypes; + Dictionary<IRInterfaceType*, IRInterfaceType*> mapLoweredInterfaceToOriginal; + // Dictionaries for interface type requirement key-value lookups. // Used by `findInterfaceRequirementVal`. @@ -55,6 +61,16 @@ namespace Slang // Emits an IRRTTIObject containing type information for a given type. IRInst* maybeEmitRTTIObject(IRInst* typeInst); + + IRIntegerValue getInterfaceAnyValueSize(IRInst* type, SourceLoc usageLocation); + IRType* lowerAssociatedType(IRBuilder* builder, IRInst* type); + + IRType* lowerType(IRBuilder* builder, IRInst* paramType, const Dictionary<IRInst*, IRInst*>& typeMapping); + + IRType* lowerType(IRBuilder* builder, IRInst* paramType) + { + return lowerType(builder, paramType, Dictionary<IRInst*, IRInst*>()); + } }; bool isPolymorphicType(IRInst* typeInst); @@ -62,4 +78,5 @@ namespace Slang // Returns true if typeInst represents a type and should be lowered into // Ptr(RTTIType). bool isTypeValue(IRInst* typeInst); + } diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index 887fcdb44..5b442354a 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -241,6 +241,9 @@ INST(Call, call, 1, 0) INST(RTTIObject, rtti_object, 0, 0) INST(Alloca, alloca, 1, 0) +INST(PackAnyValue, packAnyValue, 1, 0) +INST(UnpackAnyValue, unpackAnyValue, 1, 0) + INST(WitnessTableEntry, witness_table_entry, 2, 0) INST(InterfaceRequirementEntry, interface_req_entry, 2, 0) diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index 7f8e72121..cd2babfa6 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -480,6 +480,22 @@ struct IRCopy : IRInst IRInst* getSize() { return getOperand(2); } }; +/// Packs a value into an `AnyValue`. +/// Return type is `IRAnyValueType`. +struct IRPackAnyValue : IRInst +{ + IR_LEAF_ISA(PackAnyValue) + IRInst* getValue() { return getOperand(0); } +}; + +/// Unpacks a `AnyValue` value into a concrete type. +/// Operand must have `IRAnyValueType`. +struct IRUnpackAnyValue : IRInst +{ + IR_LEAF_ISA(UnpackAnyValue) + IRInst* getValue() { return getOperand(0); } +}; + // Layout decorations /// A decoration that marks a field key as having been associated @@ -1795,6 +1811,10 @@ struct IRBuilder IRInst* emitCopy(IRInst* dst, IRInst* src, IRInst* rttiObjPtr); + IRInst* emitPackAnyValue(IRType* type, IRInst* value); + + IRInst* emitUnpackAnyValue(IRType* type, IRInst* value); + IRInst* emitCallInst( IRType* type, IRInst* func, diff --git a/source/slang/slang-ir-lower-generic-call.cpp b/source/slang/slang-ir-lower-generic-call.cpp index 5095fadd3..22599922e 100644 --- a/source/slang/slang-ir-lower-generic-call.cpp +++ b/source/slang/slang-ir-lower-generic-call.cpp @@ -8,9 +8,84 @@ namespace Slang { SharedGenericsLoweringContext* sharedContext; + // Represents a work item for unpacking `inout` or `out` arguments after a generic call. + struct ArgumentUnpackWorkItem + { + // Concrete typed destination. + IRInst* dstArg = nullptr; + // Packed argument. + IRInst* packedArg = nullptr; + }; + + // Packs `arg` into a `IRAnyValue` if necessary, to make it feedable into the parameter. + // If `arg` represents a concrete typed variable passed in to a generic `out` parameter, + // this function indicates that it needs to be unpacked after the call by setting + // `unpackAfterCall`. + IRInst* maybePackArgument( + IRBuilder* builder, + IRType* paramType, + IRInst* arg, + ArgumentUnpackWorkItem& unpackAfterCall) + { + unpackAfterCall.dstArg = nullptr; + unpackAfterCall.packedArg = nullptr; + + // If either paramType or argType is a pointer type + // (because of `inout` or `out` modifiers), we extract + // the underlying value type first. + IRType* paramValType = paramType; + IRType* argValType = arg->getDataType(); + IRInst* argVal = arg; + bool isParamPointer = false; + if (auto ptrType = as<IRPtrTypeBase>(paramType)) + { + isParamPointer = true; + paramValType = ptrType->getValueType(); + } + bool isArgPointer = false; + auto argType = arg->getDataType(); + if (auto argPtrType = as<IRPtrTypeBase>(argType)) + { + isArgPointer = true; + argValType = argPtrType->getValueType(); + argVal = builder->emitLoad(arg); + } + + // Pack `arg` if the parameter expects AnyValue but + // `arg` is not an AnyValue. + if (as<IRAnyValueType>(paramValType) && !as<IRAnyValueType>(argValType)) + { + auto packedArgVal = builder->emitPackAnyValue(paramValType, argVal); + // if parameter expects an `out` pointer, store the packed val into a + // variable and pass in a pointer to that variable. + if (as<IRPtrTypeBase>(paramType)) + { + auto tempVar = builder->emitVar(paramValType); + builder->emitStore(tempVar, packedArgVal); + // tempVar needs to be unpacked into original var after the call. + unpackAfterCall.dstArg = arg; + unpackAfterCall.packedArg = tempVar; + return tempVar; + } + else + { + return packedArgVal; + } + } + return arg; + } + + IRInst* maybeUnpackValue(IRBuilder* builder, IRType* expectedType, IRType* actualType, IRInst* value) + { + if (as<IRAnyValueType>(actualType) && !as<IRAnyValueType>(expectedType)) + { + auto unpack = builder->emitUnpackAnyValue(expectedType, value); + return unpack; + } + return value; + } + // Translate `callInst` into a call of `newCallee`, and respect the new `funcType`. - // If `funcType` involve lowered generic parameters or return values, this function - // also translates the argument list to match with that. // If `newCallee` is a lowered generic function, `specializeInst` contains the type // arguments used to specialize the callee. void translateCallInst( @@ -28,41 +103,21 @@ namespace Slang builder->sharedBuilder = &sharedContext->sharedBuilderStorage; builder->setInsertBefore(callInst); + // Process the argument list of the call. + // For each argument, we test if it needs to be packed into an `AnyValue` for the + // call. For `out` and `inout` parameters, they may also need to be unpacked after + // the call, in which case we add such the argument to `argsToUnpack` so it can be + // processed after the new call inst is emitted. List<IRInst*> args; - - // Indicates whether the caller should allocate space for return value. - // If the lowered callee returns void and this call inst has a type that is not void, - // then we are calling a transformed function that expects caller allocated return value - // as the first argument. - bool shouldCallerAllocateReturnValue = (funcType->getResultType()->op == kIROp_VoidType && - callInst->getDataType() != funcType->getResultType()); - - IRVar* retVarInst = nullptr; - int startParamIndex = 0; - if (shouldCallerAllocateReturnValue) - { - // Declare a var for the return value. - retVarInst = builder->emitVar(callInst->getFullType()); - args.add(retVarInst); - startParamIndex = 1; - } - + List<ArgumentUnpackWorkItem> argsToUnpack; for (UInt i = 0; i < callInst->getArgCount(); i++) { auto arg = callInst->getArg(i); - if (as<IRRawPointerTypeBase>(paramTypes[i] + startParamIndex) && - !as<IRRawPointerTypeBase>(arg->getDataType()) && - !as<IRPtrTypeBase>(arg->getDataType())) - { - // We are calling a generic function that with an argument of - // some concrete value type. We need to convert this argument to void*. - // We do so by defining a local variable, store the SSA value - // in the variable, and use the pointer of this variable as argument. - auto localVar = builder->emitVar(arg->getDataType()); - builder->emitStore(localVar, arg); - arg = localVar; - } - args.add(arg); + ArgumentUnpackWorkItem unpackWorkItem; + auto newArg = maybePackArgument(builder, paramTypes[i], arg, unpackWorkItem); + args.add(newArg); + if (unpackWorkItem.packedArg) + argsToUnpack.add(unpackWorkItem); } if (specializeInst) { @@ -96,18 +151,23 @@ namespace Slang args.add(arg); } } - auto callInstType = retVarInst ? builder->getVoidType() : callInst->getFullType(); - auto newCall = builder->emitCallInst(callInstType, newCallee, args); - if (retVarInst) - { - auto loadInst = builder->emitLoad(retVarInst); - callInst->replaceUsesWith(loadInst); - } - else + + // If callee returns `AnyValue` but we are expecting a concrete value, unpack it. + auto calleeRetType = funcType->getResultType(); + auto newCall = builder->emitCallInst(calleeRetType, newCallee, args); + auto callInstType = callInst->getDataType(); + auto unpackInst = maybeUnpackValue(builder, callInstType, calleeRetType, newCall); + callInst->replaceUsesWith(unpackInst); + callInst->removeAndDeallocate(); + + // Unpack other `out` arguments. + for (auto& item : argsToUnpack) { - callInst->replaceUsesWith(newCall); + auto packedVal = builder->emitLoad(item.packedArg); + auto originalValType = cast<IRPtrTypeBase>(item.dstArg->getDataType())->getValueType(); + auto unpackedVal = builder->emitUnpackAnyValue(originalValType, packedVal); + builder->emitStore(item.dstArg, unpackedVal); } - callInst->removeAndDeallocate(); } void lowerCallToSpecializedFunc(IRCall* callInst, IRSpecialize* specializeInst) diff --git a/source/slang/slang-ir-lower-generic-function.cpp b/source/slang/slang-ir-lower-generic-function.cpp index 92f00c509..1d90625a4 100644 --- a/source/slang/slang-ir-lower-generic-function.cpp +++ b/source/slang/slang-ir-lower-generic-function.cpp @@ -15,18 +15,6 @@ namespace Slang struct GenericFunctionLoweringContext { SharedGenericsLoweringContext* sharedContext; - IRInst* lowerParameterType(IRBuilder* builder, IRInst* paramType) - { - if (isTypeValue(paramType)) - { - return builder->getPtrType(builder->getRTTIType()); - } - if (isPolymorphicType(paramType)) - { - return builder->getRawPointerType(); - } - return paramType; - } IRInst* lowerGenericFunction(IRInst* genericValue) { @@ -49,6 +37,7 @@ namespace Slang auto loweredFunc = cast<IRFunc>(cloneInstAndOperands(&cloneEnv, &builder, func)); loweredFunc->setFullType(lowerGenericFuncType(&builder, cast<IRGeneric>(genericParent->getFullType()))); List<IRInst*> clonedParams; + for (auto genericChild : genericParent->getFirstBlock()->getChildren()) { if (genericChild == func) @@ -60,7 +49,7 @@ namespace Slang if (clonedChild->op == kIROp_Param) { auto paramType = clonedChild->getFullType(); - auto loweredParamType = lowerParameterType(&builder, paramType); + auto loweredParamType = sharedContext->lowerType(&builder, paramType, Dictionary<IRInst*, IRInst*>()); if (loweredParamType != paramType) { clonedChild->setFullType((IRType*)loweredParamType); @@ -70,71 +59,15 @@ namespace Slang } cloneInstDecorationsAndChildren(&cloneEnv, &sharedContext->sharedBuilderStorage, func, loweredFunc); - // If the function returns a generic typed value, we need to turn it - // into an `out` parameter, since only the caller can allocate space - // for it. - auto oldFuncType = cast<IRFuncType>(func->getDataType()); - if (isPolymorphicType(oldFuncType->getResultType())) - { - builder.setInsertBefore(loweredFunc->getFirstBlock()->getFirstOrdinaryInst()); - // We defer creation of the returnVal parameter until we see the first - // `return` instruction, because we can only obtain the cloned return type - // of this function by checking the type of the cloned return inst. - IRParam* retValParam = nullptr; - // Translate all return insts to `store`s. - // Those `store`s will be processed and translated into `copy`s when we - // get to process them via workList. - for (auto bb : loweredFunc->getBlocks()) - { - auto retInst = as<IRReturnVal>(bb->getTerminator()); - if (!retInst) - continue; - if (!retValParam) - { - // Now we have the return type, emit the returnVal parameter. - // The type of this parameter is still not translated to RawPointer yet, - // and will be processed together with all the other existing parameters. - retValParam = builder.emitParamAtHead( - builder.getOutType(retInst->getVal()->getDataType())); - } - builder.setInsertBefore(retInst); - builder.emitStore(retValParam, retInst->getVal()); - builder.emitReturn(); - retInst->removeAndDeallocate(); - } - } - auto block = as<IRBlock>(loweredFunc->getFirstChild()); for (auto param : clonedParams) { param->removeFromParent(); block->addParam(as<IRParam>(param)); } - // Lower generic typed parameters into RTTIPointers. + // Lower generic typed parameters into AnyValueType. auto firstInst = loweredFunc->getFirstOrdinaryInst(); builder.setInsertBefore(firstInst); - - for (IRInst* param = loweredFunc->getFirstParam(); - param && param->op == kIROp_Param; - param = param->getNextInst()) - { - // Generic typed parameters have a type that is a param itself. - auto paramType = param->getDataType(); - if (auto ptrType = as<IRPtrTypeBase>(paramType)) - paramType = ptrType->getValueType(); - if (isPointerOfType(paramType->getDataType(), kIROp_RTTIType) || - paramType->op == kIROp_lookup_interface_method) - { - // Lower into a function parameter of raw pointer type. - param->setFullType(builder.getRawPointerType()); - auto newType = builder.getRTTIPointerType(paramType); - // 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); - } - } sharedContext->loweredGenericFunctions[genericValue] = loweredFunc; sharedContext->addToWorkList(loweredFunc); return loweredFunc; @@ -143,36 +76,38 @@ namespace Slang IRType* lowerGenericFuncType(IRBuilder* builder, IRGeneric* genericVal) { ShortList<IRInst*> genericParamTypes; + Dictionary<IRInst*, IRInst*> typeMapping; for (auto genericParam : genericVal->getParams()) { - genericParamTypes.add(lowerParameterType(builder, genericParam->getFullType())); + genericParamTypes.add(sharedContext->lowerType(builder, genericParam->getFullType(), Dictionary<IRInst*, IRInst*>())); + if (auto anyValueSizeDecor = genericParam->findDecoration<IRTypeConstraintDecoration>()) + { + auto anyValueSize = sharedContext->getInterfaceAnyValueSize(anyValueSizeDecor->getConstraintType(), genericParam->sourceLoc); + auto anyValueType = builder->getAnyValueType(anyValueSize); + typeMapping[genericParam] = anyValueType; + } } auto innerType = (IRFuncType*)lowerFuncType( builder, cast<IRFuncType>(findGenericReturnVal(genericVal)), + typeMapping, genericParamTypes.getArrayView().arrayView); return innerType; } - IRType* lowerFuncType(IRBuilder* builder, IRFuncType* funcType, ArrayView<IRInst*> additionalParams) + IRType* lowerFuncType(IRBuilder* builder, IRFuncType* funcType, + const Dictionary<IRInst*, IRInst*>& typeMapping, + ArrayView<IRInst*> additionalParams) { List<IRInst*> newOperands; bool translated = false; for (UInt i = 0; i < funcType->getOperandCount(); i++) { auto paramType = funcType->getOperand(i); - auto loweredParamType = lowerParameterType(builder, paramType); + auto loweredParamType = sharedContext->lowerType(builder, paramType, typeMapping); translated = translated || (loweredParamType != paramType); - if (translated && i == 0) - { - // We are translating the return value, this means that - // the return value must be passed explicitly via an `out` parameter. - // In this case, the new return value will be `void`, and the - // translated return value type will be the first parameter type; - newOperands.add(builder->getVoidType()); - } newOperands.add(loweredParamType); } if (!translated && additionalParams.getCount() == 0) @@ -193,9 +128,14 @@ namespace Slang IRInterfaceType* maybeLowerInterfaceType(IRInterfaceType* interfaceType) { - if (sharedContext->loweredInterfaceTypes.Contains(interfaceType)) + IRInterfaceType* loweredType = nullptr; + if (sharedContext->loweredInterfaceTypes.TryGetValue(interfaceType, loweredType)) + return loweredType; + if (sharedContext->mapLoweredInterfaceToOriginal.ContainsKey(interfaceType)) return interfaceType; + List<IRInterfaceRequirementEntry*> newEntries; + IRBuilder builder; builder.sharedBuilder = &sharedContext->sharedBuilderStorage; builder.setInsertBefore(interfaceType); @@ -205,23 +145,33 @@ namespace Slang { if (auto entry = as<IRInterfaceRequirementEntry>(interfaceType->getOperand(i))) { + IRInst* loweredVal = nullptr; if (auto funcType = as<IRFuncType>(entry->getRequirementVal())) { - entry->setRequirementVal(lowerFuncType(&builder, funcType, ArrayView<IRInst*>())); + loweredVal = lowerFuncType(&builder, funcType, Dictionary<IRInst*, IRInst*>(), ArrayView<IRInst*>()); } else if (auto genericFuncType = as<IRGeneric>(entry->getRequirementVal())) { - entry->setRequirementVal(lowerGenericFuncType(&builder, genericFuncType)); + loweredVal = lowerGenericFuncType(&builder, genericFuncType); } else if (entry->getRequirementVal()->op == kIROp_AssociatedType) { - entry->setRequirementVal(builder.getPtrType(builder.getRTTIType())); + loweredVal = builder.getPtrType(builder.getRTTIType()); } + else + { + loweredVal = entry->getRequirementVal(); + } + auto newEntry = builder.createInterfaceRequirementEntry(entry->getRequirementKey(), loweredVal); + newEntries.add(newEntry); } } - - sharedContext->loweredInterfaceTypes.Add(interfaceType); - return interfaceType; + loweredType = builder.createInterfaceType(newEntries.getCount(), (IRInst**)newEntries.getBuffer()); + interfaceType->transferDecorationsTo(loweredType); + interfaceType->replaceUsesWith(loweredType); + sharedContext->loweredInterfaceTypes.Add(interfaceType, loweredType); + sharedContext->mapLoweredInterfaceToOriginal[loweredType] = interfaceType; + return loweredType; } bool isTypeKindVal(IRInst* inst) diff --git a/source/slang/slang-ir-lower-generic-type.cpp b/source/slang/slang-ir-lower-generic-type.cpp new file mode 100644 index 000000000..c94a4c37e --- /dev/null +++ b/source/slang/slang-ir-lower-generic-type.cpp @@ -0,0 +1,70 @@ +// slang-ir-lower-generic-type.cpp +#include "slang-ir-lower-generic-type.h" + +#include "slang-ir-generics-lowering-context.h" +#include "slang-ir.h" +#include "slang-ir-clone.h" +#include "slang-ir-insts.h" + +namespace Slang +{ + // This is a subpass of generics lowering IR transformation. + // This pass lowers all generic/polymorphic types into IRAnyValueType. + struct GenericVarLoweringContext + { + SharedGenericsLoweringContext* sharedContext; + + void processInst(IRInst* inst) + { + IRBuilder builderStorage; + auto builder = &builderStorage; + builder->sharedBuilder = &sharedContext->sharedBuilderStorage; + builder->setInsertBefore(inst); + + auto newType = sharedContext->lowerType(builder, inst->getFullType()); + if (newType != inst->getFullType()) + inst->setFullType((IRType*)newType); + } + + void processModule() + { + // We start by initializing our shared IR building state, + // since we will re-use that state for any code we + // generate along the way. + // + SharedIRBuilder* sharedBuilder = &sharedContext->sharedBuilderStorage; + sharedBuilder->module = sharedContext->module; + sharedBuilder->session = sharedContext->module->session; + + sharedContext->addToWorkList(sharedContext->module->getModuleInst()); + + while (sharedContext->workList.getCount() != 0) + { + // We will then iterate until our work list goes dry. + // + while (sharedContext->workList.getCount() != 0) + { + IRInst* inst = sharedContext->workList.getLast(); + + sharedContext->workList.removeLast(); + sharedContext->workListSet.Remove(inst); + + processInst(inst); + + for (auto child = inst->getLastChild(); child; child = child->getPrevInst()) + { + sharedContext->addToWorkList(child); + } + } + } + } + }; + + void lowerGenericType(SharedGenericsLoweringContext* sharedContext) + { + GenericVarLoweringContext context; + context.sharedContext = sharedContext; + context.processModule(); + } +} + diff --git a/source/slang/slang-ir-lower-generic-type.h b/source/slang/slang-ir-lower-generic-type.h new file mode 100644 index 000000000..6432a494b --- /dev/null +++ b/source/slang/slang-ir-lower-generic-type.h @@ -0,0 +1,12 @@ +// slang-ir-lower-generic-type.h +#pragma once + +namespace Slang +{ + struct SharedGenericsLoweringContext; + + /// Lower all references to generic types (ThisType, AssociatedType, etc.) into IRAnyValueType. + void lowerGenericType( + SharedGenericsLoweringContext* sharedContext); + +} diff --git a/source/slang/slang-ir-lower-generic-var.cpp b/source/slang/slang-ir-lower-generic-var.cpp deleted file mode 100644 index 936ed017f..000000000 --- a/source/slang/slang-ir-lower-generic-var.cpp +++ /dev/null @@ -1,202 +0,0 @@ -// slang-ir-lower-generic-function.cpp -#include "slang-ir-lower-generic-function.h" - -#include "slang-ir-generics-lowering-context.h" -#include "slang-ir.h" -#include "slang-ir-clone.h" -#include "slang-ir-insts.h" - -namespace Slang -{ - // This is a subpass of generics lowering IR transformation. - // This pass lowers all generic function types and function definitions, including - // the function types used in interface types, to ordinary functions that takes - // raw pointers in place of generic types. - struct GenericVarLoweringContext - { - SharedGenericsLoweringContext* sharedContext; - - void processVarInst(IRInst* varInst) - { - // We process only var declarations that have type - // `Ptr<IRParam>` or `Ptr<IRLookupInterfaceMethod>`. - // - // Due to the processing of `lowerGenericFunction`, - // A local variable of generic type now appears as - // `var X:Ptr<y:Ptr<RTTIType>>`, - // where y can be an IRParam if it is a generic type, - // or an `lookup_interface_method` if it is an associated type. - // We match this pattern and turn this inst into - // `X:RTTIPtr(irParam) = alloca(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 && varTypeParam->op != kIROp_lookup_interface_method) - 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 = &sharedContext->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 (valPtr->op == kIROp_undefined) - { - // We don't need to store an undef value. - storeInst->removeAndDeallocate(); - return; - } - 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 = &sharedContext->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 = &sharedContext->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(); - } - - void processInst(IRInst* inst) - { - if (inst->op == kIROp_Var) - { - processVarInst(inst); - } - else if (inst->op == kIROp_Load) - { - processLoadInst(cast<IRLoad>(inst)); - } - else if (inst->op == kIROp_Store) - { - processStoreInst(cast<IRStore>(inst)); - } - } - - void processModule() - { - // We start by initializing our shared IR building state, - // since we will re-use that state for any code we - // generate along the way. - // - SharedIRBuilder* sharedBuilder = &sharedContext->sharedBuilderStorage; - sharedBuilder->module = sharedContext->module; - sharedBuilder->session = sharedContext->module->session; - - sharedContext->addToWorkList(sharedContext->module->getModuleInst()); - - while (sharedContext->workList.getCount() != 0) - { - // We will then iterate until our work list goes dry. - // - while (sharedContext->workList.getCount() != 0) - { - IRInst* inst = sharedContext->workList.getLast(); - - sharedContext->workList.removeLast(); - sharedContext->workListSet.Remove(inst); - - processInst(inst); - - for (auto child = inst->getLastChild(); child; child = child->getPrevInst()) - { - sharedContext->addToWorkList(child); - } - } - } - } - }; - - void lowerGenericVar(SharedGenericsLoweringContext* sharedContext) - { - GenericVarLoweringContext context; - context.sharedContext = sharedContext; - context.processModule(); - } -} - diff --git a/source/slang/slang-ir-lower-generic-var.h b/source/slang/slang-ir-lower-generic-var.h deleted file mode 100644 index dfc59b24e..000000000 --- a/source/slang/slang-ir-lower-generic-var.h +++ /dev/null @@ -1,12 +0,0 @@ -// slang-ir-lower-generic-var.h -#pragma once - -namespace Slang -{ - struct SharedGenericsLoweringContext; - - /// Lower load and stores of generic local variables into raw pointer operations. - void lowerGenericVar( - SharedGenericsLoweringContext* sharedContext); - -} diff --git a/source/slang/slang-ir-lower-generics.cpp b/source/slang/slang-ir-lower-generics.cpp index c60b5386a..9960b7451 100644 --- a/source/slang/slang-ir-lower-generics.cpp +++ b/source/slang/slang-ir-lower-generics.cpp @@ -4,7 +4,7 @@ #include "slang-ir-generics-lowering-context.h" #include "slang-ir-lower-generic-function.h" #include "slang-ir-lower-generic-call.h" -#include "slang-ir-lower-generic-var.h" +#include "slang-ir-lower-generic-type.h" #include "slang-ir-witness-table-wrapper.h" #include "slang-ir-ssa.h" #include "slang-ir-dce.h" @@ -12,20 +12,20 @@ namespace Slang { void lowerGenerics( - IRModule* module) + IRModule* module, + DiagnosticSink* sink) { SharedGenericsLoweringContext sharedContext; sharedContext.module = module; + sharedContext.sink = sink; + lowerGenericFunctions(&sharedContext); + lowerGenericType(&sharedContext); lowerGenericCalls(&sharedContext); // We might have generated new temporary variables during lowering. - // An SSA pass can clean up unncessary load/stores. + // An SSA pass can clean up unnecessary load/stores. constructSSA(module); eliminateDeadCode(module); - lowerGenericVar(&sharedContext); - // After lowerGenericVar, there could be some unused `undef` values. - // We eliminate them in a DCE pass. - eliminateDeadCode(module); generateWitnessTableWrapperFunctions(&sharedContext); } } // namespace Slang diff --git a/source/slang/slang-ir-lower-generics.h b/source/slang/slang-ir-lower-generics.h index 664dd11ff..76b5c1b58 100644 --- a/source/slang/slang-ir-lower-generics.h +++ b/source/slang/slang-ir-lower-generics.h @@ -6,10 +6,12 @@ namespace Slang
{
struct IRModule;
+ class DiagnosticSink;
/// Lower generic and interface-based code to ordinary types and functions using
/// dynamic dispatch mechanisms.
void lowerGenerics(
- IRModule* module);
+ IRModule* module,
+ DiagnosticSink* sink);
}
diff --git a/source/slang/slang-ir-witness-table-wrapper.cpp b/source/slang/slang-ir-witness-table-wrapper.cpp index 8a30ed148..8dda95563 100644 --- a/source/slang/slang-ir-witness-table-wrapper.cpp +++ b/source/slang/slang-ir-witness-table-wrapper.cpp @@ -14,6 +14,73 @@ namespace Slang { SharedGenericsLoweringContext* sharedContext; + // Represents a work item for packing `inout` or `out` arguments after a concrete call. + struct ArgumentPackWorkItem + { + // A `AnyValue` typed destination. + IRInst* dstArg = nullptr; + // A concrete value to be packed. + IRInst* concreteArg = nullptr; + }; + + // Unpack an `arg` of `IRAnyValue` into concrete type if necessary, to make it feedable into the parameter. + // If `arg` represents a AnyValue typed variable passed in to a concrete `out` parameter, + // this function indicates that it needs to be packed after the call by setting + // `packAfterCall`. + IRInst* maybeUnpackArg( + IRBuilder* builder, + IRType* paramType, + IRInst* arg, + ArgumentPackWorkItem& packAfterCall) + { + packAfterCall.dstArg = nullptr; + packAfterCall.concreteArg = nullptr; + + // If either paramType or argType is a pointer type + // (because of `inout` or `out` modifiers), we extract + // the underlying value type first. + IRType* paramValType = paramType; + IRType* argValType = arg->getDataType(); + IRInst* argVal = arg; + bool isParamPointer = false; + if (auto ptrType = as<IRPtrTypeBase>(paramType)) + { + isParamPointer = true; + paramValType = ptrType->getValueType(); + } + bool isArgPointer = false; + auto argType = arg->getDataType(); + if (auto argPtrType = as<IRPtrTypeBase>(argType)) + { + isArgPointer = true; + argValType = argPtrType->getValueType(); + argVal = builder->emitLoad(arg); + } + + // Unpack `arg` if the parameter expects concrete type but + // `arg` is an AnyValue. + if (!as<IRAnyValueType>(paramValType) && as<IRAnyValueType>(argValType)) + { + auto unpackedArgVal = builder->emitUnpackAnyValue(paramValType, argVal); + // if parameter expects an `out` pointer, store the unpacked val into a + // variable and pass in a pointer to that variable. + if (as<IRPtrTypeBase>(paramType)) + { + auto tempVar = builder->emitVar(paramValType); + builder->emitStore(tempVar, unpackedArgVal); + // tempVar needs to be unpacked into original var after the call. + packAfterCall.dstArg = arg; + packAfterCall.concreteArg = tempVar; + return tempVar; + } + else + { + return unpackedArgVal; + } + } + return arg; + } + IRStringLit* _getWitnessTableWrapperFuncName(IRFunc* func) { IRBuilder builderStorage; @@ -56,80 +123,43 @@ namespace Slang } List<IRInst*> args; - bool callerAllocatesReturnVal = funcTypeInInterface->getResultType()->op == kIROp_VoidType - && func->getResultType()->op != kIROp_VoidType; - IRVar* retVar = nullptr; - if (callerAllocatesReturnVal) - { - // If return value is allocated by caller, we need to write the result - // of the call into a local variable, and copy from that local variable - // to the address passed in by the caller. - retVar = builder->emitVar(func->getResultType()); - SLANG_ASSERT(params.getCount() == (Index)(func->getParamCount() + 1)); - } - else - { - SLANG_ASSERT(params.getCount() == (Index)func->getParamCount()); - } + List<ArgumentPackWorkItem> argsToPack; + + SLANG_ASSERT(params.getCount() == (Index)func->getParamCount()); for (UInt i = 0; i < func->getParamCount(); i++) { - auto wrapperParam = params[i + (callerAllocatesReturnVal ? 1 : 0)]; - // Type of the parameter in interface requirement. - auto reqParamType = wrapperParam->getDataType(); + auto wrapperParam = params[i]; // Type of the parameter in the callee. auto funcParamType = func->getParamType(i); // 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 (as<IRRawPointerTypeBase>(reqParamType) && - !as<IRRawPointerTypeBase>(funcParamType)) - { - if (as<IRPtrTypeBase>(funcParamType)) - { - // The implementation function expects a pointer to the - // concrete type. This is the case for inout/out parameters. - auto bitCast = builder->emitBitCast(funcParamType, wrapperParam); - args.add(bitCast); - } - else - { - // The implementation function expects just a value of the - // concrete type. We need to insert a load in this case. - auto bitCast = builder->emitBitCast( - builder->getPtrType(funcParamType), - wrapperParam); - auto load = builder->emitLoad(bitCast); - args.add(load); - } - } - else - { - args.add(wrapperParam); - } + // or in the form a value for `in` parameters, while + // the interface exposes an AnyValue type, + // we need to unpack the AnyValue argument to the appropriate + // concerete type. + ArgumentPackWorkItem packWorkItem; + auto newArg = maybeUnpackArg(builder, funcParamType, wrapperParam, packWorkItem); + args.add(newArg); + if (packWorkItem.concreteArg) + argsToPack.add(packWorkItem); } auto call = builder->emitCallInst(func->getResultType(), func, args); - if (retVar) + + // Pack all `out` arguments. + for (auto item : argsToPack) + { + auto anyValType = cast<IRPtrTypeBase>(item.dstArg->getDataType())->getValueType(); + auto concreteVal = builder->emitLoad(item.concreteArg); + auto packedVal = builder->emitPackAnyValue(anyValType, concreteVal); + builder->emitStore(item.dstArg, packedVal); + } + + // Pack return value if necessary. + if (!as<IRAnyValueType>(call->getDataType()) && as<IRAnyValueType>(funcTypeInInterface->getResultType())) { - // If the caller of the wrapper function allocates space, - // we need to store the result of the call into a local varaible, - // and then copy the local variable into the caller-provided - // buffer (params[0]). - builder->emitStore(retVar, call); - // The result type of the inner function can only be a concrete type - // if we reach here. If it is a generic type or generic associated type, - // it would have already been lowered out during interface lowering and - // lowerGenericFunction. - // This means that we can just grab the rtti object from the type directly. - auto rttiObject = sharedContext->maybeEmitRTTIObject(func->getResultType()); - auto rttiPtr = builder->emitGetAddress( - builder->getPtrType(builder->getRTTIType()), - rttiObject); - builder->emitCopy(params[0], retVar, rttiPtr); - builder->emitReturn(); + auto pack = builder->emitPackAnyValue(funcTypeInInterface->getResultType(), call); + builder->emitReturn(pack); } else { diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index fcb039d25..2c9aee817 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -17,6 +17,12 @@ namespace Slang return inst->sourceLoc; } + void printDiagnosticArg(StringBuilder& sb, IRInst* irObject) + { + if (auto nameHint = irObject->findDecoration<IRNameHintDecoration>()) + sb << nameHint->getName(); + } + // !!!!!!!!!!!!!!!!!!!!!!!!!!!! DiagnosticSink Impls !!!!!!!!!!!!!!!!!!!!! IRInst* cloneGlobalValueWithLinkage( @@ -2612,6 +2618,30 @@ namespace Slang return inst; } + IRInst* IRBuilder::emitPackAnyValue(IRType* type, IRInst* value) + { + auto inst = createInst<IRPackAnyValue>( + this, + kIROp_PackAnyValue, + type, + value); + + addInst(inst); + return inst; + } + + IRInst* IRBuilder::emitUnpackAnyValue(IRType* type, IRInst* value) + { + auto inst = createInst<IRPackAnyValue>( + this, + kIROp_UnpackAnyValue, + type, + value); + + addInst(inst); + return inst; + } + IRInst* IRBuilder::emitCallInst( IRType* type, IRInst* pFunc, diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index d59b0c3ea..9316c0228 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -1220,6 +1220,11 @@ struct IRAssociatedType : IRType struct IRThisType : IRType { IR_LEAF_ISA(ThisType) + + IRInst* getConstraintType() + { + return getOperand(0); + } }; struct IRInterfaceRequirementEntry : IRInst diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 36825f828..a4542647a 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -5850,9 +5850,9 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> { SLANG_ASSERT(decl->parentDecl != nullptr); ShortList<IRInterfaceType*> constraintInterfaces; - for (auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>()) + for (auto constraintDecl : decl->getMembersOfType<GenericTypeConstraintDecl>()) { - auto baseType = lowerType(context, inheritanceDecl->base.type); + auto baseType = lowerType(context, constraintDecl->sup.type); SLANG_ASSERT(baseType && baseType->op == kIROp_InterfaceType); constraintInterfaces.add((IRInterfaceType*)baseType); } diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj index aaece7095..8df498a11 100644 --- a/source/slang/slang.vcxproj +++ b/source/slang/slang.vcxproj @@ -242,7 +242,7 @@ <ClInclude Include="slang-ir-link.h" /> <ClInclude Include="slang-ir-lower-generic-call.h" /> <ClInclude Include="slang-ir-lower-generic-function.h" /> - <ClInclude Include="slang-ir-lower-generic-var.h" /> + <ClInclude Include="slang-ir-lower-generic-type.h" /> <ClInclude Include="slang-ir-lower-generics.h" /> <ClInclude Include="slang-ir-missing-return.h" /> <ClInclude Include="slang-ir-restructure-scoping.h" /> @@ -343,7 +343,7 @@ <ClCompile Include="slang-ir-link.cpp" /> <ClCompile Include="slang-ir-lower-generic-call.cpp" /> <ClCompile Include="slang-ir-lower-generic-function.cpp" /> - <ClCompile Include="slang-ir-lower-generic-var.cpp" /> + <ClCompile Include="slang-ir-lower-generic-type.cpp" /> <ClCompile Include="slang-ir-lower-generics.cpp" /> <ClCompile Include="slang-ir-missing-return.cpp" /> <ClCompile Include="slang-ir-restructure-scoping.cpp" /> diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters index 9f3d9b32f..6e0a4b80b 100644 --- a/source/slang/slang.vcxproj.filters +++ b/source/slang/slang.vcxproj.filters @@ -177,9 +177,6 @@ <ClInclude Include="slang-ir-lower-generic-function.h"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="slang-ir-lower-generic-var.h"> - <Filter>Header Files</Filter> - </ClInclude> <ClInclude Include="slang-ir-lower-generics.h"> <Filter>Header Files</Filter> </ClInclude> @@ -312,6 +309,9 @@ <ClInclude Include="slang-visitor.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="slang-ir-lower-generic-type.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="slang-ast-builder.cpp"> @@ -476,9 +476,6 @@ <ClCompile Include="slang-ir-lower-generic-function.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="slang-ir-lower-generic-var.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="slang-ir-lower-generics.cpp"> <Filter>Source Files</Filter> </ClCompile> @@ -608,6 +605,9 @@ <ClCompile Include="slang.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="slang-ir-lower-generic-type.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <None Include="core.meta.slang"> |
