HLSL output parameters are denoted with the `inout` and `out` keywords in the function declaration. When an argument to an output parameter is constructed a temporary value is constructed for the argument. For `inout` pamameters the argument is initialized via copy-initialization from the argument lvalue expression to the parameter type. For `out` parameters the argument is not initialized before the call. In both cases on return of the function the temporary value is written back to the argument lvalue expression through an implicit assignment binary operator with casting as required. This change introduces a new HLSLOutArgExpr ast node which represents the output argument behavior. The OutArgExpr has three defined children: - An OpaqueValueExpr of the argument lvalue expression. - An OpaqueValueExpr of the copy-initialized parameter. - A BinaryOpExpr assigning the first with the value of the second. Fixes #87526 --------- Co-authored-by: Damyan Pepper <damyanp@microsoft.com> Co-authored-by: John McCall <rjmccall@gmail.com>
215 lines
9.5 KiB
HLSL
215 lines
9.5 KiB
HLSL
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -finclude-default-header %s -ast-dump | FileCheck %s
|
|
|
|
// Case 1: Template declaration with a call to an inout or out argument that is
|
|
// resolved based on the template parameter. For this case the template decl
|
|
// should have an UnresolvedLookupExpr for the call, and the HLSLOutArgExpr is
|
|
// built during call resolution.
|
|
|
|
// CHECK: FunctionDecl {{.*}} used fn 'void (inout int)'
|
|
void fn(inout int I) {
|
|
I += 1;
|
|
}
|
|
|
|
// CHECK: FunctionDecl {{.*}} used fn 'void (out double)'
|
|
void fn(out double F) {
|
|
F = 1.5;
|
|
}
|
|
|
|
// CHECK-LABEL: FunctionTemplateDecl {{.*}} wrapper
|
|
// CHECK-NEXT: TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T
|
|
|
|
// Verify that the template has an unresolved call.
|
|
// CHECK-NEXT: FunctionDecl {{.*}} wrapper 'T (T)'
|
|
// CHECK-NEXT: ParmVarDecl {{.*}} referenced V 'T'
|
|
// CHECK: CallExpr {{.*}} '<dependent type>'
|
|
// CHECK: UnresolvedLookupExpr {{.*}} '<overloaded function type>' lvalue (ADL) = 'fn'
|
|
|
|
// Verify that the int instantiation resolves an inout argument expression.
|
|
|
|
// CHECK-LABEL: FunctionDecl {{.*}} used wrapper 'int (int)' implicit_instantiation
|
|
// CHECK: CallExpr {{.*}} 'void'
|
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(inout int)' <FunctionToPointerDecay>
|
|
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (inout int)' lvalue Function {{.*}} 'fn' 'void (inout int)'
|
|
// CHECK-NEXT: HLSLOutArgExpr {{.*}} 'int' lvalue inout
|
|
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV:0x[0-9a-fA-F]+]] {{.*}} 'int' lvalue
|
|
// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'V' 'int'
|
|
// CHECK-NEXT: OpaqueValueExpr [[TmpOpV:0x[0-9a-fA-F]+]] {{.*}} 'int' lvalue
|
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV]] {{.*}} 'int' lvalue
|
|
|
|
// CHECK: BinaryOperator {{.*}} 'int' lvalue '='
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV]] {{.*}} 'int' lvalue
|
|
// CHECK: ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
|
|
// CHECK-NEXT: OpaqueValueExpr [[TmpOpV]] {{.*}} 'int' lvalue
|
|
|
|
|
|
// Verify that the float instantiation has an out argument expression
|
|
// containing casts to and from double.
|
|
|
|
// CHECK-LABEL: FunctionDecl {{.*}} used wrapper 'float (float)' implicit_instantiation
|
|
// CHECK: CallExpr {{.*}} 'void'
|
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(out double)' <FunctionToPointerDecay>
|
|
// CHECK-NEXT: DeclRefExpr {{.*}}'void (out double)' lvalue Function {{.*}} 'fn' 'void (out double)'
|
|
// CHECK-NEXT: HLSLOutArgExpr {{.*}} 'double' lvalue out
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV:0x[0-9a-fA-F]+]] {{.*}} 'float' lvalue
|
|
// CHECK-NEXT: DeclRefExpr {{.*}} 'float' lvalue ParmVar {{.*}} 'V' 'float'
|
|
// CHECK-NEXT: OpaqueValueExpr [[TmpOpV:0x[0-9a-fA-F]+]] {{.*}} 'double' lvalue
|
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <FloatingCast>
|
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' <LValueToRValue>
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV]] {{.*}} 'float' lvalue
|
|
|
|
// CHECK: BinaryOperator {{.*}} 'float' lvalue '='
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV]] {{.*}} 'float' lvalue
|
|
// CHECK: ImplicitCastExpr {{.*}} 'float' <FloatingCast>
|
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <LValueToRValue>
|
|
// CHECK-NEXT: OpaqueValueExpr [[TmpOpV]] {{.*}} 'double' lvalue
|
|
|
|
|
|
// Verify that the double instantiation is just an out expression.
|
|
|
|
// CHECK-LABEL: FunctionDecl {{.*}} used wrapper 'double (double)' implicit_instantiation
|
|
// CHECK: CallExpr {{.*}} 'void'
|
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(out double)' <FunctionToPointerDecay>
|
|
// CHECK-NEXT: DeclRefExpr {{.*}}'void (out double)' lvalue Function {{.*}} 'fn' 'void (out double)'
|
|
// CHECK-NEXT: HLSLOutArgExpr {{.*}} 'double' lvalue out
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV:0x[0-9a-fA-F]+]] {{.*}} 'double' lvalue
|
|
// CHECK-NEXT: DeclRefExpr {{.*}} 'double' lvalue ParmVar {{.*}} 'V' 'double'
|
|
// CHECK-NEXT: OpaqueValueExpr [[TmpOpV:0x[0-9a-fA-F]+]] {{.*}} 'double' lvalue
|
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <LValueToRValue>
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV]] {{.*}} 'double' lvalue
|
|
|
|
// CHECK: BinaryOperator {{.*}} 'double' lvalue '='
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV]] {{.*}} 'double' lvalue
|
|
// CHECK: ImplicitCastExpr {{.*}} 'double' <LValueToRValue>
|
|
// CHECK-NEXT: OpaqueValueExpr [[TmpOpV]] {{.*}} 'double' lvalue
|
|
|
|
template <typename T>
|
|
T wrapper(T V) {
|
|
fn(V);
|
|
return V;
|
|
}
|
|
|
|
// Case 2: Verify that the parameter modifier attribute is instantiated with the
|
|
// template (this one is a gimme).
|
|
|
|
// CHECK-LABEL: FunctionTemplateDecl {{.*}} fizz
|
|
|
|
// Check the pattern decl.
|
|
// CHECK: FunctionDecl {{.*}} fizz 'void (inout T)'
|
|
// CHECK-NEXT: ParmVarDecl {{.*}} referenced V 'T'
|
|
// CHECK-NEXT: HLSLParamModifierAttr {{.*}} inout
|
|
|
|
// Check the 3 instantiations (int, float, & double).
|
|
|
|
// CHECK-LABEL: FunctionDecl {{.*}} used fizz 'void (inout int)' implicit_instantiation
|
|
// CHECK: ParmVarDecl {{.*}} used V 'int &__restrict'
|
|
// CHECK-NEXT: HLSLParamModifierAttr {{.*}} inout
|
|
|
|
// CHECK-LABEL: FunctionDecl {{.*}} used fizz 'void (inout float)' implicit_instantiation
|
|
// CHECK: ParmVarDecl {{.*}} used V 'float &__restrict'
|
|
// CHECK-NEXT: HLSLParamModifierAttr {{.*}} inout
|
|
|
|
// CHECK-LABEL: FunctionDecl {{.*}} used fizz 'void (inout double)' implicit_instantiation
|
|
// CHECK: ParmVarDecl {{.*}} used V 'double &__restrict'
|
|
// CHECK-NEXT: HLSLParamModifierAttr {{.*}} inout
|
|
template <typename T>
|
|
void fizz(inout T V) {
|
|
V += 2;
|
|
}
|
|
|
|
// Case 3: Verify that HLSLOutArgExpr nodes which are present in the template
|
|
// are correctly instantiated into the instantation.
|
|
|
|
// First we check that the AST node is in the template.
|
|
|
|
// CHECK-LABEL: FunctionTemplateDecl {{.*}} buzz
|
|
|
|
// CHECK: FunctionDecl {{.*}} buzz 'T (int, T)'
|
|
// CHECK: CallExpr {{.*}} 'void'
|
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(inout int)' <FunctionToPointerDecay>
|
|
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (inout int)' lvalue Function {{.*}} 'fn' 'void (inout int)'
|
|
// CHECK-NEXT: HLSLOutArgExpr {{.*}} 'int' lvalue inout
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV:0x[0-9a-fA-F]+]] {{.*}} 'int' lvalue
|
|
// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'X' 'int'
|
|
// CHECK-NEXT: OpaqueValueExpr [[TmpOpV:0x[0-9a-fA-F]+]] {{.*}} 'int' lvalue
|
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV]] {{.*}} 'int' lvalue
|
|
// CHECK: BinaryOperator {{.*}} 'int' lvalue '='
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV]] {{.*}} 'int' lvalue
|
|
// CHECK: ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
|
|
// CHECK-NEXT: OpaqueValueExpr [[TmpOpV]] {{.*}} 'int' lvalue
|
|
|
|
|
|
|
|
// CHECK-LABEL: FunctionDecl {{.*}} used buzz 'int (int, int)' implicit_instantiation
|
|
// CHECK: CallExpr {{.*}} 'void'
|
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(inout int)' <FunctionToPointerDecay>
|
|
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (inout int)' lvalue Function {{.*}} 'fn' 'void (inout int)'
|
|
// CHECK-NEXT: HLSLOutArgExpr {{.*}} 'int' lvalue inout
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV:0x[0-9a-fA-F]+]] {{.*}} 'int' lvalue
|
|
// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'X' 'int'
|
|
// CHECK-NEXT: OpaqueValueExpr [[TmpOpV:0x[0-9a-fA-F]+]] {{.*}} 'int' lvalue
|
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV]] {{.*}} 'int' lvalue
|
|
// CHECK: BinaryOperator {{.*}} 'int' lvalue '='
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV]] {{.*}} 'int' lvalue
|
|
// CHECK: ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
|
|
// CHECK-NEXT: OpaqueValueExpr [[TmpOpV]] {{.*}} 'int' lvalue
|
|
|
|
|
|
// CHECK-LABEL: FunctionDecl {{.*}} used buzz 'float (int, float)' implicit_instantiation
|
|
// CHECK: CallExpr {{.*}} 'void'
|
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(inout int)' <FunctionToPointerDecay>
|
|
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (inout int)' lvalue Function {{.*}} 'fn' 'void (inout int)'
|
|
// CHECK-NEXT: HLSLOutArgExpr {{.*}} 'int' lvalue inout
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV:0x[0-9a-fA-F]+]] {{.*}} 'int' lvalue
|
|
// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'X' 'int'
|
|
// CHECK-NEXT: OpaqueValueExpr [[TmpOpV:0x[0-9a-fA-F]+]] {{.*}} 'int' lvalue
|
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV]] {{.*}} 'int' lvalue
|
|
// CHECK: BinaryOperator {{.*}} 'int' lvalue '='
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV]] {{.*}} 'int' lvalue
|
|
// CHECK: ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
|
|
// CHECK-NEXT: OpaqueValueExpr [[TmpOpV]] {{.*}} 'int' lvalue
|
|
|
|
|
|
// CHECK-LABEL: FunctionDecl {{.*}} used buzz 'double (int, double)' implicit_instantiation
|
|
// CHECK: CallExpr {{.*}} 'void'
|
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(inout int)' <FunctionToPointerDecay>
|
|
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (inout int)' lvalue Function {{.*}} 'fn' 'void (inout int)'
|
|
// CHECK-NEXT: HLSLOutArgExpr {{.*}} 'int' lvalue inout
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV:0x[0-9a-fA-F]+]] {{.*}} 'int' lvalue
|
|
// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'X' 'int'
|
|
// CHECK-NEXT: OpaqueValueExpr [[TmpOpV:0x[0-9a-fA-F]+]] {{.*}} 'int' lvalue
|
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV]] {{.*}} 'int' lvalue
|
|
// CHECK: BinaryOperator {{.*}} 'int' lvalue '='
|
|
// CHECK-NEXT: OpaqueValueExpr [[LVOpV]] {{.*}} 'int' lvalue
|
|
// CHECK: ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
|
|
// CHECK-NEXT: OpaqueValueExpr [[TmpOpV]] {{.*}} 'int' lvalue
|
|
|
|
template <typename T>
|
|
T buzz(int X, T Y) {
|
|
fn(X);
|
|
return X + Y;
|
|
}
|
|
|
|
export void caller() {
|
|
int X = 2;
|
|
float Y = 3.3;
|
|
double Z = 2.2;
|
|
|
|
X = wrapper(X);
|
|
Y = wrapper(Y);
|
|
Z = wrapper(Z);
|
|
|
|
fizz(X);
|
|
fizz(Y);
|
|
fizz(Z);
|
|
|
|
X = buzz(X, X);
|
|
Y = buzz(X, Y);
|
|
Z = buzz(X, Z);
|
|
}
|