Files
clang-p2996/clang/test/SemaHLSL/ArrayTemporary.hlsl
Chris B 9434c08347 [HLSL] Implement array temporary support (#79382)
HLSL constant sized array function parameters do not decay to pointers.
Instead constant sized array types are preserved as unique types for
overload resolution, template instantiation and name mangling.

This implements the change by adding a new `ArrayParameterType` which
represents a non-decaying `ConstantArrayType`. The new type behaves the
same as `ConstantArrayType` except that it does not decay to a pointer.

Values of `ConstantArrayType` in HLSL decay during overload resolution
via a new `HLSLArrayRValue` cast to `ArrayParameterType`.

`ArrayParamterType` values are passed indirectly by-value to functions
in IR generation resulting in callee generated memcpy instructions.

The behavior of HLSL function calls is documented in the [draft language
specification](https://microsoft.github.io/hlsl-specs/specs/hlsl.pdf)
under the Expr.Post.Call heading.

Additionally the design of this implementation approach is documented in
[Clang's
documentation](https://clang.llvm.org/docs/HLSL/FunctionCalls.html)

Resolves #70123
2024-04-01 12:10:10 -05:00

96 lines
3.8 KiB
HLSL

// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -ast-dump %s | FileCheck %s
void fn(float x[2]) { }
// CHECK: CallExpr {{.*}} 'void'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float[2])' <FunctionToPointerDecay>
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (float[2])' lvalue Function {{.*}} 'fn' 'void (float[2])'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float[2]' <HLSLArrayRValue>
void call() {
float Arr[2] = {0, 0};
fn(Arr);
}
struct Obj {
float V;
int X;
};
void fn2(Obj O[4]) { }
// CHECK: CallExpr {{.*}} 'void'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(Obj[4])' <FunctionToPointerDecay>
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (Obj[4])' lvalue Function {{.*}} 'fn2' 'void (Obj[4])'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'Obj[4]' <HLSLArrayRValue>
void call2() {
Obj Arr[4] = {};
fn2(Arr);
}
void fn3(float x[2][2]) { }
// CHECK: CallExpr {{.*}} 'void'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float[2][2])' <FunctionToPointerDecay>
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (float[2][2])' lvalue Function {{.*}} 'fn3' 'void (float[2][2])'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float[2][2]' <HLSLArrayRValue>
void call3() {
float Arr[2][2] = {{0, 0}, {1,1}};
fn3(Arr);
}
// This template function should be instantiated 3 times for the different array
// types and lengths.
// CHECK: FunctionTemplateDecl {{.*}} template_fn
// CHECK-NEXT: TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T
// CHECK-NEXT: FunctionDecl {{.*}} template_fn 'void (T)'
// CHECK-NEXT: ParmVarDecl {{.*}} Val 'T'
// CHECK: FunctionDecl {{.*}} used template_fn 'void (float[2])' implicit_instantiation
// CHECK-NEXT: TemplateArgument type 'float[2]'
// CHECK-NEXT: ArrayParameterType {{.*}} 'float[2]' 2
// CHECK-NEXT: BuiltinType {{.*}} 'float'
// CHECK-NEXT: ParmVarDecl {{.*}} Val 'float[2]'
// CHECK: FunctionDecl {{.*}} used template_fn 'void (float[4])' implicit_instantiation
// CHECK-NEXT: TemplateArgument type 'float[4]'
// CHECK-NEXT: ArrayParameterType {{.*}} 'float[4]' 4
// CHECK-NEXT: BuiltinType {{.*}} 'float'
// CHECK-NEXT: ParmVarDecl {{.*}} Val 'float[4]'
// CHECK: FunctionDecl {{.*}} used template_fn 'void (int[3])' implicit_instantiation
// CHECK-NEXT: TemplateArgument type 'int[3]'
// CHECK-NEXT: ArrayParameterType {{.*}} 'int[3]' 3
// CHECK-NEXT: BuiltinType {{.*}} 'int'
// CHECK-NEXT: ParmVarDecl {{.*}} Val 'int[3]'
template<typename T>
void template_fn(T Val) {}
// CHECK: FunctionDecl {{.*}} call 'void (float[2], float[4], int[3])'
// CHECK: CallExpr {{.*}} 'void'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float[2])' <FunctionToPointerDecay>
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (float[2])' lvalue Function {{.*}} 'template_fn' 'void (float[2])' (FunctionTemplate {{.*}} 'template_fn')
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float[2]' <LValueToRValue>
// CHECK-NEXT: DeclRefExpr {{.*}} 'float[2]' lvalue ParmVar {{.*}} 'FA2' 'float[2]'
// CHECK-NEXT: CallExpr {{.*}} 'void'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float[4])' <FunctionToPointerDecay>
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (float[4])' lvalue Function {{.*}} 'template_fn' 'void (float[4])' (FunctionTemplate {{.*}} 'template_fn')
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float[4]' <LValueToRValue>
// CHECK-NEXT: DeclRefExpr {{.*}} 'float[4]' lvalue ParmVar {{.*}} 'FA4' 'float[4]'
// CHECK-NEXT: CallExpr {{.*}} 'void'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(int[3])' <FunctionToPointerDecay>
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (int[3])' lvalue Function {{.*}} 'template_fn' 'void (int[3])' (FunctionTemplate {{.*}} 'template_fn')
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int[3]' <LValueToRValue>
// CHECK-NEXT: DeclRefExpr {{.*}} 'int[3]' lvalue ParmVar {{.*}} 'IA3' 'int[3]'
void call(float FA2[2], float FA4[4], int IA3[3]) {
template_fn(FA2);
template_fn(FA4);
template_fn(IA3);
}