summaryrefslogtreecommitdiffstats
path: root/source/slang
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2023-09-03 12:56:31 -0700
committerGitHub <noreply@github.com>2023-09-03 12:56:31 -0700
commit1d4b5b6fd2433a10cc7ab87626cb560f54b0acbb (patch)
tree6196d519190720fd2968ac7d4b373e3c967d5fe6 /source/slang
parent355bb4287861f96082751042f4e58ff3598b4e5e (diff)
Proper lowering of functiosn that returns NonCopyable values. (#3179)
* Proper lowering of functiosn that returns NonCopyable values. * Fix tests. * Fix clang errors. * Fix. * Fix clang error. --------- Co-authored-by: Yong He <yhe@nvidia.com>
Diffstat (limited to 'source/slang')
-rw-r--r--source/slang/hlsl.meta.slang70
-rw-r--r--source/slang/slang-ast-expr.h10
-rw-r--r--source/slang/slang-ast-iterator.h2
-rw-r--r--source/slang/slang-ast-type.cpp10
-rw-r--r--source/slang/slang-ast-type.h1
-rw-r--r--source/slang/slang-check-expr.cpp26
-rw-r--r--source/slang/slang-check-impl.h1
-rw-r--r--source/slang/slang-diagnostic-defs.h2
-rw-r--r--source/slang/slang-emit-c-like.cpp29
-rw-r--r--source/slang/slang-emit-c-like.h3
-rw-r--r--source/slang/slang-emit-glsl.cpp67
-rw-r--r--source/slang/slang-emit-glsl.h3
-rw-r--r--source/slang/slang-emit-hlsl.cpp25
-rw-r--r--source/slang/slang-emit-hlsl.h2
-rw-r--r--source/slang/slang-ir-specialize-resources.cpp6
-rw-r--r--source/slang/slang-lower-to-ir.cpp1140
-rw-r--r--source/slang/slang-parser.cpp8
17 files changed, 774 insertions, 631 deletions
diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang
index d56c4ffcd..23815d2e9 100644
--- a/source/slang/hlsl.meta.slang
+++ b/source/slang/hlsl.meta.slang
@@ -8286,8 +8286,6 @@ struct HitObject
RayDesc Ray,
inout payload_t Payload)
{
- HitObject hitObj;
-
[__vulkanRayPayload]
static payload_t p;
@@ -8295,7 +8293,7 @@ struct HitObject
p = Payload;
__glslTraceRay(
- hitObj,
+ __return_val,
AccelerationStructure,
RayFlags, // Assumes D3D/VK have some RayFlags values
InstanceInclusionMask, // cullMask
@@ -8310,8 +8308,6 @@ struct HitObject
// Write the payload out
Payload = p;
-
- return hitObj;
}
/// Executes motion ray traversal (including anyhit and intersection shaders) like TraceRay, but returns the
@@ -8329,8 +8325,6 @@ struct HitObject
float CurrentTime,
inout payload_t Payload)
{
- HitObject hitObj;
-
[__vulkanRayPayload]
static payload_t p;
@@ -8338,7 +8332,7 @@ struct HitObject
p = Payload;
__glslTraceMotionRay(
- hitObj,
+ __return_val,
AccelerationStructure,
RayFlags, // Assumes D3D/VK have some RayFlags values
InstanceInclusionMask, // cullMask
@@ -8354,8 +8348,6 @@ struct HitObject
// Write the payload out
Payload = p;
-
- return hitObj;
}
/// Creates a HitObject representing a hit based on values explicitly passed as arguments, without
@@ -8404,14 +8396,13 @@ struct HitObject
RayDesc Ray,
attr_t attributes)
{
- HitObject hitObj;
-
// Save the attributes
__ref attr_t attr = __hitObjectAttributes<attr_t>();
attr = attributes;
- __glslMakeHit(hitObj,
+ __glslMakeHit(
+ __return_val,
AccelerationStructure,
InstanceIndex,
PrimitiveIndex,
@@ -8424,8 +8415,6 @@ struct HitObject
Ray.Direction,
Ray.TMax,
__hitObjectAttributesLocation(__hitObjectAttributes<attr_t>()));
-
- return hitObj;
}
/// See MakeHit but handles Motion
@@ -8444,14 +8433,13 @@ struct HitObject
float CurrentTime,
attr_t attributes)
{
- HitObject hitObj;
-
// Save the attributes
__ref attr_t attr = __hitObjectAttributes<attr_t>();
attr = attributes;
- __glslMakeMotionHit(hitObj,
+ __glslMakeMotionHit(
+ __return_val,
AccelerationStructure,
InstanceIndex,
PrimitiveIndex,
@@ -8465,8 +8453,6 @@ struct HitObject
Ray.TMax,
CurrentTime,
__hitObjectAttributesLocation(__hitObjectAttributes<attr_t>()));
-
- return hitObj;
}
/// Creates a HitObject representing a hit based on values explicitly passed as arguments, without
@@ -8513,13 +8499,12 @@ struct HitObject
RayDesc Ray,
attr_t attributes)
{
- HitObject hitObj;
-
// Save the attributes
__ref attr_t attr = __hitObjectAttributes<attr_t>();
attr = attributes;
- __glslMakeHitWithIndex(hitObj,
+ __glslMakeHitWithIndex(
+ __return_val,
AccelerationStructure,
InstanceIndex, ///? Same as instanceid ?
GeometryIndex,
@@ -8531,8 +8516,6 @@ struct HitObject
Ray.Direction,
Ray.TMax,
__hitObjectAttributesLocation(__hitObjectAttributes<attr_t>()));
-
- return hitObj;
}
/// See MakeHit but handles Motion
@@ -8556,7 +8539,8 @@ struct HitObject
__ref attr_t attr = __hitObjectAttributes<attr_t>();
attr = attributes;
- __glslMakeMotionHitWithIndex(hitObj,
+ __glslMakeMotionHitWithIndex(
+ __return_val,
AccelerationStructure,
InstanceIndex, ///? Same as instanceid ?
GeometryIndex,
@@ -8569,8 +8553,6 @@ struct HitObject
Ray.TMax,
CurrentTime,
__hitObjectAttributesLocation(__hitObjectAttributes<attr_t>()));
-
- return hitObj;
}
/// Creates a HitObject representing a miss based on values explicitly passed as arguments, without
@@ -8588,9 +8570,7 @@ struct HitObject
uint MissShaderIndex,
RayDesc Ray)
{
- HitObject hitObj;
- __glslMakeMiss(hitObj, MissShaderIndex, Ray.Origin, Ray.TMin, Ray.Direction, Ray.TMax);
- return hitObj;
+ __glslMakeMiss(__return_val, MissShaderIndex, Ray.Origin, Ray.TMin, Ray.Direction, Ray.TMax);
}
/// See MakeMiss but handles Motion
@@ -8602,9 +8582,7 @@ struct HitObject
RayDesc Ray,
float CurrentTime)
{
- HitObject hitObj;
- __glslMakeMotionMiss(hitObj, MissShaderIndex, Ray.Origin, Ray.TMin, Ray.Direction, Ray.TMax, CurrentTime);
- return hitObj;
+ __glslMakeMotionMiss(__return_val, MissShaderIndex, Ray.Origin, Ray.TMin, Ray.Direction, Ray.TMax, CurrentTime);
}
/// Creates a HitObject representing “NOP” (no operation) which is neither a hit nor a miss. Invoking a
@@ -8620,9 +8598,7 @@ struct HitObject
__specialized_for_target(glsl)
static HitObject MakeNop()
{
- HitObject hitObj;
- __glslMakeNop(hitObj);
- return hitObj;
+ __glslMakeNop(__return_val);
}
/// Invokes closesthit or miss shading for the specified hit object. In case of a NOP HitObject, no
@@ -8656,18 +8632,21 @@ struct HitObject
[__requiresNVAPI]
__target_intrinsic(hlsl)
__target_intrinsic(glsl, "hitObjectIsMissNV($0)")
+ __glsl_extension(GL_EXT_ray_tracing)
bool IsMiss();
/// Returns true if the HitObject encodes a hit, otherwise returns false.
[__requiresNVAPI]
__target_intrinsic(hlsl)
__target_intrinsic(glsl, "hitObjectIsHitNV($0)")
+ __glsl_extension(GL_EXT_ray_tracing)
bool IsHit();
/// Returns true if the HitObject encodes a nop, otherwise returns false.
[__requiresNVAPI]
__target_intrinsic(hlsl)
__target_intrinsic(glsl, "hitObjectIsEmptyNV($0)")
+ __glsl_extension(GL_EXT_ray_tracing)
bool IsNop();
/// Queries ray properties from HitObject. Valid if the hit object represents a hit or a miss.
@@ -8686,36 +8665,42 @@ struct HitObject
[__requiresNVAPI]
__target_intrinsic(hlsl)
__target_intrinsic(glsl, "hitObjectGetShaderBindingTableRecordIndexNV($0)")
+ __glsl_extension(GL_EXT_ray_tracing)
uint GetShaderTableIndex();
/// Returns the instance index of a hit. Valid if the hit object represents a hit.
[__requiresNVAPI]
__target_intrinsic(hlsl)
__target_intrinsic(glsl, "hitObjectGetInstanceCustomIndexNV($0)")
+ __glsl_extension(GL_EXT_ray_tracing)
uint GetInstanceIndex();
/// Returns the instance ID of a hit. Valid if the hit object represents a hit.
[__requiresNVAPI]
__target_intrinsic(hlsl)
__target_intrinsic(glsl, "hitObjectGetInstanceIdNV($0)")
+ __glsl_extension(GL_EXT_ray_tracing)
uint GetInstanceID();
/// Returns the geometry index of a hit. Valid if the hit object represents a hit.
[__requiresNVAPI]
__target_intrinsic(hlsl)
__target_intrinsic(glsl, "hitObjectGetGeometryIndexNV($0)")
+ __glsl_extension(GL_EXT_ray_tracing)
uint GetGeometryIndex();
/// Returns the primitive index of a hit. Valid if the hit object represents a hit.
[__requiresNVAPI]
__target_intrinsic(hlsl)
__target_intrinsic(glsl, "hitObjectGetPrimitiveIndexNV($0)")
+ __glsl_extension(GL_EXT_ray_tracing)
uint GetPrimitiveIndex();
/// Returns the hit kind. Valid if the hit object represents a hit.
[__requiresNVAPI]
__target_intrinsic(hlsl)
__target_intrinsic(glsl, "hitObjectGetHitKindNV($0)")
+ __glsl_extension(GL_EXT_ray_tracing)
uint GetHitKind();
/// Returns the attributes of a hit. Valid if the hit object represents a hit or a miss.
@@ -8797,6 +8782,7 @@ struct HitObject
__glsl_extension(GL_NV_shader_invocation_reorder)
__glsl_extension(GL_EXT_ray_tracing)
+ __glsl_version(460)
__target_intrinsic(glsl, "hitObjectRecordMissNV")
static void __glslMakeMiss(
out HitObject hitObj,
@@ -8810,7 +8796,8 @@ struct HitObject
__glsl_extension(GL_NV_shader_invocation_reorder)
__glsl_extension(GL_EXT_ray_tracing)
__glsl_extension(GL_NV_ray_tracing_motion_blur)
- __target_intrinsic(glsl, "hitObjectRecordMissNV")
+ __glsl_version(460)
+ __target_intrinsic(glsl, "hitObjectRecordMissMotionNV")
static void __glslMakeMotionMiss(
out HitObject hitObj,
uint MissShaderIndex,
@@ -8822,7 +8809,7 @@ struct HitObject
__glsl_extension(GL_NV_shader_invocation_reorder)
__glsl_extension(GL_EXT_ray_tracing)
- __target_intrinsic(glsl, "hitObjectRecordEmptyNV($0)")
+ __target_intrinsic(glsl, "hitObjectRecordEmptyNV")
static void __glslMakeNop(out HitObject hitObj);
__glsl_extension(GL_NV_shader_invocation_reorder)
@@ -8844,6 +8831,7 @@ struct HitObject
// "void hitObjectRecordHitWithIndexNV(hitObjectNV, accelerationStructureEXT,int,int,int,uint,uint,vec3,float,vec3,float,int);"
__glsl_extension(GL_NV_shader_invocation_reorder)
__glsl_extension(GL_EXT_ray_tracing)
+ __glsl_version(460)
__target_intrinsic(glsl, "hitObjectRecordHitWithIndexNV")
static void __glslMakeHitWithIndex(
out HitObject hitObj,
@@ -8929,7 +8917,7 @@ struct HitObject
__glsl_extension(GL_NV_shader_invocation_reorder)
__target_intrinsic(glsl, "hitObjectTraceRayNV")
static void __glslTraceRay(
- out HitObject hitObj,
+ out HitObject hitObject,
RaytracingAccelerationStructure accelerationStructure,
uint rayFlags,
uint cullMask,
@@ -8947,7 +8935,7 @@ struct HitObject
__glsl_extension(GL_NV_ray_tracing_motion_blur)
__target_intrinsic(glsl, "hitObjectTraceRayMotionNV")
static void __glslTraceMotionRay(
- out HitObject hitObj,
+ out HitObject hitObject,
RaytracingAccelerationStructure accelerationStructure,
uint rayFlags,
uint cullMask,
diff --git a/source/slang/slang-ast-expr.h b/source/slang/slang-ast-expr.h
index 5603ef2a5..6699426d5 100644
--- a/source/slang/slang-ast-expr.h
+++ b/source/slang/slang-ast-expr.h
@@ -469,6 +469,16 @@ class ThisExpr: public Expr
Scope* scope = nullptr;
};
+// Represent a reference to the virtual __return_val object holding the return value of
+// functions whose result type is non-copyable.
+class ReturnValExpr : public Expr
+{
+ SLANG_AST_CLASS(ReturnValExpr)
+
+ SLANG_UNREFLECTED
+ Scope* scope = nullptr;
+};
+
// An expression that binds a temporary variable in a local expression context
class LetExpr: public Expr
{
diff --git a/source/slang/slang-ast-iterator.h b/source/slang/slang-ast-iterator.h
index d38fd9374..dbf9bc8fc 100644
--- a/source/slang/slang-ast-iterator.h
+++ b/source/slang/slang-ast-iterator.h
@@ -220,6 +220,8 @@ struct ASTIterator
void visitThisExpr(ThisExpr* expr) { iterator->maybeDispatchCallback(expr); }
void visitThisTypeExpr(ThisTypeExpr* expr) { iterator->maybeDispatchCallback(expr); }
+ void visitReturnValExpr(ReturnValExpr* expr) { iterator->maybeDispatchCallback(expr); }
+
void visitAndTypeExpr(AndTypeExpr* expr)
{
iterator->maybeDispatchCallback(expr);
diff --git a/source/slang/slang-ast-type.cpp b/source/slang/slang-ast-type.cpp
index 9704b4c87..f80de86fd 100644
--- a/source/slang/slang-ast-type.cpp
+++ b/source/slang/slang-ast-type.cpp
@@ -796,4 +796,14 @@ Type* removeParamDirType(Type* type)
return type;
}
+bool isNonCopyableType(Type* type)
+{
+ auto declRefType = as<DeclRefType>(type);
+ if (!declRefType)
+ return false;
+ if (declRefType->getDeclRef().getDecl()->findModifier<NonCopyableTypeAttribute>())
+ return true;
+ return false;
+}
+
} // namespace Slang
diff --git a/source/slang/slang-ast-type.h b/source/slang/slang-ast-type.h
index c2a13542d..50d523cc5 100644
--- a/source/slang/slang-ast-type.h
+++ b/source/slang/slang-ast-type.h
@@ -834,5 +834,6 @@ class ModifiedType : public Type
};
Type* removeParamDirType(Type* type);
+bool isNonCopyableType(Type* type);
} // namespace Slang
diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp
index a00b1bea2..055364d5e 100644
--- a/source/slang/slang-check-expr.cpp
+++ b/source/slang/slang-check-expr.cpp
@@ -3769,6 +3769,32 @@ namespace Slang
return CreateErrorExpr(expr);
}
+ Expr* SemanticsExprVisitor::visitReturnValExpr(ReturnValExpr* expr)
+ {
+ auto scope = expr->scope;
+ if (scope)
+ {
+ auto parentFunc = as<CallableDecl>(getParentFunc(scope->containerDecl));
+ if (parentFunc)
+ {
+ if (as<ErrorType>(parentFunc->returnType.type))
+ {
+ expr->type = parentFunc->returnType.type;
+ return expr;
+ }
+ if (isNonCopyableType(parentFunc->returnType.type))
+ {
+ expr->type.isLeftValue = true;
+ expr->type.type = parentFunc->returnType.type;
+ return expr;
+ }
+ }
+ }
+ getSink()->diagnose(expr, Diagnostics::returnValNotAvailable);
+ expr->type = getASTBuilder()->getErrorType();
+ return expr;
+ }
+
Expr* SemanticsExprVisitor::visitAndTypeExpr(AndTypeExpr* expr)
{
// The left and right sides of an `&` for types must both be types.
diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h
index c4a7b3e6d..37dcba3f4 100644
--- a/source/slang/slang-check-impl.h
+++ b/source/slang/slang-check-impl.h
@@ -2409,6 +2409,7 @@ namespace Slang
Expr* visitThisExpr(ThisExpr* expr);
Expr* visitThisTypeExpr(ThisTypeExpr* expr);
+ Expr* visitReturnValExpr(ReturnValExpr* expr);
Expr* visitAndTypeExpr(AndTypeExpr* expr);
Expr* visitPointerTypeExpr(PointerTypeExpr* expr);
Expr* visitModifiedTypeExpr(ModifiedTypeExpr* expr);
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index b562ac880..7c8bab1ad 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -536,7 +536,7 @@ DIAGNOSTIC(38100, Error, typeDoesntImplementInterfaceRequirement, "type '$0' doe
DIAGNOSTIC(38101, Error, thisExpressionOutsideOfTypeDecl, "'this' expression can only be used in members of an aggregate type")
DIAGNOSTIC(38102, Error, initializerNotInsideType, "an 'init' declaration is only allowed inside a type or 'extension' declaration")
DIAGNOSTIC(38103, Error, thisTypeOutsideOfTypeDecl, "'This' type can only be used inside of an aggregate type")
-
+DIAGNOSTIC(38104, Error, returnValNotAvailable, "cannot use '__return_val' here. '__return_val' is defined only in functions that return a non-copyable value.")
DIAGNOSTIC(38020, Error, mismatchEntryPointTypeArgument, "expecting $0 entry-point type arguments, provided $1.")
DIAGNOSTIC(38021, Error, typeArgumentForGenericParameterDoesNotConformToInterface, "type argument `$0` for generic parameter `$1` does not conform to interface `$2`.")
diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp
index fe25c3f19..a8080851f 100644
--- a/source/slang/slang-emit-c-like.cpp
+++ b/source/slang/slang-emit-c-like.cpp
@@ -1535,25 +1535,6 @@ bool CLikeSourceEmitter::shouldFoldInstIntoUseSites(IRInst* inst)
if(as<IRUnconditionalBranch>(user))
return false;
- // HACK: As a special case, an `allocateOpaqueHandle` operation should
- // only be folded in if its only use is as the operand of a `store`
- // that will *itself* get peephole merged in as the initial-value expression
- // of a `var`:
- //
- if (inst->getOp() == kIROp_AllocateOpaqueHandle)
- {
- auto store = as<IRStore>(user);
- if (!store) return false;
- if (store->getVal() != inst) return false;
-
- auto var = as<IRVar>(store->getPtr());
- if (!var) return false;
-
- if(var->getNextInst() != store) return false;
-
- return true;
- }
-
// Okay, if we reach this point then the user comes later in
// the same block, and there are no instructions with side
// effects in between, so it seems safe to fold things in.
@@ -2003,7 +1984,6 @@ void CLikeSourceEmitter::defaultEmitInstExpr(IRInst* inst, const EmitOpInfo& inO
case kIROp_undefined:
case kIROp_DefaultConstruct:
- case kIROp_AllocateOpaqueHandle:
m_writer->emit(getName(inst));
break;
@@ -2676,11 +2656,7 @@ void CLikeSourceEmitter::_emitInst(IRInst* inst)
break;
case kIROp_AllocateOpaqueHandle:
- {
- _emitAllocateOpaqueHandleImpl(inst);
- }
break;
-
case kIROp_Var:
{
auto var = cast<IRVar>(inst);
@@ -2875,11 +2851,6 @@ void CLikeSourceEmitter::_emitInstAsDefaultInitializedVar(IRInst* inst, IRType*
m_writer->emit(";\n");
}
-void CLikeSourceEmitter::_emitAllocateOpaqueHandleImpl(IRInst* allocateInst)
-{
- _emitInstAsDefaultInitializedVar(allocateInst, allocateInst->getDataType());
-}
-
void CLikeSourceEmitter::emitSemanticsUsingVarLayout(IRVarLayout* varLayout)
{
if(auto semanticAttr = varLayout->findAttr<IRSemanticAttr>())
diff --git a/source/slang/slang-emit-c-like.h b/source/slang/slang-emit-c-like.h
index 02ab28028..62f6d20a2 100644
--- a/source/slang/slang-emit-c-like.h
+++ b/source/slang/slang-emit-c-like.h
@@ -309,9 +309,8 @@ public:
void emitStore(IRStore* store);
virtual void _emitStoreImpl(IRStore* store);
- virtual void _emitInstAsVarInitializerImpl(IRInst* inst);
void _emitInstAsDefaultInitializedVar(IRInst* inst, IRType* type);
- virtual void _emitAllocateOpaqueHandleImpl(IRInst* allocateInst);
+ void _emitInstAsVarInitializerImpl(IRInst* inst);
UInt getBindingOffset(EmitVarChain* chain, LayoutResourceKind kind);
UInt getBindingSpace(EmitVarChain* chain, LayoutResourceKind kind);
diff --git a/source/slang/slang-emit-glsl.cpp b/source/slang/slang-emit-glsl.cpp
index c651c8735..94c85409f 100644
--- a/source/slang/slang-emit-glsl.cpp
+++ b/source/slang/slang-emit-glsl.cpp
@@ -927,61 +927,6 @@ void GLSLSourceEmitter::emitLoopControlDecorationImpl(IRLoopControlDecoration* d
}
}
-void GLSLSourceEmitter::_emitInstAsVarInitializerImpl(IRInst* inst)
-{
- // Some opcodes can be folded into a variable initialization
- // by allowing the variable to be "default-constructed."
- //
- switch (inst->getOp())
- {
- case kIROp_AllocateOpaqueHandle:
- //
- // Note: semantically, we should only elide the initializer
- // if `inst` is able to be folded here, since otherwise
- // it could be a single allocation that is used to initialize
- // multiple local variables (which should then alias the
- // same location).
- //
- // However, since GlSL doesn't support assignment of opaque
- // handle types, code will fail to compile downstream in
- // the case where the initializer *doesn't* fold.
- //
- // The decision being made here should help ensure that we
- // don't emit code that silently has different semantics
- // than the input.
- //
- if (shouldFoldInstIntoUseSites(inst))
- {
- return;
- }
- break;
-
- default:
- break;
- }
-
- // We fall back to the default behavior for all targets,
- // which is to emit `inst` as an initial-value expression
- // after an `=`.
- //
- Super::_emitInstAsVarInitializerImpl(inst);
-}
-
-void GLSLSourceEmitter::_emitStoreImpl(IRStore* store)
-{
- auto srcVal = store->getVal();
- switch (srcVal->getOp())
- {
- default:
- Super::_emitStoreImpl(store);
- break;
-
- case kIROp_AllocateOpaqueHandle:
- break;
- }
-
-}
-
void GLSLSourceEmitter::_emitSpecialFloatImpl(IRType* type, const char* valueExpr)
{
if( type->getOp() != kIROp_FloatType )
@@ -2215,9 +2160,17 @@ void GLSLSourceEmitter::emitParamTypeImpl(IRType* type, String const& name)
{
if (auto refType = as<IRRefType>(type))
{
- _requireGLSLExtension(UnownedStringSlice("GL_EXT_spirv_intrinsics"));
- m_writer->emit("spirv_by_reference ");
type = refType->getValueType();
+
+ if (as<IRRayQueryType>(type) || as<IRHitObjectType>(type))
+ {
+ // GLSL will automatically pass these by reference, so we don't need to do anything.
+ }
+ else
+ {
+ _requireGLSLExtension(UnownedStringSlice("GL_EXT_spirv_intrinsics"));
+ m_writer->emit("spirv_by_reference ");
+ }
}
else if (auto spirvLiteralType = as<IRSPIRVLiteralType>(type))
{
diff --git a/source/slang/slang-emit-glsl.h b/source/slang/slang-emit-glsl.h
index 7c1a15315..780b24453 100644
--- a/source/slang/slang-emit-glsl.h
+++ b/source/slang/slang-emit-glsl.h
@@ -56,9 +56,6 @@ protected:
virtual void emitSimpleValueImpl(IRInst* inst) SLANG_OVERRIDE;
virtual void emitLoopControlDecorationImpl(IRLoopControlDecoration* decl) SLANG_OVERRIDE;
- virtual void _emitInstAsVarInitializerImpl(IRInst* inst) SLANG_OVERRIDE;
- virtual void _emitStoreImpl(IRStore* store) SLANG_OVERRIDE;
-
void _emitGLSLTextureOrTextureSamplerType(IRTextureTypeBase* type, char const* baseName);
void _emitGLSLStructuredBuffer(IRGlobalParam* varDecl, IRHLSLStructuredBufferTypeBase* structuredBufferType);
diff --git a/source/slang/slang-emit-hlsl.cpp b/source/slang/slang-emit-hlsl.cpp
index 30de45773..66902a624 100644
--- a/source/slang/slang-emit-hlsl.cpp
+++ b/source/slang/slang-emit-hlsl.cpp
@@ -1136,31 +1136,6 @@ void HLSLSourceEmitter::_emitPrefixTypeAttr(IRAttr* attr)
}
}
-void HLSLSourceEmitter::_emitInstAsVarInitializerImpl(IRInst* inst)
-{
- // Some opcodes can be folded into a variable initialization
- // by allowing the variable to be "default-constructed."
- //
- switch (inst->getOp())
- {
- case kIROp_AllocateOpaqueHandle:
- if (shouldFoldInstIntoUseSites(inst))
- {
- return;
- }
- break;
-
- default:
- break;
- }
-
- // We fall back to the default behavior for all targets,
- // which is to emit `inst` as an initial-value expression
- // after an `=`.
- //
- Super::_emitInstAsVarInitializerImpl(inst);
-}
-
void HLSLSourceEmitter::emitSimpleFuncParamImpl(IRParam* param)
{
emitRateQualifiers(param);
diff --git a/source/slang/slang-emit-hlsl.h b/source/slang/slang-emit-hlsl.h
index f2440fe38..08363bceb 100644
--- a/source/slang/slang-emit-hlsl.h
+++ b/source/slang/slang-emit-hlsl.h
@@ -64,8 +64,6 @@ protected:
void _emitPrefixTypeAttr(IRAttr* attr) SLANG_OVERRIDE;
- virtual void _emitInstAsVarInitializerImpl(IRInst* inst) SLANG_OVERRIDE;
-
// Emit a single `register` semantic, as appropriate for a given resource-type-specific layout info
// Keyword to use in the uniform case (`register` for globals, `packoffset` inside a `cbuffer`)
void _emitHLSLRegisterSemantic(LayoutResourceKind kind, EmitVarChain* chain, char const* uniformSemanticSpelling = "register");
diff --git a/source/slang/slang-ir-specialize-resources.cpp b/source/slang/slang-ir-specialize-resources.cpp
index 620c4e508..87d00a32b 100644
--- a/source/slang/slang-ir-specialize-resources.cpp
+++ b/source/slang/slang-ir-specialize-resources.cpp
@@ -355,6 +355,12 @@ struct ResourceOutputSpecializationPass
if(as<IRSamplerStateTypeBase>(type))
return true;
+ if (as<IRRayQueryType>(type))
+ return true;
+
+ if (as<IRHitObjectType>(type))
+ return true;
+
// TODO: more cases here?
return false;
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index bbd494ee9..ed1da3d25 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -563,6 +563,10 @@ struct IRGenContext
// The IR witness value to use for `ThisType`
IRInst* thisTypeWitness = nullptr;
+ // The return destination parameter to write to at return sites.
+ // (For use by functions that returns non-copyable types)
+ LoweredValInfo returnDestination;
+
bool includeDebugInfo = false;
explicit IRGenContext(SharedIRGenContext* inShared, ASTBuilder* inAstBuilder)
@@ -804,6 +808,11 @@ LoweredValInfo lowerRValueExpr(
IRGenContext* context,
Expr* expr);
+void lowerRValueExprWithDestination(
+ IRGenContext* context,
+ LoweredValInfo destination,
+ Expr* expr);
+
IRType* lowerType(
IRGenContext* context,
Type* type);
@@ -1038,8 +1047,6 @@ LoweredValInfo extractField(
}
}
-
-
LoweredValInfo materialize(
IRGenContext* context,
LoweredValInfo lowered)
@@ -1232,6 +1239,12 @@ void assign(
LoweredValInfo const& left,
LoweredValInfo const& right);
+void assignExpr(
+ IRGenContext* context,
+ const LoweredValInfo& inLeft,
+ Expr* rightExpr,
+ SourceLoc assignmentLoc);
+
IRInst* getAddress(
IRGenContext* context,
LoweredValInfo const& inVal,
@@ -2703,6 +2716,9 @@ struct IRLoweringParameterInfo
// Is this the representation of a `this` parameter?
bool isThisParam = false;
+
+ // Is this the destination of address for non-copyable return val?
+ bool isReturnDestination = false;
};
//
// We need a way to be able to create a `IRLoweringParameterInfo` given the declaration
@@ -2772,6 +2788,19 @@ void addThisParameter(
ioParameterLists->params.add(info);
}
+
+void maybeAddReturnDestinationParam(ParameterLists* ioParameterLists, Type* resultType)
+{
+ if (isNonCopyableType(resultType))
+ {
+ IRLoweringParameterInfo info;
+ info.type = resultType;
+ info.decl = nullptr;
+ info.direction = kParameterDirection_Ref;
+ info.isReturnDestination = true;
+ ioParameterLists->params.add(info);
+ }
+}
//
// And here is our function that will do the recursive walk:
void collectParameterLists(
@@ -2842,6 +2871,7 @@ void collectParameterLists(
{
ioParameterLists->params.add(getParameterInfo(context, paramDeclRef));
}
+ maybeAddReturnDestinationParam(ioParameterLists, getResultType(context->astBuilder, callableDeclRef));
}
}
}
@@ -2883,6 +2913,10 @@ struct FuncDeclBaseTypeInfo
IRType* resultType;
ParameterLists parameterLists;
List<IRType*> paramTypes;
+ // If the function returns a non-copyable value, this
+ // flag is set to indicate that the result should be
+ // returned via the last ref parameter.
+ bool returnViaLastRefParam = false;
};
void _lowerFuncDeclBaseTypeInfo(
@@ -2947,24 +2981,34 @@ void _lowerFuncDeclBaseTypeInfo(
}
auto& irResultType = outInfo.resultType;
- irResultType = lowerType(context, getResultType(context->astBuilder, declRef));
-
- if (auto setterDeclRef = declRef.as<SetterDecl>())
+
+ if (parameterLists.params.getCount() && parameterLists.params.getLast().isReturnDestination)
{
- // A `set` accessor always returns `void`
- //
- // TODO: We should handle this by making the result
- // type of a `set` accessor be represented accurately
- // at the AST level (ditto for the `ref` case below).
- //
- irResultType = builder->getVoidType();
+ irResultType = context->irBuilder->getVoidType();
+ outInfo.returnViaLastRefParam = true;
}
-
- if( auto refAccessorDeclRef = declRef.as<RefAccessorDecl>() )
+ else
{
- // A `ref` accessor needs to return a *pointer* to the value
- // being accessed, rather than a simple value.
- irResultType = builder->getPtrType(irResultType);
+ irResultType = lowerType(context, getResultType(context->astBuilder, declRef));
+
+
+ if (auto setterDeclRef = declRef.as<SetterDecl>())
+ {
+ // A `set` accessor always returns `void`
+ //
+ // TODO: We should handle this by making the result
+ // type of a `set` accessor be represented accurately
+ // at the AST level (ditto for the `ref` case below).
+ //
+ irResultType = builder->getVoidType();
+ }
+
+ if (auto refAccessorDeclRef = declRef.as<RefAccessorDecl>())
+ {
+ // A `ref` accessor needs to return a *pointer* to the value
+ // being accessed, rather than a simple value.
+ irResultType = builder->getPtrType(irResultType);
+ }
}
if (!getErrorCodeType(context->astBuilder, declRef)->equals(context->astBuilder->getBottomType()))
@@ -3023,10 +3067,8 @@ static LoweredValInfo _emitCallToAccessor(
return result;
}
-//
-
template<typename Derived>
-struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
+struct ExprLoweringContext
{
static bool isLValueContext() { return Derived::_isLValueContext(); }
@@ -3035,20 +3077,493 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
IRBuilder* getBuilder() { return context->irBuilder; }
ASTBuilder* getASTBuilder() { return context->astBuilder; }
+
+ struct ResolvedCallInfo
+ {
+ DeclRef<Decl> funcDeclRef;
+ Expr* baseExpr = nullptr;
+ };
+
+ // Try to resolve a the function expression for a call
+ // into a reference to a specific declaration, along
+ // with some contextual information about the declaration
+ // we are calling.
+ bool tryResolveDeclRefForCall(
+ Expr* funcExpr,
+ ResolvedCallInfo* outInfo)
+ {
+ // TODO: unwrap any "identity" expressions that might
+ // be wrapping the callee.
+
+ // First look to see if the expression references a
+ // declaration at all.
+ auto declRefExpr = as<DeclRefExpr>(funcExpr);
+ if (!declRefExpr)
+ return false;
+
+ // A little bit of future proofing here: if we ever
+ // allow higher-order functions, then we might be
+ // calling through a variable/field that has a function
+ // type, but is not itself a function.
+ // In such a case we should be careful to not statically
+ // resolve things.
+ //
+ if (auto callableDecl = as<CallableDecl>(declRefExpr->declRef.getDecl()))
+ {
+ // Okay, the declaration is directly callable, so we can continue.
+ }
+ else
+ {
+ // The callee declaration isn't itself a callable (it must have
+ // a function type, though).
+ return false;
+ }
+
+ // Now we can look at the specific kinds of declaration references,
+ // and try to tease them apart.
+ if (auto memberFuncExpr = as<MemberExpr>(funcExpr))
+ {
+ outInfo->funcDeclRef = memberFuncExpr->declRef;
+ outInfo->baseExpr = memberFuncExpr->baseExpression;
+ return true;
+ }
+ else if (auto staticMemberFuncExpr = as<StaticMemberExpr>(funcExpr))
+ {
+ outInfo->funcDeclRef = staticMemberFuncExpr->declRef;
+ return true;
+ }
+ else if (auto varExpr = as<VarExpr>(funcExpr))
+ {
+ outInfo->funcDeclRef = varExpr->declRef;
+ return true;
+ }
+ else
+ {
+ // Seems to be a case of declaration-reference we don't know about.
+ SLANG_UNEXPECTED("unknown declaration reference kind");
+ //return false;
+ }
+ }
+
+ /// Return `expr` with any outer casts to interface types stripped away
+ Expr* maybeIgnoreCastToInterface(Expr* expr)
+ {
+ auto e = expr;
+ while (auto castExpr = as<CastToSuperTypeExpr>(e))
+ {
+ if (auto declRefType = as<DeclRefType>(e->type))
+ {
+ if (declRefType->getDeclRef().as<InterfaceDecl>())
+ {
+ e = castExpr->valueArg;
+ continue;
+ }
+ }
+ else if (auto andType = as<AndType>(e->type))
+ {
+ // TODO: We might eventually need to tell the difference
+ // between conjunctions of interfaces and conjunctions
+ // that might include non-interface types.
+ //
+ // For now we assume that any case to a conjunction
+ // is effectively a cast to an interface type.
+ //
+ e = castExpr->valueArg;
+ continue;
+ }
+ break;
+ }
+ return e;
+ }
+
+
// Lower an expression that should have the same l-value-ness
// as the visitor itself.
LoweredValInfo lowerSubExpr(Expr* expr)
{
IRBuilderSourceLocRAII sourceLocInfo(getBuilder(), expr->loc);
- return this->dispatch(expr);
+ if (isLValueContext())
+ return lowerLValueExpr(context, expr);
+ return lowerRValueExpr(context, expr);
}
- LoweredValInfo lowerSubExpr(Expr* expr, IRGenContext* subContext)
+ /// Create IR instructions for an argument at a call site, based on
+ /// AST-level expressions plus function signature information.
+ ///
+ /// The `funcType` parameter is always required, and specifies the types
+ /// of all the parameters. The `funcDeclRef` parameter is only required
+ /// if there are parameter positions for which the matching argument is
+ /// absent.
+ ///
+ void addDirectCallArgs(
+ InvokeExpr* expr,
+ Index argIndex,
+ IRType* paramType,
+ ParameterDirection paramDirection,
+ DeclRef<ParamDecl> paramDeclRef,
+ List<IRInst*>* ioArgs,
+ List<OutArgumentFixup>* ioFixups)
{
- IRBuilderSourceLocRAII sourceLocInfo(getBuilder(), expr->loc);
- Derived d;
- d.context = subContext;
- return d.dispatch(expr);
+ Count argCount = expr->arguments.getCount();
+ if (argIndex < argCount)
+ {
+ auto argExpr = expr->arguments[argIndex];
+ addCallArgsForParam(context, paramType, paramDirection, argExpr, ioArgs, ioFixups);
+ }
+ else
+ {
+ // We have run out of arguments supplied at the call site,
+ // but there are still parameters remaining. This must mean
+ // that these parameters have default argument expressions
+ // associated with them.
+ //
+ // Currently we simply extract the initial-value expression
+ // from the parameter declaration and then lower it in
+ // the context of the caller.
+ //
+ // Note that the expression could involve subsitutions because
+ // in the general case it could depend on the generic parameters
+ // used the specialize the callee. For now we do not handle that
+ // case, and simply ignore generic arguments.
+ //
+ SubstExpr<Expr> argExpr = getInitExpr(getASTBuilder(), paramDeclRef);
+ SLANG_ASSERT(argExpr);
+
+ IRGenEnv subEnvStorage;
+ IRGenEnv* subEnv = &subEnvStorage;
+ subEnv->outer = context->env;
+
+ IRGenContext subContextStorage = *context;
+ IRGenContext* subContext = &subContextStorage;
+ subContext->env = subEnv;
+
+ _lowerSubstitutionEnv(subContext, argExpr.getSubsts() ? argExpr.getSubsts().declRef : nullptr);
+
+ addCallArgsForParam(subContext, paramType, paramDirection, argExpr.getExpr(), ioArgs, ioFixups);
+
+ // TODO: The approach we are taking here to default arguments
+ // is simplistic, and has consequences for the front-end as
+ // well as binary serialization of modules.
+ //
+ // We could consider some more refined approaches where, e.g.,
+ // functions with default arguments generate multiple IR-level
+ // functions, that compute and provide the default values.
+ //
+ // Alternatively, each parameter with defaults could be generated
+ // into its own callable function that provides the default value,
+ // so that calling modules can call into a pre-generated function.
+ //
+ // Each of these options involves trade-offs, and we need to
+ // make a conscious decision at some point.
+
+ // Assert that such an expression must have been present.
+ }
+ }
+
+ void addDirectCallArgs(
+ InvokeExpr* expr,
+ FuncType* funcType,
+ List<IRInst*>* ioArgs,
+ List<OutArgumentFixup>* ioFixups)
+ {
+ Count argCount = expr->arguments.getCount();
+ SLANG_ASSERT(argCount == funcType->getParamCount());
+
+ for (Index i = 0; i < argCount; ++i)
+ {
+ IRType* paramType = lowerType(context, funcType->getParamType(i));
+ ParameterDirection paramDirection = funcType->getParamDirection(i);
+ addDirectCallArgs(expr, i, paramType, paramDirection, DeclRef<ParamDecl>(), ioArgs, ioFixups);
+ }
+ }
+
+ void addDirectCallArgs(
+ InvokeExpr* expr,
+ DeclRef<CallableDecl> funcDeclRef,
+ List<IRInst*>* ioArgs,
+ List<OutArgumentFixup>* ioFixups)
+ {
+ Count argCounter = 0;
+ for (auto paramDeclRef : getMembersOfType<ParamDecl>(getASTBuilder(), funcDeclRef))
+ {
+ auto paramDecl = paramDeclRef.getDecl();
+ IRType* paramType = lowerType(context, getType(getASTBuilder(), paramDeclRef));
+ auto paramDirection = getParameterDirection(paramDecl);
+
+ Index argIndex = argCounter++;
+ addDirectCallArgs(expr, argIndex, paramType, paramDirection, paramDeclRef, ioArgs, ioFixups);
+ }
+ }
+
+ // Add arguments that appeared directly in an argument list
+ // to the list of argument values for a call.
+ void addDirectCallArgs(
+ InvokeExpr* expr,
+ DeclRef<Decl> funcDeclRef,
+ List<IRInst*>* ioArgs,
+ List<OutArgumentFixup>* ioFixups)
+ {
+ if (auto callableDeclRef = funcDeclRef.as<CallableDecl>())
+ {
+ addDirectCallArgs(expr, callableDeclRef, ioArgs, ioFixups);
+ }
+ else
+ {
+ SLANG_UNEXPECTED("callee was not a callable decl");
+ }
+ }
+
+ void addFuncBaseArgs(
+ LoweredValInfo funcVal,
+ List<IRInst*>* /*ioArgs*/)
+ {
+ switch (funcVal.flavor)
+ {
+ default:
+ return;
+ }
+ }
+
+
+ void _lowerSubstitutionArg(IRGenContext* subContext, GenericAppDeclRef* subst, Decl* paramDecl, Index argIndex)
+ {
+ SLANG_ASSERT(argIndex < subst->getArgs().getCount());
+ auto argVal = lowerVal(subContext, subst->getArgs()[argIndex]);
+ subContext->setValue(paramDecl, argVal);
+ }
+
+ void _lowerSubstitutionEnv(IRGenContext* subContext, DeclRefBase* subst)
+ {
+ if (!subst) return;
+ _lowerSubstitutionEnv(subContext, subst->getBase());
+
+ if (auto genSubst = as<GenericAppDeclRef>(subst))
+ {
+ auto genDecl = genSubst->getGenericDecl();
+
+ Index argCounter = 0;
+ for (auto memberDecl : genDecl->members)
+ {
+ if (auto typeParamDecl = as<GenericTypeParamDecl>(memberDecl))
+ {
+ _lowerSubstitutionArg(subContext, genSubst, typeParamDecl, argCounter++);
+ }
+ else if (auto valParamDecl = as<GenericValueParamDecl>(memberDecl))
+ {
+ _lowerSubstitutionArg(subContext, genSubst, valParamDecl, argCounter++);
+ }
+ }
+ for (auto memberDecl : genDecl->members)
+ {
+ if (auto constraintDecl = as<GenericTypeConstraintDecl>(memberDecl))
+ {
+ _lowerSubstitutionArg(subContext, genSubst, constraintDecl, argCounter++);
+ }
+ }
+ }
+ // TODO: also need to handle this-type substitution here?
+ }
+
+ /// Lower an invoke expr, and attempt to fuse a store of the expr's result into destination.
+ /// If the store is fused, returns LoweredValInfo::None. Otherwise, returns the IR val representing the RValue.
+ LoweredValInfo visitInvokeExprImpl(InvokeExpr* expr, LoweredValInfo destination, const TryClauseEnvironment& tryEnv)
+ {
+ auto type = lowerType(context, expr->type);
+
+ // We are going to look at the syntactic form of
+ // the "function" expression, so that we can avoid
+ // a lot of complexity that would come from lowering
+ // it as a general expression first, and then trying
+ // to apply it. For example, given `obj.f(a,b)` we
+ // will try to detect that we are trying to compute
+ // something like `ObjType::f(obj, a, b)` (in pseudo-code),
+ // rather than trying to construct a meaningful
+ // intermediate value for `obj.f` first.
+ //
+ // Note that this doe not preclude having support
+ // for directly generating code from `obj.f` - it
+ // just may be that such usage is more complicated.
+
+ // Along the way, we may end up collecting additional
+ // arguments that will be part of the call.
+ List<IRInst*> irArgs;
+
+ // We will also collect "fixup" actions that need
+ // to be performed after the call, in order to
+ // copy the final values for `out` parameters
+ // back to their arguments.
+ List<OutArgumentFixup> argFixups;
+
+ auto funcExpr = expr->functionExpr;
+ ResolvedCallInfo resolvedInfo;
+ if (tryResolveDeclRefForCall(funcExpr, &resolvedInfo))
+ {
+ // In this case we know exactly what declaration we
+ // are going to call, and so we can resolve things
+ // appropriately.
+ auto funcDeclRef = resolvedInfo.funcDeclRef;
+ auto baseExpr = resolvedInfo.baseExpr;
+
+ // If the thing being invoked is a subscript operation,
+ // then we need to handle multiple extra details
+ // that don't arise for other kinds of calls.
+ //
+ // TODO: subscript operations probably deserve to
+ // be handled on their own path for this reason...
+ //
+ if (auto subscriptDeclRef = funcDeclRef.template as<SubscriptDecl>())
+ {
+ // A reference to a subscript declaration is a special case,
+ // because it is not possible to call a subscript directly;
+ // we must call one of its accessors.
+ //
+ auto loweredBase = lowerSubExpr(baseExpr);
+ addDirectCallArgs(expr, funcDeclRef, &irArgs, &argFixups);
+ auto result = lowerStorageReference(context, type, subscriptDeclRef, loweredBase, irArgs.getCount(), irArgs.getBuffer());
+
+ // TODO: Applying the fixups for arguments to the subscript at this point
+ // won't technically be correct, since the call to the subscript may
+ // not have occured at this point.
+ //
+ // It seems like we need to either:
+ //
+ // * Capture the arguments to the subscript as `LoweredValInfo` instead of `IRInst*`
+ // so that we can deal with everything related to fixups around the actual call
+ // site.
+ //
+ // OR
+ //
+ // * Handle everything to do with "fixups" differently, by treating them as deferred
+ // actions that gert queued up on the context itself and then flushed at certain
+ // well-defined points, so that we don't have to be as careful around them.
+ //
+ // OR
+ //
+ // * Switch to a more "destination-driven" approach to code generation, where we
+ // can determine on entry to the lowering of a sub-expression whether it will be
+ // used for read, write, or read/write, and resolve things like the choice of
+ // accessor at that point instead.
+ //
+ applyOutArgumentFixups(context, argFixups);
+ return result;
+ }
+
+ // First comes the `this` argument if we are calling
+ // a member function:
+ if (baseExpr)
+ {
+ // The base expression might be an "upcast" to a base interface, in
+ // which case we don't want to emit the result of the cast, but instead
+ // the source.
+ //
+ baseExpr = this->maybeIgnoreCastToInterface(baseExpr);
+
+ auto thisType = getThisParamTypeForCallable(context, funcDeclRef);
+ auto irThisType = lowerType(context, thisType);
+ addCallArgsForParam(
+ context,
+ irThisType,
+ getThisParamDirection(funcDeclRef.getDecl(), kParameterDirection_In),
+ baseExpr,
+ &irArgs,
+ &argFixups);
+ }
+
+ // Then we have the "direct" arguments to the call.
+ // These may include `out` and `inout` arguments that
+ // require "fixup" work on the other side.
+ //
+ FuncDeclBaseTypeInfo funcTypeInfo;
+ _lowerFuncDeclBaseTypeInfo(context, funcDeclRef.template as<FunctionDeclBase>(), funcTypeInfo);
+
+ auto funcType = funcTypeInfo.type;
+ addDirectCallArgs(expr, funcDeclRef, &irArgs, &argFixups);
+
+ LoweredValInfo result;
+ if (funcTypeInfo.returnViaLastRefParam)
+ {
+ // If the function returns a non-copyable type, then we need to
+ // pass in the destination that receives the result value as an `__ref` parameter.
+ //
+ if (destination.flavor != LoweredValInfo::Flavor::None)
+ {
+ // If we have a known destination, we can use it directly as argument to the call.
+ irArgs.add(destination.val);
+ result = LoweredValInfo();
+ }
+ else
+ {
+ // Otherwise, we need to create a temporary variable to hold the result.
+ //
+ auto tempVar = context->irBuilder->emitVar(tryGetPointedToType(context->irBuilder, funcTypeInfo.paramTypes.getLast()));
+ irArgs.add(tempVar);
+ result = LoweredValInfo::ptr(tempVar);
+ }
+ }
+
+ auto callResult = emitCallToDeclRef(
+ context,
+ type,
+ funcDeclRef,
+ funcType,
+ irArgs,
+ tryEnv);
+ applyOutArgumentFixups(context, argFixups);
+
+ if (funcTypeInfo.returnViaLastRefParam)
+ return result;
+ return callResult;
+ }
+ else if (auto funcType = as<FuncType>(expr->functionExpr->type))
+ {
+ auto funcVal = lowerRValueExpr(context, expr->functionExpr);
+ addDirectCallArgs(expr, funcType, &irArgs, &argFixups);
+
+ auto result = emitCallToVal(context, type, funcVal, irArgs.getCount(), irArgs.getBuffer(), tryEnv);
+
+ applyOutArgumentFixups(context, argFixups);
+ return result;
+ }
+
+
+ // TODO: In this case we should be emitting code for the callee as
+ // an ordinary expression, then emitting the arguments according
+ // to the type information on the callee (e.g., which parameters
+ // are `out` or `inout`, and then finally emitting the `call`
+ // instruction.
+ //
+ // We don't currently have the case of emitting arguments according
+ // to function type info (instead of declaration info), and really
+ // this case can't occur unless we start adding first-class functions
+ // to the source language.
+ //
+ // For now we just bail out with an error.
+ //
+ SLANG_UNEXPECTED("could not resolve target declaration for call");
+ UNREACHABLE_RETURN(LoweredValInfo());
+ }
+
+};
+
+template<typename Derived>
+struct ExprLoweringVisitorBase : public ExprVisitor<Derived, LoweredValInfo>
+{
+ static bool isLValueContext() { return Derived::_isLValueContext(); }
+
+ ExprLoweringContext<Derived> sharedLoweringContext;
+
+ IRGenContext*& context;
+
+ ExprLoweringVisitorBase()
+ : context(sharedLoweringContext.context)
+ {
+ }
+
+ IRBuilder* getBuilder() { return context->irBuilder; }
+ ASTBuilder* getASTBuilder() { return context->astBuilder; }
+ LoweredValInfo lowerSubExpr(Expr* expr)
+ {
+ return sharedLoweringContext.lowerSubExpr(expr);
}
LoweredValInfo visitIncompleteExpr(IncompleteExpr*)
@@ -3380,12 +3895,17 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
return context->thisVal;
}
+ LoweredValInfo visitReturnValExpr(ReturnValExpr*)
+ {
+ return context->returnDestination;
+ }
+
LoweredValInfo visitMemberExpr(MemberExpr* expr)
{
auto loweredType = lowerType(context, expr->type);
auto baseExpr = expr->baseExpression;
- baseExpr = maybeIgnoreCastToInterface(baseExpr);
+ baseExpr = sharedLoweringContext.maybeIgnoreCastToInterface(baseExpr);
auto loweredBase = lowerSubExpr(baseExpr);
auto declRef = expr->declRef;
@@ -3812,281 +4332,6 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
UNREACHABLE_RETURN(LoweredValInfo());
}
- void _lowerSubstitutionArg(IRGenContext* subContext, GenericAppDeclRef* subst, Decl* paramDecl, Index argIndex)
- {
- SLANG_ASSERT(argIndex < subst->getArgs().getCount());
- auto argVal = lowerVal(subContext, subst->getArgs()[argIndex]);
- subContext->setValue(paramDecl, argVal);
- }
-
- void _lowerSubstitutionEnv(IRGenContext* subContext, DeclRefBase* subst)
- {
- if(!subst) return;
- _lowerSubstitutionEnv(subContext, subst->getBase());
-
- if (auto genSubst = as<GenericAppDeclRef>(subst))
- {
- auto genDecl = genSubst->getGenericDecl();
-
- Index argCounter = 0;
- for( auto memberDecl: genDecl->members )
- {
- if(auto typeParamDecl = as<GenericTypeParamDecl>(memberDecl) )
- {
- _lowerSubstitutionArg(subContext, genSubst, typeParamDecl, argCounter++);
- }
- else if( auto valParamDecl = as<GenericValueParamDecl>(memberDecl) )
- {
- _lowerSubstitutionArg(subContext, genSubst, valParamDecl, argCounter++);
- }
- }
- for( auto memberDecl: genDecl->members )
- {
- if(auto constraintDecl = as<GenericTypeConstraintDecl>(memberDecl) )
- {
- _lowerSubstitutionArg(subContext, genSubst, constraintDecl, argCounter++);
- }
- }
- }
- // TODO: also need to handle this-type substitution here?
- }
-
- /// Create IR instructions for an argument at a call site, based on
- /// AST-level expressions plus function signature information.
- ///
- /// The `funcType` parameter is always required, and specifies the types
- /// of all the parameters. The `funcDeclRef` parameter is only required
- /// if there are parameter positions for which the matching argument is
- /// absent.
- ///
- void addDirectCallArgs(
- InvokeExpr* expr,
- Index argIndex,
- IRType* paramType,
- ParameterDirection paramDirection,
- DeclRef<ParamDecl> paramDeclRef,
- List<IRInst*>* ioArgs,
- List<OutArgumentFixup>* ioFixups)
- {
- Count argCount = expr->arguments.getCount();
- if (argIndex < argCount)
- {
- auto argExpr = expr->arguments[argIndex];
- addCallArgsForParam(context, paramType, paramDirection, argExpr, ioArgs, ioFixups);
- }
- else
- {
- // We have run out of arguments supplied at the call site,
- // but there are still parameters remaining. This must mean
- // that these parameters have default argument expressions
- // associated with them.
- //
- // Currently we simply extract the initial-value expression
- // from the parameter declaration and then lower it in
- // the context of the caller.
- //
- // Note that the expression could involve subsitutions because
- // in the general case it could depend on the generic parameters
- // used the specialize the callee. For now we do not handle that
- // case, and simply ignore generic arguments.
- //
- SubstExpr<Expr> argExpr = getInitExpr(getASTBuilder(), paramDeclRef);
- SLANG_ASSERT(argExpr);
-
- IRGenEnv subEnvStorage;
- IRGenEnv* subEnv = &subEnvStorage;
- subEnv->outer = context->env;
-
- IRGenContext subContextStorage = *context;
- IRGenContext* subContext = &subContextStorage;
- subContext->env = subEnv;
-
- _lowerSubstitutionEnv(subContext, argExpr.getSubsts() ? argExpr.getSubsts().declRef : nullptr);
-
- addCallArgsForParam(subContext, paramType, paramDirection, argExpr.getExpr(), ioArgs, ioFixups);
-
- // TODO: The approach we are taking here to default arguments
- // is simplistic, and has consequences for the front-end as
- // well as binary serialization of modules.
- //
- // We could consider some more refined approaches where, e.g.,
- // functions with default arguments generate multiple IR-level
- // functions, that compute and provide the default values.
- //
- // Alternatively, each parameter with defaults could be generated
- // into its own callable function that provides the default value,
- // so that calling modules can call into a pre-generated function.
- //
- // Each of these options involves trade-offs, and we need to
- // make a conscious decision at some point.
-
- // Assert that such an expression must have been present.
- }
- }
-
- void addDirectCallArgs(
- InvokeExpr* expr,
- FuncType* funcType,
- List<IRInst*>* ioArgs,
- List<OutArgumentFixup>* ioFixups)
- {
- Count argCount = expr->arguments.getCount();
- SLANG_ASSERT(argCount == funcType->getParamCount());
-
- for(Index i = 0; i < argCount; ++i)
- {
- IRType* paramType = lowerType(context, funcType->getParamType(i));
- ParameterDirection paramDirection = funcType->getParamDirection(i);
- addDirectCallArgs(expr, i, paramType, paramDirection, DeclRef<ParamDecl>(), ioArgs, ioFixups);
- }
- }
-
-
- void addDirectCallArgs(
- InvokeExpr* expr,
- DeclRef<CallableDecl> funcDeclRef,
- List<IRInst*>* ioArgs,
- List<OutArgumentFixup>* ioFixups)
- {
- Count argCounter = 0;
- for (auto paramDeclRef : getMembersOfType<ParamDecl>(getASTBuilder(), funcDeclRef))
- {
- auto paramDecl = paramDeclRef.getDecl();
- IRType* paramType = lowerType(context, getType(getASTBuilder(), paramDeclRef));
- auto paramDirection = getParameterDirection(paramDecl);
-
- Index argIndex = argCounter++;
- addDirectCallArgs(expr, argIndex, paramType, paramDirection, paramDeclRef, ioArgs, ioFixups);
- }
- }
-
- // Add arguments that appeared directly in an argument list
- // to the list of argument values for a call.
- void addDirectCallArgs(
- InvokeExpr* expr,
- DeclRef<Decl> funcDeclRef,
- List<IRInst*>* ioArgs,
- List<OutArgumentFixup>* ioFixups)
- {
- if (auto callableDeclRef = funcDeclRef.as<CallableDecl>())
- {
- addDirectCallArgs(expr, callableDeclRef, ioArgs, ioFixups);
- }
- else
- {
- SLANG_UNEXPECTED("callee was not a callable decl");
- }
- }
-
- void addFuncBaseArgs(
- LoweredValInfo funcVal,
- List<IRInst*>* /*ioArgs*/)
- {
- switch (funcVal.flavor)
- {
- default:
- return;
- }
- }
-
- struct ResolvedCallInfo
- {
- DeclRef<Decl> funcDeclRef;
- Expr* baseExpr = nullptr;
- };
-
- // Try to resolve a the function expression for a call
- // into a reference to a specific declaration, along
- // with some contextual information about the declaration
- // we are calling.
- bool tryResolveDeclRefForCall(
- Expr* funcExpr,
- ResolvedCallInfo* outInfo)
- {
- // TODO: unwrap any "identity" expressions that might
- // be wrapping the callee.
-
- // First look to see if the expression references a
- // declaration at all.
- auto declRefExpr = as<DeclRefExpr>(funcExpr);
- if(!declRefExpr)
- return false;
-
- // A little bit of future proofing here: if we ever
- // allow higher-order functions, then we might be
- // calling through a variable/field that has a function
- // type, but is not itself a function.
- // In such a case we should be careful to not statically
- // resolve things.
- //
- if(auto callableDecl = as<CallableDecl>(declRefExpr->declRef.getDecl()))
- {
- // Okay, the declaration is directly callable, so we can continue.
- }
- else
- {
- // The callee declaration isn't itself a callable (it must have
- // a function type, though).
- return false;
- }
-
- // Now we can look at the specific kinds of declaration references,
- // and try to tease them apart.
- if (auto memberFuncExpr = as<MemberExpr>(funcExpr))
- {
- outInfo->funcDeclRef = memberFuncExpr->declRef;
- outInfo->baseExpr = memberFuncExpr->baseExpression;
- return true;
- }
- else if (auto staticMemberFuncExpr = as<StaticMemberExpr>(funcExpr))
- {
- outInfo->funcDeclRef = staticMemberFuncExpr->declRef;
- return true;
- }
- else if (auto varExpr = as<VarExpr>(funcExpr))
- {
- outInfo->funcDeclRef = varExpr->declRef;
- return true;
- }
- else
- {
- // Seems to be a case of declaration-reference we don't know about.
- SLANG_UNEXPECTED("unknown declaration reference kind");
- //return false;
- }
- }
-
- /// Return `expr` with any outer casts to interface types stripped away
- Expr* maybeIgnoreCastToInterface(Expr* expr)
- {
- auto e = expr;
- while( auto castExpr = as<CastToSuperTypeExpr>(e) )
- {
- if(auto declRefType = as<DeclRefType>(e->type))
- {
- if(declRefType->getDeclRef().as<InterfaceDecl>())
- {
- e = castExpr->valueArg;
- continue;
- }
- }
- else if( auto andType = as<AndType>(e->type) )
- {
- // TODO: We might eventually need to tell the difference
- // between conjunctions of interfaces and conjunctions
- // that might include non-interface types.
- //
- // For now we assume that any case to a conjunction
- // is effectively a cast to an interface type.
- //
- e = castExpr->valueArg;
- continue;
- }
- break;
- }
- return e;
- }
-
LoweredValInfo visitSelectExpr(SelectExpr* expr)
{
// A vector typed `select` expr will turn into a normal `select` op.
@@ -4126,158 +4371,7 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
LoweredValInfo visitInvokeExpr(InvokeExpr* expr)
{
- return visitInvokeExprImpl(expr, TryClauseEnvironment());
- }
-
- LoweredValInfo visitInvokeExprImpl(InvokeExpr* expr, const TryClauseEnvironment& tryEnv)
- {
- auto type = lowerType(context, expr->type);
-
- // We are going to look at the syntactic form of
- // the "function" expression, so that we can avoid
- // a lot of complexity that would come from lowering
- // it as a general expression first, and then trying
- // to apply it. For example, given `obj.f(a,b)` we
- // will try to detect that we are trying to compute
- // something like `ObjType::f(obj, a, b)` (in pseudo-code),
- // rather than trying to construct a meaningful
- // intermediate value for `obj.f` first.
- //
- // Note that this doe not preclude having support
- // for directly generating code from `obj.f` - it
- // just may be that such usage is more complicated.
-
- // Along the way, we may end up collecting additional
- // arguments that will be part of the call.
- List<IRInst*> irArgs;
-
- // We will also collect "fixup" actions that need
- // to be performed after the call, in order to
- // copy the final values for `out` parameters
- // back to their arguments.
- List<OutArgumentFixup> argFixups;
-
- auto funcExpr = expr->functionExpr;
- ResolvedCallInfo resolvedInfo;
- if (tryResolveDeclRefForCall(funcExpr, &resolvedInfo))
- {
- // In this case we know exactly what declaration we
- // are going to call, and so we can resolve things
- // appropriately.
- auto funcDeclRef = resolvedInfo.funcDeclRef;
- auto baseExpr = resolvedInfo.baseExpr;
-
- // If the thing being invoked is a subscript operation,
- // then we need to handle multiple extra details
- // that don't arise for other kinds of calls.
- //
- // TODO: subscript operations probably deserve to
- // be handled on their own path for this reason...
- //
- if (auto subscriptDeclRef = funcDeclRef.template as<SubscriptDecl>())
- {
- // A reference to a subscript declaration is a special case,
- // because it is not possible to call a subscript directly;
- // we must call one of its accessors.
- //
- auto loweredBase = lowerSubExpr(baseExpr);
- addDirectCallArgs(expr, funcDeclRef, &irArgs, &argFixups);
- auto result = lowerStorageReference(context, type, subscriptDeclRef, loweredBase, irArgs.getCount(), irArgs.getBuffer());
-
- // TODO: Applying the fixups for arguments to the subscript at this point
- // won't technically be correct, since the call to the subscript may
- // not have occured at this point.
- //
- // It seems like we need to either:
- //
- // * Capture the arguments to the subscript as `LoweredValInfo` instead of `IRInst*`
- // so that we can deal with everything related to fixups around the actual call
- // site.
- //
- // OR
- //
- // * Handle everything to do with "fixups" differently, by treating them as deferred
- // actions that gert queued up on the context itself and then flushed at certain
- // well-defined points, so that we don't have to be as careful around them.
- //
- // OR
- //
- // * Switch to a more "destination-driven" approach to code generation, where we
- // can determine on entry to the lowering of a sub-expression whether it will be
- // used for read, write, or read/write, and resolve things like the choice of
- // accessor at that point instead.
- //
- applyOutArgumentFixups(context, argFixups);
- return result;
- }
-
- // First comes the `this` argument if we are calling
- // a member function:
- if (baseExpr)
- {
- // The base expression might be an "upcast" to a base interface, in
- // which case we don't want to emit the result of the cast, but instead
- // the source.
- //
- baseExpr = maybeIgnoreCastToInterface(baseExpr);
-
- auto thisType = getThisParamTypeForCallable(context, funcDeclRef);
- auto irThisType = lowerType(context, thisType);
- addCallArgsForParam(
- context,
- irThisType,
- getThisParamDirection(funcDeclRef.getDecl(), kParameterDirection_In),
- baseExpr,
- &irArgs,
- &argFixups);
- }
-
- // Then we have the "direct" arguments to the call.
- // These may include `out` and `inout` arguments that
- // require "fixup" work on the other side.
- //
- FuncDeclBaseTypeInfo funcTypeInfo;
- _lowerFuncDeclBaseTypeInfo(context, funcDeclRef.template as<FunctionDeclBase>(), funcTypeInfo);
-
- auto funcType = funcTypeInfo.type;
- addDirectCallArgs(expr, funcDeclRef, &irArgs, &argFixups);
- auto result = emitCallToDeclRef(
- context,
- type,
- funcDeclRef,
- funcType,
- irArgs,
- tryEnv);
- applyOutArgumentFixups(context, argFixups);
- return result;
- }
- else if(auto funcType = as<FuncType>(expr->functionExpr->type))
- {
- auto funcVal = lowerRValueExpr(context, expr->functionExpr);
- addDirectCallArgs(expr, funcType, &irArgs, &argFixups);
-
- auto result = emitCallToVal(context, type, funcVal, irArgs.getCount(), irArgs.getBuffer(), tryEnv);
-
- applyOutArgumentFixups(context, argFixups);
- return result;
- }
-
-
- // TODO: In this case we should be emitting code for the callee as
- // an ordinary expression, then emitting the arguments according
- // to the type information on the callee (e.g., which parameters
- // are `out` or `inout`, and then finally emitting the `call`
- // instruction.
- //
- // We don't currently have the case of emitting arguments according
- // to function type info (instead of declaration info), and really
- // this case can't occur unless we start adding first-class functions
- // to the source language.
- //
- // For now we just bail out with an error.
- //
- SLANG_UNEXPECTED("could not resolve target declaration for call");
- UNREACHABLE_RETURN(LoweredValInfo());
+ return sharedLoweringContext.visitInvokeExprImpl(expr, LoweredValInfo(), TryClauseEnvironment());
}
/// Emit code for a `try` invoke.
@@ -4287,7 +4381,7 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
assert(invokeExpr);
TryClauseEnvironment tryEnv;
tryEnv.clauseType = expr->tryClauseType;
- return visitInvokeExprImpl(invokeExpr, tryEnv);
+ return sharedLoweringContext.visitInvokeExprImpl(invokeExpr, LoweredValInfo(), tryEnv);
}
/// Emit code to cast `value` to a concrete `superType` (e.g., a `struct`).
@@ -4540,8 +4634,7 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
// based on the resulting values.
//
auto leftVal = lowerLValueExpr(context, expr->left);
- auto rightVal = lowerRValueExpr(context, expr->right);
- assign(context, leftVal, rightVal);
+ assignExpr(context, leftVal, expr->right, expr->loc);
// The result value of the assignment expression is
// the value of the left-hand side (and it is expected
@@ -4780,7 +4873,7 @@ struct LValueExprLoweringVisitor : ExprLoweringVisitorBase<LValueExprLoweringVis
}
};
-struct RValueExprLoweringVisitor : ExprLoweringVisitorBase<RValueExprLoweringVisitor>
+struct RValueExprLoweringVisitor : public ExprLoweringVisitorBase<RValueExprLoweringVisitor>
{
static bool _isLValueContext() { return false; }
@@ -4868,6 +4961,55 @@ struct RValueExprLoweringVisitor : ExprLoweringVisitorBase<RValueExprLoweringVis
}
};
+// ExprLoweringVisitor that fuses the destination assignment.
+//
+struct DestinationDrivenRValueExprLoweringVisitor
+ : ExprVisitor<DestinationDrivenRValueExprLoweringVisitor>
+{
+ ExprLoweringContext<DestinationDrivenRValueExprLoweringVisitor> sharedLoweringContext;
+ LoweredValInfo destination;
+
+ IRGenContext*& context;
+ DestinationDrivenRValueExprLoweringVisitor()
+ : context(sharedLoweringContext.context)
+ {}
+
+ static bool _isLValueContext() { return false; }
+
+ // The default case is lower the rvalue expr independently and then assign to destination.
+ void visitExpr(Expr* expr)
+ {
+ auto rValue = lowerRValueExpr(context, expr);
+ assign(context, destination, rValue);
+ }
+
+ void visitInvokeExpr(InvokeExpr* expr)
+ {
+ LoweredValInfo resultRVal;
+ {
+ IRBuilderSourceLocRAII sourceLocInfo(context->irBuilder, expr->loc);
+ resultRVal = sharedLoweringContext.visitInvokeExprImpl(expr, destination, TryClauseEnvironment{});
+ }
+ if (resultRVal.flavor != LoweredValInfo::Flavor::None)
+ {
+ // If we weren't able to fuse the destination write during lowering rvalue,
+ // we should insert the assign operation now.
+ assign(context, destination, resultRVal);
+ }
+ }
+
+ /// Emit code for a `try` invoke.
+ LoweredValInfo visitTryExpr(TryExpr* expr)
+ {
+ auto invokeExpr = as<InvokeExpr>(expr->base);
+ assert(invokeExpr);
+ TryClauseEnvironment tryEnv;
+ tryEnv.clauseType = expr->tryClauseType;
+ return sharedLoweringContext.visitInvokeExprImpl(invokeExpr, destination, tryEnv);
+ }
+
+};
+
LoweredValInfo lowerLValueExpr(
IRGenContext* context,
Expr* expr)
@@ -4892,6 +5034,17 @@ LoweredValInfo lowerRValueExpr(
return info;
}
+void lowerRValueExprWithDestination(
+ IRGenContext* context,
+ LoweredValInfo destination,
+ Expr* expr)
+{
+ DestinationDrivenRValueExprLoweringVisitor visitor;
+ visitor.context = context;
+ visitor.destination = destination;
+ visitor.dispatch(expr);
+}
+
struct StmtLoweringVisitor : StmtVisitor<StmtLoweringVisitor>
{
IRGenContext* context;
@@ -5464,6 +5617,14 @@ struct StmtLoweringVisitor : StmtVisitor<StmtLoweringVisitor>
//
if( auto expr = stmt->expression )
{
+ if (context->returnDestination.flavor != LoweredValInfo::Flavor::None)
+ {
+ // If this function should return via a __ref parameter, do that and return void.
+ lowerRValueExprWithDestination(context, context->returnDestination, expr);
+ getBuilder()->emitReturn();
+ return;
+ }
+
// If the AST `return` statement had an expression, then we
// need to lower it to the IR at this point, both to
// compute its value and (in case we are returning a
@@ -6125,6 +6286,30 @@ IRInst* getAddress(
return nullptr;
}
+void assignExpr(
+ IRGenContext* context,
+ const LoweredValInfo& inLeft,
+ Expr* rightExpr,
+ SourceLoc assignmentLoc)
+{
+ auto left = tryGetAddress(context, inLeft, TryGetAddressMode::Default);
+ IRBuilderSourceLocRAII locRAII(context->irBuilder, assignmentLoc);
+ switch (left.flavor)
+ {
+ case LoweredValInfo::Flavor::Ptr:
+ {
+ lowerRValueExprWithDestination(context, left, rightExpr);
+ }
+ break;
+ default:
+ {
+ auto right = lowerRValueExpr(context, rightExpr);
+ assign(context, inLeft, right);
+ }
+ break;
+ }
+}
+
void assign(
IRGenContext* context,
LoweredValInfo const& inLeft,
@@ -7228,6 +7413,8 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
subContextStorage.thisType = outerContext->thisType;
subContextStorage.thisTypeWitness = outerContext->thisTypeWitness;
+
+ subContextStorage.returnDestination = LoweredValInfo();
}
IRBuilder* getBuilder() { return &subBuilderStorage; }
@@ -7381,9 +7568,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
if( auto initExpr = decl->initExpr )
{
- auto initVal = lowerRValueExpr(context, initExpr);
-
- assign(context, varVal, initVal);
+ assignExpr(context, varVal, initExpr, decl->loc);
}
context->setGlobalValue(decl, varVal);
@@ -7395,7 +7580,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
{
return Slang::getInterfaceRequirementKey(context, requirementDecl);
}
-
+
LoweredValInfo visitAssocTypeDecl(AssocTypeDecl* decl)
{
SLANG_ASSERT(decl->parentDecl != nullptr);
@@ -8700,6 +8885,9 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
paramVal = LoweredValInfo::ptr(irParam);
+ if (paramInfo.isReturnDestination)
+ subContext->returnDestination = paramVal;
+
// TODO: We might want to copy the pointed-to value into
// a temporary at the start of the function, and then copy
// back out at the end, so that we don't have to worry
@@ -8815,14 +9003,19 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
auto constructorDecl = as<ConstructorDecl>(decl);
if (constructorDecl)
{
- auto thisVar = subContext->irBuilder->emitVar(irResultType);
- subContext->thisVal = LoweredValInfo::ptr(thisVar);
-
- // For class-typed objects, we need to allocate it from heap.
- if (isClassType(irResultType))
+ if (subContext->returnDestination.flavor != LoweredValInfo::Flavor::None)
+ subContext->thisVal = subContext->returnDestination;
+ else
{
- auto allocatedObj = subContext->irBuilder->emitAllocObj(irResultType);
- subContext->irBuilder->emitStore(thisVar, allocatedObj);
+ auto thisVar = subContext->irBuilder->emitVar(irResultType);
+ subContext->thisVal = LoweredValInfo::ptr(thisVar);
+
+ // For class-typed objects, we need to allocate it from heap.
+ if (isClassType(irResultType))
+ {
+ auto allocatedObj = subContext->irBuilder->emitAllocObj(irResultType);
+ subContext->irBuilder->emitStore(thisVar, allocatedObj);
+ }
}
}
@@ -8846,8 +9039,13 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
// path in an initializer/constructor attempts
// to do an early `return;`.
//
- subContext->irBuilder->emitReturn(
- getSimpleVal(subContext, subContext->thisVal));
+ if (subContext->returnDestination.flavor != LoweredValInfo::Flavor::None)
+ subContext->irBuilder->emitReturn();
+ else
+ {
+ subContext->irBuilder->emitReturn(
+ getSimpleVal(subContext, subContext->thisVal));
+ }
}
else if (as<IRVoidType>(irResultType))
{
diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp
index b270ba713..c3eba8c58 100644
--- a/source/slang/slang-parser.cpp
+++ b/source/slang/slang-parser.cpp
@@ -5299,6 +5299,13 @@ namespace Slang
return expr;
}
+ static NodeBase* parseReturnValExpr(Parser* parser, void* /*userData*/)
+ {
+ ReturnValExpr* expr = parser->astBuilder->create<ReturnValExpr>();
+ expr->scope = parser->currentScope;
+ return expr;
+ }
+
static Expr* parseBoolLitExpr(Parser* parser, bool value)
{
BoolLiteralExpr* expr = parser->astBuilder->create<BoolLiteralExpr>();
@@ -7314,6 +7321,7 @@ namespace Slang
_makeParseExpr("this", parseThisExpr),
_makeParseExpr("true", parseTrueExpr),
_makeParseExpr("false", parseFalseExpr),
+ _makeParseExpr("__return_val", parseReturnValExpr),
_makeParseExpr("nullptr", parseNullPtrExpr),
_makeParseExpr("none", parseNoneExpr),
_makeParseExpr("try", parseTryExpr),