summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2020-08-05 10:32:52 -0700
committerGitHub <noreply@github.com>2020-08-05 10:32:52 -0700
commit6fb2aa70a2681bffbac7e8de67e9598105389945 (patch)
treef89cb433e7498bf74292bf460096f1129ef3ac13 /source
parent092337a67e7ef8ec108cab9cb6679e59bb2ff791 (diff)
`AnyValue` based dynamic dispatch code gen (#1477)
* AnyValue based dynamic code gen * Fix aarch64 build error
Diffstat (limited to 'source')
-rw-r--r--source/slang/slang-check-modifier.cpp25
-rw-r--r--source/slang/slang-diagnostic-defs.h7
-rw-r--r--source/slang/slang-diagnostics.h4
-rw-r--r--source/slang/slang-emit-c-like.cpp22
-rw-r--r--source/slang/slang-emit-cpp.cpp8
-rw-r--r--source/slang/slang-emit.cpp16
-rw-r--r--source/slang/slang-ir-generics-lowering-context.cpp107
-rw-r--r--source/slang/slang-ir-generics-lowering-context.h19
-rw-r--r--source/slang/slang-ir-inst-defs.h3
-rw-r--r--source/slang/slang-ir-insts.h20
-rw-r--r--source/slang/slang-ir-lower-generic-call.cpp146
-rw-r--r--source/slang/slang-ir-lower-generic-function.cpp126
-rw-r--r--source/slang/slang-ir-lower-generic-type.cpp70
-rw-r--r--source/slang/slang-ir-lower-generic-type.h12
-rw-r--r--source/slang/slang-ir-lower-generic-var.cpp202
-rw-r--r--source/slang/slang-ir-lower-generic-var.h12
-rw-r--r--source/slang/slang-ir-lower-generics.cpp14
-rw-r--r--source/slang/slang-ir-lower-generics.h4
-rw-r--r--source/slang/slang-ir-witness-table-wrapper.cpp158
-rw-r--r--source/slang/slang-ir.cpp30
-rw-r--r--source/slang/slang-ir.h5
-rw-r--r--source/slang/slang-lower-to-ir.cpp4
-rw-r--r--source/slang/slang.vcxproj4
-rw-r--r--source/slang/slang.vcxproj.filters12
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">