4
4
//! [`Identifier`]s cannot be created directly, only able to be converted from other
5
5
//! compatible IDs.
6
6
use self :: { kinds:: IdKind , masks:: IdentifierMask } ;
7
+ use std:: hash:: Hash ;
7
8
8
9
pub mod error;
9
10
pub ( crate ) mod kinds;
@@ -13,16 +14,24 @@ pub(crate) mod masks;
13
14
/// Has the same size as a `u64` integer, but the layout is split between a 32-bit low
14
15
/// segment, a 31-bit high segment, and the significant bit reserved as type flags to denote
15
16
/// entity kinds.
16
- #[ derive( Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
17
+ #[ derive( Debug , Clone , Copy ) ]
18
+ // Alignment repr necessary to allow LLVM to better output
19
+ // optimised codegen for `to_bits`, `PartialEq` and `Ord`.
20
+ #[ repr( C , align( 8 ) ) ]
17
21
pub struct Identifier {
22
+ // Do not reorder the fields here. The ordering is explicitly used by repr(C)
23
+ // to make this struct equivalent to a u64.
24
+ #[ cfg( target_endian = "little" ) ]
18
25
low : u32 ,
19
26
high : u32 ,
27
+ #[ cfg( target_endian = "big" ) ]
28
+ low : u32 ,
20
29
}
21
30
22
31
impl Identifier {
23
32
/// Construct a new [`Identifier`]. The `high` parameter is masked with the
24
33
/// `kind` so to pack the high value and bit flags into the same field.
25
- #[ inline]
34
+ #[ inline( always ) ]
26
35
#[ must_use]
27
36
pub ( crate ) const fn new ( low : u32 , high : u32 , kind : IdKind ) -> Self {
28
37
// the high bits are masked to cut off the most significant bit
@@ -38,34 +47,98 @@ impl Identifier {
38
47
}
39
48
40
49
/// Returns the value of the low segment of the [`Identifier`].
41
- #[ inline]
42
- pub ( crate ) const fn low ( self ) -> u32 {
50
+ #[ inline( always ) ]
51
+ pub const fn low ( self ) -> u32 {
43
52
self . low
44
53
}
45
54
46
55
/// Returns the value of the high segment of the [`Identifier`]. This
47
56
/// does not apply any masking.
48
- #[ inline]
49
- pub ( crate ) const fn high ( self ) -> u32 {
57
+ #[ inline( always ) ]
58
+ pub const fn high ( self ) -> u32 {
50
59
self . high
51
60
}
52
61
62
+ /// Returns the masked value of the high segment of the [`Identifier`].
63
+ /// Does not include the flag bits.
64
+ #[ inline( always) ]
65
+ pub const fn masked_high ( self ) -> u32 {
66
+ IdentifierMask :: extract_value_from_high ( self . high ( ) )
67
+ }
68
+
69
+ /// Returns the kind of [`Identifier`] from the high segment.
70
+ #[ inline( always) ]
71
+ pub const fn kind ( self ) -> IdKind {
72
+ IdentifierMask :: extract_kind_from_high ( self . high ( ) )
73
+ }
74
+
53
75
/// Convert the [`Identifier`] into a `u64`.
54
- #[ inline]
55
- pub ( crate ) const fn to_bits ( self ) -> u64 {
76
+ #[ inline( always ) ]
77
+ pub const fn to_bits ( self ) -> u64 {
56
78
IdentifierMask :: pack_into_u64 ( self . low , self . high )
57
79
}
58
80
59
81
/// Convert a `u64` into an [`Identifier`].
60
- #[ inline]
61
- pub ( crate ) const fn from_bits ( value : u64 ) -> Self {
82
+ #[ inline( always ) ]
83
+ pub const fn from_bits ( value : u64 ) -> Self {
62
84
Self {
63
85
low : IdentifierMask :: get_low ( value) ,
64
86
high : IdentifierMask :: get_high ( value) ,
65
87
}
66
88
}
67
89
}
68
90
91
+ // By not short-circuiting in comparisons, we get better codegen.
92
+ // See <https://github.com/rust-lang/rust/issues/117800>
93
+ impl PartialEq for Identifier {
94
+ #[ inline]
95
+ fn eq ( & self , other : & Self ) -> bool {
96
+ // By using `to_bits`, the codegen can be optimised out even
97
+ // further potentially. Relies on the correct alignment/field
98
+ // order of `Entity`.
99
+ self . to_bits ( ) == other. to_bits ( )
100
+ }
101
+ }
102
+
103
+ impl Eq for Identifier { }
104
+
105
+ // The derive macro codegen output is not optimal and can't be optimised as well
106
+ // by the compiler. This impl resolves the issue of non-optimal codegen by relying
107
+ // on comparing against the bit representation of `Entity` instead of comparing
108
+ // the fields. The result is then LLVM is able to optimise the codegen for Entity
109
+ // far beyond what the derive macro can.
110
+ // See <https://github.com/rust-lang/rust/issues/106107>
111
+ impl PartialOrd for Identifier {
112
+ #[ inline]
113
+ fn partial_cmp ( & self , other : & Self ) -> Option < std:: cmp:: Ordering > {
114
+ // Make use of our `Ord` impl to ensure optimal codegen output
115
+ Some ( self . cmp ( other) )
116
+ }
117
+ }
118
+
119
+ // The derive macro codegen output is not optimal and can't be optimised as well
120
+ // by the compiler. This impl resolves the issue of non-optimal codegen by relying
121
+ // on comparing against the bit representation of `Entity` instead of comparing
122
+ // the fields. The result is then LLVM is able to optimise the codegen for Entity
123
+ // far beyond what the derive macro can.
124
+ // See <https://github.com/rust-lang/rust/issues/106107>
125
+ impl Ord for Identifier {
126
+ #[ inline]
127
+ fn cmp ( & self , other : & Self ) -> std:: cmp:: Ordering {
128
+ // This will result in better codegen for ordering comparisons, plus
129
+ // avoids pitfalls with regards to macro codegen relying on property
130
+ // position when we want to compare against the bit representation.
131
+ self . to_bits ( ) . cmp ( & other. to_bits ( ) )
132
+ }
133
+ }
134
+
135
+ impl Hash for Identifier {
136
+ #[ inline]
137
+ fn hash < H : std:: hash:: Hasher > ( & self , state : & mut H ) {
138
+ self . to_bits ( ) . hash ( state) ;
139
+ }
140
+ }
141
+
69
142
#[ cfg( test) ]
70
143
mod tests {
71
144
use super :: * ;
@@ -100,4 +173,36 @@ mod tests {
100
173
IdKind :: Entity
101
174
) ;
102
175
}
176
+
177
+ #[ rustfmt:: skip]
178
+ #[ test]
179
+ fn id_comparison ( ) {
180
+ // This is intentionally testing `lt` and `ge` as separate functions.
181
+ #![ allow( clippy:: nonminimal_bool) ]
182
+
183
+ assert ! ( Identifier :: new( 123 , 456 , IdKind :: Entity ) == Identifier :: new( 123 , 456 , IdKind :: Entity ) ) ;
184
+ assert ! ( Identifier :: new( 123 , 456 , IdKind :: Placeholder ) == Identifier :: new( 123 , 456 , IdKind :: Placeholder ) ) ;
185
+ assert ! ( Identifier :: new( 123 , 789 , IdKind :: Entity ) != Identifier :: new( 123 , 456 , IdKind :: Entity ) ) ;
186
+ assert ! ( Identifier :: new( 123 , 456 , IdKind :: Entity ) != Identifier :: new( 123 , 789 , IdKind :: Entity ) ) ;
187
+ assert ! ( Identifier :: new( 123 , 456 , IdKind :: Entity ) != Identifier :: new( 456 , 123 , IdKind :: Entity ) ) ;
188
+ assert ! ( Identifier :: new( 123 , 456 , IdKind :: Entity ) != Identifier :: new( 123 , 456 , IdKind :: Placeholder ) ) ;
189
+
190
+ // ordering is by flag then high then by low
191
+
192
+ assert ! ( Identifier :: new( 123 , 456 , IdKind :: Entity ) >= Identifier :: new( 123 , 456 , IdKind :: Entity ) ) ;
193
+ assert ! ( Identifier :: new( 123 , 456 , IdKind :: Entity ) <= Identifier :: new( 123 , 456 , IdKind :: Entity ) ) ;
194
+ assert ! ( !( Identifier :: new( 123 , 456 , IdKind :: Entity ) < Identifier :: new( 123 , 456 , IdKind :: Entity ) ) ) ;
195
+ assert ! ( !( Identifier :: new( 123 , 456 , IdKind :: Entity ) > Identifier :: new( 123 , 456 , IdKind :: Entity ) ) ) ;
196
+
197
+ assert ! ( Identifier :: new( 9 , 1 , IdKind :: Entity ) < Identifier :: new( 1 , 9 , IdKind :: Entity ) ) ;
198
+ assert ! ( Identifier :: new( 1 , 9 , IdKind :: Entity ) > Identifier :: new( 9 , 1 , IdKind :: Entity ) ) ;
199
+
200
+ assert ! ( Identifier :: new( 9 , 1 , IdKind :: Entity ) < Identifier :: new( 9 , 1 , IdKind :: Placeholder ) ) ;
201
+ assert ! ( Identifier :: new( 1 , 9 , IdKind :: Placeholder ) > Identifier :: new( 1 , 9 , IdKind :: Entity ) ) ;
202
+
203
+ assert ! ( Identifier :: new( 1 , 1 , IdKind :: Entity ) < Identifier :: new( 2 , 1 , IdKind :: Entity ) ) ;
204
+ assert ! ( Identifier :: new( 1 , 1 , IdKind :: Entity ) <= Identifier :: new( 2 , 1 , IdKind :: Entity ) ) ;
205
+ assert ! ( Identifier :: new( 2 , 2 , IdKind :: Entity ) > Identifier :: new( 1 , 2 , IdKind :: Entity ) ) ;
206
+ assert ! ( Identifier :: new( 2 , 2 , IdKind :: Entity ) >= Identifier :: new( 1 , 2 , IdKind :: Entity ) ) ;
207
+ }
103
208
}
0 commit comments