@@ -678,12 +678,19 @@ property_set {
678
678
### DPC++ Compiler: front-end
679
679
680
680
DPC++ FE is responsible for several things related to specialization constants:
681
- 1 . Handling of ` kernel_handler ` SYCL kernel function argument.
682
- 2 . Communicating to DPC++ RT which kernel argument should be used for passing
683
- buffer with specialization constants values when they are emulated.
684
- 3 . Communicating to DPC++ RT mapping between ` specialization_id ` s and
685
- corresponding symbolic IDs through integration footer.
686
- 4 . It provides ` __builtin_unique_ID ` implementation.
681
+
682
+ While transforming SYCL kernel function into an OpenCL kernel, DPC++ FE should
683
+ - Handle ` kernel_handler ` argument: it is not captured by lambda and therefore
684
+ should be separately handled in DPC++ FE
685
+ - Communicate to DPC++ RT which kernel argument should be used for passing
686
+ a buffer with specialization constant values when they are emulated.
687
+
688
+ DPC++ FE provides implementation of ` __builtin_unique_ID ` built-in function and
689
+ it also populates special integration footer with the content required by DPC++
690
+ RT for access to right device image properties describing specialization
691
+ constants.
692
+
693
+ #### SYCL Kernel function transformations
687
694
688
695
` kernel_handler ` is defined by SYCL 2020 specification as interface for
689
696
retrieving specialization constant values in SYCL kernel functions, but it
@@ -743,21 +750,46 @@ integration header mechanism, i.e. it is added as new entry to
743
750
` kernel_signatures ` structure there with parameter kind set to a new
744
751
enumeration value ` kernel_param_kind_t::kind_specialization_constants_buffer ` .
745
752
746
- Those were descriptions of tasks (1) and (2) of DPC++ FE. Task (3) is to help
747
- DPC++ RT to connect user-provided ` specialization_id ` variable with
748
- corresponding symbolic ID of a specialization constant when
749
- ` handler::set_specialization_constant ` is invoked.
753
+ #### ` __builtin_unique_ID `
750
754
751
- As noted above, we can't use regular integration header here, because in general
752
- case, ` specialization_id ` variables can't be forward-declared. Therefore, we are
753
- using integration footer approach, which for the following code snippet:
755
+ This built-in is used to generate unique identifiers for specialization
756
+ constants, which are used in communication between the compiler and the runtime.
757
+
758
+ ` __builtin_unique_ID ` is defined as follows: it accepts a variable and returns
759
+ a C-string (` const char * ` ), which:
760
+ - if the input variable has external linkage, the string must be the same in all
761
+ translation units that pass this same variable to the built-in.
762
+ - if the input variable has internal linkage, the string must be unique across
763
+ all translation units.
764
+ - return string must be the same if the built-in was called twice for the same
765
+ variable within a single translation unit (regardless of its linkage type).
766
+
767
+ #### Integration footer generation
768
+
769
+ Note: we could have used ` __builtin_unique_ID ` directly in DPC++ Headers, but
770
+ this would break compilation of those with a third-party C++ 17-compatible
771
+ compiler, which is unaware of this built-in function. Therefore, the compiler
772
+ generates a header file, which includes _ the result_ of calling
773
+ ` __builtin_unique_ID ` function and it is included into the user's program. By
774
+ doing so we can still use this non-standard built-in function and preserve
775
+ support for third-party host compilers.
776
+
777
+ However, as noted above, we can't use regular integration header here, because
778
+ in general case, ` specialization_id ` variables can't be forward-declared.
779
+ Therefore, we are using _ integration footer_ approach, i.e. we generate a header
780
+ file which must be included at the end of a translation unit.
781
+
782
+ For the following code snippet:
754
783
```
755
784
struct A {
756
785
float a, b;
757
786
};
758
787
759
788
constexpr specialization_id<int> id_int;
760
- constexpr specialization_id<A> id_A;
789
+ struct Wraper {
790
+ public:
791
+ static constexpr specialization_id<A> id_A;
792
+ };
761
793
constexpr inline specialization_id<double> id_double;
762
794
constexpr inline specialization_id<float> id_float;
763
795
// ...
@@ -767,20 +799,19 @@ constexpr inline specialization_id<float> id_float;
767
799
// ...
768
800
[=](kernel_handler h) {
769
801
h.get_specialization_constant<id_int>();
770
- h.get_specialization_constant<id_A>();
802
+ h.get_specialization_constant<Wrapper:: id_A>();
771
803
}
772
804
}
773
805
```
774
806
775
- Will look like:
807
+ The integration footer will look like:
776
808
777
809
```
778
810
namespace detail {
779
- // generic declaration
780
- template<auto &SpecName>
781
- inline const char *get_spec_constant_symbolic_ID();
811
+ // Note: we do not declare `get_spec_constant_symbolic_ID` here and assume that
812
+ // it is declared in some other header which was already included.
782
813
783
- // specializations for each specialization constant:
814
+ // specializations for each specialization constant (for each `specialization_id`) :
784
815
// we can refer to all those specialization_id variables, because integration
785
816
// footer was _appended_ to the user-provided translation unit
786
817
template<>
@@ -789,8 +820,8 @@ inline const char *get_spec_constant_symbolic_ID<id_int>() {
789
820
}
790
821
791
822
template<>
792
- inline const char *get_spec_constant_symbolic_ID<id_A>() {
793
- return "result of __builtin_unique_ID(id_A) encoded here";
823
+ inline const char *get_spec_constant_symbolic_ID<Wrapper:: id_A>() {
824
+ return "result of __builtin_unique_ID(Wrapper:: id_A) encoded here";
794
825
}
795
826
796
827
template<>
@@ -814,19 +845,97 @@ definition of `specialization_id` object regardless of its uses within SYCL
814
845
kernel functions: those IDs are used by DPC++ RT as well even for those spec
815
846
constants, which are never accessed on device.
816
847
817
- NOTE: By direct using ` __builtin_unique_ID ` in DPC++ Headers we could avoid
818
- generating integration footer at all, but since the host part of the program can
819
- be compiled with a third-party C++ 17-compatible compiler, which is unaware of
820
- the clang-specific built-ins, it can result in build errors.
821
848
822
- ` __builtin_unique_ID ` is defined as follows: it accepts a variable and returns
823
- a C-string (` const char * ` ), which:
824
- - if the variable has external linkage, the string must be consistent in all
825
- translation units that reference this same variable.
826
- - if the variable has internal linkage, the string must be unique across all
827
- translation units.
828
- - return string must be the same if the built-in was called twice for the same
829
- variable within a single translation unit.
849
+ ##### Ambiguous references to specialization_id
850
+
851
+ There are valid C++ code examples, where references to ` specialization_id `
852
+ variables could be ambiguous if they just referenced from a global namespace
853
+ like shown above. For example:
854
+
855
+ ```
856
+ constexpr sycl::specialization_id<int> same_name{1};
857
+
858
+ /* application code that references "::same_name" */
859
+
860
+ namespace {
861
+ constexpr sycl::specialization_id<int> same_name{2}:
862
+ /* application code that referenes ::(unnamed)::same_name */
863
+ namespace {
864
+ constexpr sycl::specialization_id<int> same_name{3}:
865
+ /* application code that referenes ::(unnamed)::(unnamed)::same_name */
866
+ }
867
+ }
868
+
869
+ /* application code that references "::same_name" */
870
+ ```
871
+
872
+ In that case we can't use ` same_name ` for specializing
873
+ ` get_spec_constant_symbolic_ID ` , because it would be ambiguous reference.
874
+ However, we can do the following trick:
875
+
876
+ ```
877
+ // Content of integration footer for the example above
878
+
879
+ // For unambiguous references we can generate regular specialization
880
+ template<>
881
+ inline const char *get_spec_constant_symbolic_ID<::same_name>() {
882
+ return "result of __builtin_unique_ID(::same_name) encoded here";
883
+ }
884
+
885
+ // For ambiguous references we generate 'shim' functions, which allows us to
886
+ // get an address of a variable within a (possible nested) anonymous namespace
887
+ // without spelling it.
888
+ namespace {
889
+ namespace __sycl_detail {
890
+ // This helper is need to get addresses of variables defined within
891
+ // anonymous namespace.
892
+ // It is generated for each specialization_id within an anonymous namespace
893
+ // if there is the same specialization_id defined in global namespace
894
+ static constexpr decltype(spec_name) __spec_id_shim_0() {
895
+ // address of ::(unnamed)::same_name;
896
+ return spec_name;
897
+ }
898
+ }
899
+ }
900
+ namespace sycl {
901
+ namespace detail {
902
+ // By using 'shim' function were are able to unambiguously refer to a
903
+ // variable within an anonymous namespace
904
+ template<>
905
+ inline const char *get_spec_constant_symbolic_ID<::__sycl_detail::__spec_id_shim_0()>() {
906
+ return "unique id for ::(unnamed)::same_name";
907
+ }
908
+ }
909
+ }
910
+ namespace {
911
+ namespace {
912
+ namespace __sycl_detail {
913
+ static constexpr decltype(same_name) &spec_id_shim_1() {
914
+ // address of ::(unnamed)::(unnamed)::same_name;
915
+ return same_name;
916
+ }
917
+ }
918
+ }
919
+
920
+ namespace __sycl_detail {
921
+ // Sometimes we need a 'shim', which points to another 'shim' in order to
922
+ // "extract" a variable from an anonymous namespace unambiguosly
923
+ static constexpr decltype(__sycl_detail::__spec_id_shim_1()) &__spec_id_shim_2() {
924
+ // still address of ::(unnamed)::(unnamed)::same_name;
925
+ return __sycl_detail::__spec_id_shim_1();
926
+ }
927
+ }
928
+ }
929
+ namespace sycl {
930
+ namespace detail {
931
+ template<>
932
+ inline const char *get_spec_constant_symbolic_ID<::__sycl_detail::__spec_id_shim_2()>() {
933
+ return "unique id for ::(unnamed)::(unnamed)::same_name";
934
+ }
935
+ }
936
+ }
937
+
938
+ ```
830
939
831
940
### DPC++ runtime
832
941
0 commit comments