[clang] [HLSL][Docs] Add documentation for HLSL functions (PR #75397)

Tex Riddell via cfe-commits cfe-commits at lists.llvm.org
Wed Dec 13 16:58:20 PST 2023


================
@@ -0,0 +1,316 @@
+===================
+HLSL Function Calls
+===================
+
+.. contents::
+   :local:
+
+Introduction
+============
+
+This document describes the design and implementation of HLSL's function call
+semantics in Clang. This includes details related to argument conversion and
+parameter lifetimes.
+
+This document does not seek to serve as official documentation for HLSL's
+call semantics, but does provide an overview to assist a reader. The
+authoritative documentation for HLSL's language semantics is the `draft language
+specification <https://microsoft.github.io/hlsl-specs/specs/hlsl.pdf>`_.
+
+Argument Semantics
+==================
+
+In HLSL, all function arguments are passed by value in and out of functions.
+HLSL has 3 keywords which denote the parameter semantics (``in``, ``out`` and
+``inout``). In a function declaration a parameter may be annotated any of the
+following ways:
+
+#. <no parameter annotation> - denotes input
+#. ``in`` - denotes input
+#. ``out`` - denotes output
+#. ``in out`` - denotes input and output
+#. ``out in`` - denotes input and output
+#. ``inout`` - denotes input and output
+
+Parameters that are exclusively input behave like C/C++ parameters that are
+passed by value.
+
+For parameters that are output (or input and output), a temporary value is
+created in the caller. The temporary value is then passed by-address. For
+output-only parameters, the temporary is uninitialized when passed (if the
+parameter is not explicitly initialized inside the function an undefined value
+is stored back to the argument expression). For input and output parameters, the
+temporary is initialized from  the lvalue argument expression through implicit
+or explicit casting from the lvalue argument type to the parameter type.
+
+On return of the function, the values of any parameter temporaries are written
+back to the argument expression through an inverted conversion sequence (if an
+``out`` parameter was not initialized in the function, the uninitialized value
+may be written back).
+
+Parameters of constant-sized array type, are also passed with value semantics.
+This requires input parameters of arrays to construct temporaries and the
+temporaries go through array-to-pointer decay when initializing parameters.
+
+Implementations are allowed to avoid unnecessary temporaries, and HLSL's strict
+no-alias rules can enable some trivial optimizations.
+
+Array Temporaries
+-----------------
+
+Given the following example:
+
+.. code-block:: c++
+
+  void fn(float a[4]) {
+    a[0] = a[1] + a[2] + a[3];
+  }
+
+  float4 main() : SV_Target {
+    float arr[4] = {1, 1, 1, 1};
+    fn(arr);
+    return float4(arr[0], arr[1], arr[2], arr[3]);
+  }
+
+In C or C++, the array parameter decays to a pointer, so after the call to
+``fn``, the value of ``arr[0]`` is ``3``. In HLSL, the array is passed by value,
+so modifications inside ``fn`` do not propagate out.
+
+.. note::
+
+  DXC supports unsized arrays passed directly as decayed pointers, which is an
+  unfortunate behavior divergence.
+
+Out Parameter Temporaries
+-------------------------
+
+.. code-block:: c++
+
+  void Init(inout int X, inout int Y) {
+    Y = 2;
+    X = 1;
+  }
+
+  void main() {
+    int V;
+    Init(V, V); // MSVC ABI V == 2, Itanium V == 1
+  }
+
+In the above example the ``Init`` function's behavior depends on the C++ ABI
+implementation. In the MSVC C++ ABI (used for the HLSL DXIL target), call
+arguments are emitted right-to-left and destroyed left-to-right. This means that
+the parameter initialization and destruction occurs in the order: {``Y``,
+``X``, ``~X``, ``~Y``}. This causes the write-back of the value of ``Y`` to occur
+last, so the resulting value of ``V`` is ``2``. In the Itanium C++ ABI, the
+parameter ordering is reversed, so the initialization and destruction occurs in
+the order: {``X``, ``Y``, ``~Y``, ``X``}. This causes the write-back of the
+value ``X`` to occur last, resulting in the value of ``V`` being set to ``1``.
+
+.. code-block:: c++
+
+  void Trunc(inout int3 V) { }
+
+
+  void main() {
+    float3 F = {1.5, 2.6, 3.3};
+    Trunc(F); // F == {1.0, 2.0, 3.0}
+  }
+
+In the above example, the argument expression ``F`` undergoes element-wise
+conversion from a float vector to an integer vector to create a temporary
+``int3``. On expiration the temporary undergoes elementwise conversion back to
+the floating point vector type ``float3``. This results in an implicit
+truncation of the vector even if the value is unused in the function.
+
+
+.. code-block:: c++
+
+  void UB(out int X) {}
+
+  void main() {
+    int X = 7;
+    UB(X); // X is undefined!
+  }
+
+In this example an initialized value is passed to an ``out`` parameter.
+Parameters marked ``out`` are not initialized by the argument expression or
+implicitly by the function. They must be explicitly initialized. In this case
+the argument is not initialized in the function so the temporary is still
+uninitialized when it is copied back to the argument expression. This is
+undefined behavior in HLSL, and may be illegal in generated programs.
----------------
tex3d wrote:

> and may be illegal in generated programs

Can we clarify what this means?  In FXC, it would (usually try to) generate an error if you didn't initialize the output parameter, whether or not the caller used that output, but this might depend on static control flow, and whether or not you called the function at all from an active entry point.

For this statement, are we saying that it may simply result in code that tries to read the undefined value, which in some cases will be caught as illegal by the DXIL validator?

I think we need to refine our rules for this, and what the compiler is expected to do.  That doesn't have to be done here, but maybe we should make note that this area needs improvement.

https://github.com/llvm/llvm-project/pull/75397


More information about the cfe-commits mailing list