Skip to content

Commit 4476369

Browse files
committed
Address review comments
* When representing properties using `@llvm.ptr.annotation`, represent all properties in the fifth argument. This allows each property and its value to be represented as its own metadata, rather than combining them all into a single string. * Add an initial optional parameter to each C++ attribute that allows filtering of the properties. * The header now passes `nullptr` instead of `""` to represent the "value" of a property that has no value. * Clarify that each property in the C++ attribute parameter list has exactly one value, so the number of parameters is even (assuming the initial optional parameter is not specified).
1 parent ba0d71f commit 4476369

File tree

1 file changed

+169
-47
lines changed

1 file changed

+169
-47
lines changed

sycl/doc/CompileTimeProperties.md

Lines changed: 169 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,13 @@ class
8888
} // namespace sycl::ext::oneapi
8989
```
9090

91-
The initial entries in the C++ attribute's parameter list are the names of the
92-
properties, and these are followed by the values of the properties. To
93-
illustrate using the same example as before, the result of the parameter pack
94-
expansion would look like this:
91+
The `[[__sycl_detail__::add_ir_global_variable_attributes()]]` attribute has an
92+
even number of parameters. The first half of the parameters are the names of
93+
the properties, and the second half of the parameters are the values for those
94+
properties. Each property has exactly one value, so the property at parameter
95+
position 0 corresponds to the value at position _N / 2_, etc. To illustrate
96+
using the same example as before, the result of the parameter pack expansion
97+
would look like this:
9598

9699
```
97100
namespace sycl::ext::oneapi {
@@ -101,7 +104,7 @@ template </* ... */> class
101104
[[__sycl_detail__::add_ir_global_variable_attributes(
102105
"sycl-device-image-scope", // Name of first property
103106
"sycl-host-access", // Name of second property
104-
"", // First property has no parameter
107+
nullptr, // First property has no parameter
105108
"read" // Value of second property
106109
)]]
107110
#endif
@@ -128,10 +131,10 @@ type as described above, it also adds one IR attribute to the global variable
128131
for each property using
129132
[`GlobalVariable::addAttribute(StringRef, StringRef)`][4]. If the property
130133
value is not already a string, it converts it to a string as described in
131-
[Property representation in C++ attributes][5].
134+
[IR representation as IR attributes][5].
132135

133136
[4]: <https://llvm.org/doxygen/classllvm_1_1GlobalVariable.html#a6cee3c634aa5de8c51e6eaa4e41898bc>
134-
[5]: <#property-representation-in-C-attributes>
137+
[5]: <#ir-representation-as-ir-attributes>
135138

136139
Note that the front-end does not need to understand any of the properties in
137140
order to do this translation.
@@ -219,7 +222,7 @@ template </* ... */> class
219222
[[__sycl_detail__::add_ir_kernel_parameter_attributes(
220223
"sycl-no-alias", // Name of first property
221224
"sycl-foo", // Name of second property
222-
"", // First property has no parameter
225+
nullptr, // First property has no parameter
223226
32 // Value of second property
224227
)]]
225228
#endif
@@ -423,10 +426,10 @@ class annotated_ptr<T, property_list<Props...>> {
423426
T *ptr
424427
#ifdef __SYCL_DEVICE_ONLY__
425428
[[__sycl_detail__::add_ir_member_annotation(
426-
"foo", // Name of first property
427-
"bar", // Name of second property
428-
"", // First property has no parameter
429-
32 // Value of second property
429+
"sycl-foo", // Name of first property
430+
"sycl-bar", // Name of second property
431+
nullptr, // First property has no parameter
432+
32 // Value of second property
430433
)]]
431434
#endif
432435
;
@@ -440,48 +443,63 @@ class annotated_ptr<T, property_list<Props...>> {
440443
When the device compiler generates code to reference the decorated member
441444
variable, it emits a call to the LLVM intrinsic function
442445
[`@llvm.ptr.annotation`][10] that annotates the pointer to that member
443-
variables, similar to the way the existing clang `__attribute__((annotate()))`
446+
variables, similar to the way the existing `[[clang::annotate()]]` attribute
444447
works. Illustrating this with some simplified LLVM IR that matches the example
445448
code above:
446449

447450
[10]: <https://llvm.org/docs/LangRef.html#llvm-ptr-annotation-intrinsic>
448451

449452
```
450-
@.str = private unnamed_addr constant [27 x i8] c"sycl-properties:foo,bar=32\00",
451-
section "llvm.metadata"
453+
@.str = private unnamed_addr constant [16 x i8] c"sycl-properties\00",
454+
section "llvm.metadata"
452455
@.str.1 = private unnamed_addr constant [9 x i8] c"file.cpp\00",
453-
section "llvm.metadata"
456+
section "llvm.metadata"
457+
@.str.2 = private unnamed_addr constant [9 x i8] c"sycl-foo\00", align 1
458+
@.str.3 = private unnamed_addr constant [9 x i8] c"sycl-bar\00", align 1
459+
460+
@.args = private unnamed_addr constant { [9 x i8]*, i8*, [9 x i8]*, i32 }
461+
{
462+
[9 x i8]* @.str.2, ; Name of first property "sycl-foo"
463+
i8* null, ; Null indicates this property has no value
464+
[9 x i8]* @.str.3, ; Name of second property "sycl-bar"
465+
i32 32 ; Value of second property
466+
},
467+
section "llvm.metadata"
454468
455469
define void @foo(i32* %ptr) {
456470
%aptr = alloca %class.annotated_ptr
457471
%ptr = getelementptr inbounds %class.annotated_ptr, %class.annotated_ptr* %aptr,
458472
i32 0, i32 0
459473
%1 = bitcast i32** %ptr to i8*
460-
%2 = call i8* @llvm.ptr.annotation.p0i8(i8* %1,
461-
i8* getelementptr inbounds ([27 x i8], [27 x i8]* @.str, i32 0, i32 0),
462-
i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.1, i32 0, i32 0),
463-
i32 3, i8* null)
474+
475+
%2 = call i8* @llvm.ptr.annotation.p0i8(i8* nonnull %0,
476+
i8* getelementptr inbounds ([16 x i8], [16 x i8]* @.str, i64 0, i64 0),
477+
i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.1, i64 0, i64 0),
478+
i32 3,
479+
i8* bitcast ({ [9 x i8]*, i8*, [9 x i8]*, i32 }* @.args to i8*))
480+
464481
%3 = bitcast i8* %2 to i32**
465482
store i32* %ptr, i32** %3
466483
ret void
467484
}
468485
```
469486

470487
The front-end encodes the properties from the C++ attribute
471-
`[[__sycl_detail__::add_ir_member_annotation()]]` into the annotation string
472-
(`@.str` in the example above) using the following algorithm:
473-
474-
* The property value is converted to a string as specified in
475-
[Property representation in C++ attributes][5].
476-
* Construct a property definition string for each property:
477-
- If the property value is the empty string, the property definition is just
478-
the name of the property.
479-
- Otherwise, the property definition string is formed by concatenating the
480-
property name with the equal sign (`=`) and the property value.
481-
* The annotation string is formed by concatenating all property definition
482-
strings, separated by a comma (`,`).
483-
* The annotation string is pre-pended with `"sycl-properties:"` and NULL
484-
terminated.
488+
`[[__sycl_detail__::add_ir_member_annotation()]]` into the
489+
`@llvm.ptr.annotation` call as follows:
490+
491+
* The first parameter to `@llvm.ptr.annotation` is the pointer to annotate (as
492+
with any call to this intrinsic).
493+
* The second parameter is the literal string `"sycl-properties"`.
494+
* The third parameter is the name of the source file (as with any call to this
495+
intrinsic).
496+
* The fourth parameter is the line number (as with any call to this intrinsic).
497+
* The fifth parameter is a metadata tuple with information about all of the
498+
properties. The first element of the tuple is a string literal with the name
499+
of the first property. The second element is the value of the first
500+
property. The third element is a string literal with the name of the second
501+
property, etc. Since each property has exactly one value, this tuple has an
502+
even number of elements.
485503

486504
**NOTE**: Calls to the `@llvm.ptr.annotation` intrinsic function are known to
487505
disable many clang optimizations. As a result, properties added to a
@@ -490,7 +508,7 @@ optimized. This puts more pressure on the SPIR-V consumer (e.g. JIT compiler)
490508
to perform these optimizations.
491509

492510

493-
## Property representation in C++ attributes
511+
## Property representation in C++ attributes and in IR
494512

495513
As noted above, there are several C++ attributes that convey property names and
496514
values to the front-end:
@@ -502,11 +520,12 @@ values to the front-end:
502520

503521
All of these attributes take a parameter list with the same format. There are
504522
always an even number of parameters, where the first half are the property
505-
names and the second half are the property values. The property name is always
506-
a string literal or a `constexpr char *` expression. By convention, property
507-
names that correspond to LLVM IR attributes normally start with the prefix
508-
`"sycl-"` in order to avoid collision with non-SYCL IR attributes, but this is
509-
not a strict requirement.
523+
names and the second half are the property values. (This assumes that the
524+
initial optional parameter is not passed. See below for a description of this
525+
optional parameter.) The property name is always a string literal or a
526+
`constexpr char *` expression. By convention, property names normally start
527+
with the prefix `"sycl-"` in order to avoid collision with non-SYCL IR
528+
attributes, but this is not a strict requirement.
510529

511530
The property value can be a literal or `constexpr` expression of the following
512531
types:
@@ -517,15 +536,29 @@ types:
517536
* A boolean type.
518537
* A character type.
519538
* An enumeration type.
539+
* `nullptr_t` (reserved for the case when a property has no value).
520540

521541
All properties require a value when represented in the C++ attribute. If the
522-
SYCL property has no value the header passes the empty string (`""`).
542+
SYCL property has no value the header passes `nullptr`.
543+
544+
### IR representation as IR attributes
545+
546+
Properties that are implemented using the following C++ attributes are
547+
represented in LLVM IR as IR attributes:
523548

524-
The front-end converts each value to a string before representing it in LLVM
525-
IR. Integer and floating point values are converted with the same format as
526-
`std::to_string()` would produce. Boolean values are converted to either
527-
`"true"` or `"false"`. Enumeration values are first converted to an integer
528-
and then converted to a string with the same format as `std::to_string()`.
549+
* `[[__sycl_detail__::add_ir_global_variable_attributes()]]`
550+
* `[[__sycl_detail__::add_ir_kernel_parameter_attributes()]]`
551+
* `[[__sycl_detail__::add_ir_function_attributes()]]`
552+
553+
When the front-end consumes these C++ attributes and produces IR, each property
554+
name becomes an IR attribute name and each property value becomes the
555+
attribute's value. Because the attribute values must be strings, the front-end
556+
converts each property value to a string. Integer and floating point values
557+
are converted with the same format as `std::to_string()` would produce.
558+
Boolean values are converted to either `"true"` or `"false"`. Enumeration
559+
values are first converted to an integer and then converted to a string with
560+
the same format as `std::to_string()`. The `nullptr` value is converted to an
561+
empty string (`""`).
529562

530563
**TODO**: Should we allow property values that are type names? If so, I
531564
suppose they would be converted to a string representation of the mangled name?
@@ -539,6 +572,94 @@ we do not allow non-fundamental types, how do we represent properties like
539572
allow `std::tuple`, where the type of each element is one of the fundamental
540573
types listed above.
541574

575+
### IR representation via `@llvm.ptr.annotation`
576+
577+
Properties that are implemented using
578+
`[[__sycl_detail__::add_ir_member_annotation()]]`, are represented in LLVM IR
579+
as the fifth metadata parameter to the `@llvm.ptr.annotation` intrinsic
580+
function. This parameter is a tuple of metadata values with the following
581+
sequence:
582+
583+
* Name of the first property
584+
* Value of the first property
585+
* Name of the second property
586+
* Value of the second property
587+
* Etc.
588+
589+
Since metadata types are not limited to strings, there is no need to convert
590+
the property values to strings.
591+
592+
593+
## Filtering properties
594+
595+
It is sometimes necessary to filter out certain properties so that only a
596+
subset of the properties in a list are represented in IR. There are two
597+
scenarios when this is useful.
598+
599+
In some cases, a property is used only in the header file itself, and there is
600+
no need to represent that property in LLVM IR. In order to avoid cluttering
601+
the IR with unneeded information, these properties can be "filtered out", so
602+
that the front-end does not generate an IR representation.
603+
604+
Another case is when a class wants to represent some properties one way in the
605+
IR while representing other properties in another way. For example, a future
606+
version of `accessor` might pass some properties to
607+
`[[__sycl_detail__::add_ir_kernel_parameter_attributes()]]` while passing other
608+
properties to `[[__sycl_detail__::add_ir_member_annotation()]]`. Again, the
609+
header wants some way to "filter" the properties, such that some properties are
610+
interpreted as "kernel parameter attributes" while other are interpreted as
611+
"member annotations".
612+
613+
To handle these cases, each of the following C++ attributes takes an optional
614+
first parameter that is a brace-enclosed list of property names:
615+
616+
* `[[__sycl_detail__::add_ir_global_variable_attributes()]]`
617+
* `[[__sycl_detail__::add_ir_kernel_parameter_attributes()]]`
618+
* `[[__sycl_detail__::add_ir_function_attributes()]]`
619+
* `[[__sycl_detail__::add_ir_member_annotation()]]`
620+
621+
The front-end treats this list as a "pass list", ignoring any property whose
622+
name is not in the list. To illustrate, consider the following example where
623+
`accessor` treats some properties as "kernel parameter attributes" and others
624+
as "member annotations":
625+
626+
```
627+
template <typename dataT,
628+
int dimensions,
629+
access::mode accessmode,
630+
access::target accessTarget,
631+
access::placeholder isPlaceholder,
632+
typename ...Props>
633+
class
634+
#ifdef __SYCL_DEVICE_ONLY__
635+
[[__sycl_detail__::add_ir_kernel_parameter_attributes(
636+
637+
// The properties in this list are "kernel parameter attributes".
638+
{"sycl-no-alias", "sycl-foo"},
639+
640+
Props::meta_name..., Props::meta_value...
641+
)]]
642+
#endif
643+
accessor<dataT,
644+
dimensions,
645+
accessmode,
646+
accessTarget,
647+
isPlaceholder,
648+
property_list<Props...>> {
649+
T *ptr
650+
#ifdef __SYCL_DEVICE_ONLY__
651+
[[__sycl_detail__::add_ir_member_annotation(
652+
653+
// The properties in this list are "member annotations".
654+
{"sycl-bar"},
655+
656+
Props::meta_name..., Props::meta_value...
657+
)]]
658+
#endif
659+
;
660+
}
661+
```
662+
542663

543664
## Representing properties in SPIR-V
544665

@@ -638,7 +759,8 @@ of the global variable's decorations. To illustrate:
638759

639760
As we noted earlier, a property on a structure member variable is represented
640761
in LLVM IR as a call to the intrinsic function `@llvm.ptr.annotation`, where
641-
the annotation string starts with the prefix `"sycl-properties:"`. In order to
762+
the annotation string is `"sycl-properties"` and the properties are represented
763+
as metadata in the fifth parameter to `@llvm.ptr.annotation`. In order to
642764
understand how these SYCL properties are translated into SPIR-V, it's useful to
643765
review how a normal (i.e. non-SYCL) call to `@llvm.ptr.annotation` is
644766
translated.

0 commit comments

Comments
 (0)