• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env ruby
2
3# Copyright (c) 2021-2024 Huawei Device Co., Ltd.
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16include_relative 'common.irt'
17include_relative 'string_helpers.irt'
18
19GenerateStringEquals(lang='', dynamic=false, compressed=true)
20GenerateStringEquals(lang='', dynamic=false, compressed=false)
21GenerateCreateStringFromStringTlab(string_compression_enabled=true)
22GenerateCreateStringFromStringTlab(string_compression_enabled=false)
23GenerateCreateStringFromCharArrayTlab(string_compression_enabled=true)
24GenerateCreateStringFromCharArrayTlab(string_compression_enabled=false)
25GenerateCreateStringFromZeroBasedCharArrayTlab(string_compression_enabled=true)
26GenerateCreateStringFromZeroBasedCharArrayTlab(string_compression_enabled=false)
27GenerateSubstringFromStringTlab(string_compression_enabled=true)
28GenerateSubstringFromStringTlab(string_compression_enabled=false)
29GenerateStringGetCharsTlab(string_compression_enabled=true)
30GenerateStringGetCharsTlab(string_compression_enabled=false)
31GenerateStringHashCode(string_compression_enabled=true)
32GenerateStringHashCode(string_compression_enabled=false)
33
34available_regs = $panda_mask
35function(:StringConcat2Tlab,
36          params: {str1: 'ref', str2: 'ref'},
37          regmap: $full_regmap,
38          regalloc_set: available_regs,
39          mode: [:FastPath]) {
40
41    if Options.arch == :arm32
42      Intrinsic(:UNREACHABLE).void.Terminator
43      next
44    end
45
46    klass := load_class(str1)
47    length1 := LoadI(str1).Imm(Constants::STRING_LENGTH_OFFSET).u32
48    length2 := LoadI(str2).Imm(Constants::STRING_LENGTH_OFFSET).u32
49
50    # any of the strings is uncompressed (resulted string is uncompressed)
51    has_uncompressed := AndI(Or(length1, length2).u32).Imm(1).u32
52
53    count1 := ShrI(length1).Imm(1).u32
54    count2 := ShrI(length2).Imm(1).u32
55
56    size := Add(count1, count2).u32
57    data_size := Shl(size, has_uncompressed).u32
58    length := Or(ShlI(size).Imm(1).u32, has_uncompressed).u32
59    new_str := allocate_string_tlab(klass, data_size)
60
61    StoreI(new_str, length).Imm(Constants::STRING_LENGTH_OFFSET).u32
62    StoreI(new_str, Cast(0).u32).Imm(Constants::STRING_HASHCODE_OFFSET).u32
63
64    src_str_data1 := AddI(str1).Imm(Constants::STRING_DATA_OFFSET).ptr
65    dst_str_data1 := AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr
66
67    # has no uncompressed then everyting is compressed, data is stored as bytes
68    If(has_uncompressed, 0).EQ.b {
69      copy_u8_chars(src_str_data1, dst_str_data1, count1)
70
71      offset := count1
72
73      src_str_data2 := AddI(str2).Imm(Constants::STRING_DATA_OFFSET).ptr
74      dst_str_data2 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset).ptr
75      copy_u8_chars(src_str_data2, dst_str_data2, count2)
76      Goto(:EndCopy)
77    }
78
79    If(AndI(length1).Imm(1).u32, 0).EQ.b {
80      expand_u8_to_u16_chars(src_str_data1, dst_str_data1, count1)
81    } Else {
82      copy_u16_chars(src_str_data1, dst_str_data1, count1)
83    }
84
85    offset := ShlI(count1).Imm(1).u32
86    src_str_data2 := AddI(str2).Imm(Constants::STRING_DATA_OFFSET).ptr
87    dst_str_data2 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset).ptr
88
89    If(AndI(length2).Imm(1).u32, 0).EQ.b {
90      expand_u8_to_u16_chars(src_str_data2, dst_str_data2, count2)
91    } Else {
92      copy_u16_chars(src_str_data2, dst_str_data2, count2)
93    }
94
95    Label(:EndCopy)
96
97    # String is supposed to be a constant object, so all its data should be visible by all threads
98    Intrinsic(:DATA_MEMORY_BARRIER_FULL).void
99    Return(new_str).ptr
100
101Label(:SlowPathEntrypoint)
102    ep_offset = get_entrypoint_offset("STRING_CONCAT2_SLOW_PATH")
103    Intrinsic(:SLOW_PATH_ENTRY, str1, str2).AddImm(ep_offset).MethodAsImm("StringConcat2UsualBridge").Terminator.ptr
104    Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG
105}
106
107available_regs = $panda_mask
108function(:StringConcat3Tlab,
109         params: {str1: 'ref', str2: 'ref', str3: 'ref'},
110         regmap: $full_regmap,
111         regalloc_set: available_regs,
112         mode: [:FastPath]) {
113
114    if Options.arch == :arm32
115      Intrinsic(:UNREACHABLE).void.Terminator
116      next
117    end
118
119    klass := load_class(str1)
120    length1 := LoadI(str1).Imm(Constants::STRING_LENGTH_OFFSET).u32
121    length2 := LoadI(str2).Imm(Constants::STRING_LENGTH_OFFSET).u32
122    length3 := LoadI(str3).Imm(Constants::STRING_LENGTH_OFFSET).u32
123
124    has_uncompressed := AndI(Or(length3, Or(length1, length2).u32).u32).Imm(1).u32
125
126    count1 := ShrI(length1).Imm(1).u32
127    count2 := ShrI(length2).Imm(1).u32
128    count3 := ShrI(length3).Imm(1).u32
129
130    size := Add(count1, Add(count2, count3).u32).u32
131    data_size := Shl(size, has_uncompressed).u32
132    length := Or(ShlI(size).Imm(1).u32, has_uncompressed).u32
133    new_str := allocate_string_tlab(klass, data_size)
134
135    StoreI(new_str, length).Imm(Constants::STRING_LENGTH_OFFSET).u32
136    StoreI(new_str, Cast(0).u32).Imm(Constants::STRING_HASHCODE_OFFSET).u32
137
138    src_str_data1 := AddI(str1).Imm(Constants::STRING_DATA_OFFSET).ptr
139    dst_str_data1 := AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr
140
141    offset1 := ShlI(count1).Imm(1).u32
142    offset2 := Add(offset1, ShlI(count2).Imm(1).u32).u32
143
144    # everything is compressed
145    If(has_uncompressed, 0).EQ.b {
146      copy_u8_chars(src_str_data1, dst_str_data1, count1)
147
148      offset1_ := ShrI(offset1).Imm(1).u32
149
150      src_str_data2 := AddI(str2).Imm(Constants::STRING_DATA_OFFSET).ptr
151      dst_str_data2 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset1_).ptr
152      copy_u8_chars(src_str_data2, dst_str_data2, count2)
153
154      offset2_ := ShrI(offset2).Imm(1).u32
155
156      src_str_data3 := AddI(str3).Imm(Constants::STRING_DATA_OFFSET).ptr
157      dst_str_data3 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset2_).ptr
158      copy_u8_chars(src_str_data3, dst_str_data3, count3)
159      Goto(:EndCopy)
160    }
161
162    If(AndI(length1).Imm(1).u32, 0).EQ.b {
163      expand_u8_to_u16_chars(src_str_data1, dst_str_data1, count1)
164    } Else {
165      copy_u16_chars(src_str_data1, dst_str_data1, count1)
166    }
167
168    src_str_data2 := AddI(str2).Imm(Constants::STRING_DATA_OFFSET).ptr
169    dst_str_data2 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset1).ptr
170
171    If(AndI(length2).Imm(1).u32, 0).EQ.b {
172      expand_u8_to_u16_chars(src_str_data2, dst_str_data2, count2)
173    } Else {
174      copy_u16_chars(src_str_data2, dst_str_data2, count2)
175    }
176
177    src_str_data3 := AddI(str3).Imm(Constants::STRING_DATA_OFFSET).ptr
178    dst_str_data3 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset2).ptr
179
180    If(AndI(length3).Imm(1).u32, 0).EQ.b {
181      expand_u8_to_u16_chars(src_str_data3, dst_str_data3, count3)
182    } Else {
183      copy_u16_chars(src_str_data3, dst_str_data3, count3)
184    }
185
186Label(:EndCopy)
187    # String is supposed to be a constant object, so all its data should be visible by all threads
188    Intrinsic(:DATA_MEMORY_BARRIER_FULL).void
189    Return(new_str).ptr
190
191Label(:SlowPathEntrypoint)
192    ep_offset = get_entrypoint_offset("STRING_CONCAT3_SLOW_PATH")
193    Intrinsic(:SLOW_PATH_ENTRY, str1, str2, str3).AddImm(ep_offset).MethodAsImm("StringConcat3OddSavedBridge").Terminator.ptr
194    Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG
195}
196
197available_regs = $panda_mask
198function(:StringConcat4Tlab,
199         params: {str1: 'ref', str2: 'ref', str3: 'ref', str4: 'ref'},
200         regmap: $full_regmap,
201         regalloc_set: available_regs,
202         mode: [:FastPath]) {
203    if Options.arch == :arm32
204      Intrinsic(:UNREACHABLE).void.Terminator
205      next
206    end
207
208    klass := load_class(str1)
209    length1 := LoadI(str1).Imm(Constants::STRING_LENGTH_OFFSET).u32
210    length2 := LoadI(str2).Imm(Constants::STRING_LENGTH_OFFSET).u32
211    length3 := LoadI(str3).Imm(Constants::STRING_LENGTH_OFFSET).u32
212    length4 := LoadI(str4).Imm(Constants::STRING_LENGTH_OFFSET).u32
213
214    # any of the strings is uncompressed (resulted string is uncompressed)
215    has_uncompressed := Or(length3, Or(length1, length2).u32).u32
216    has_uncompressed := AndI(Or(length4, has_uncompressed).u32).Imm(1).u32
217
218    count1 := ShrI(length1).Imm(1).u32
219    count2 := ShrI(length2).Imm(1).u32
220    count3 := ShrI(length3).Imm(1).u32
221    count4 := ShrI(length4).Imm(1).u32
222
223    size := Add(count1, Add(count2, Add(count3, count4).u32).u32).u32
224    data_size := Shl(size, has_uncompressed).u32
225    length := Or(ShlI(size).Imm(1).u32, has_uncompressed).u32
226    new_str := allocate_string_tlab(klass, data_size)
227
228    StoreI(new_str, length).Imm(Constants::STRING_LENGTH_OFFSET).u32
229    StoreI(new_str, Cast(0).u32).Imm(Constants::STRING_HASHCODE_OFFSET).u32
230
231    src_str_data1 := AddI(str1).Imm(Constants::STRING_DATA_OFFSET).ptr
232    dst_str_data1 := AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr
233
234    offset1 := ShlI(count1).Imm(1).u32
235    offset2 := Add(offset1, ShlI(count2).Imm(1).u32).u32
236    offset3 := Add(offset2, ShlI(count3).Imm(1).u32).u32
237
238    # has no uncompressed then everything is compressed, data is stored as bytes
239    If(has_uncompressed, 0).EQ.b {
240      copy_u8_chars(src_str_data1, dst_str_data1, count1)
241
242      offset1_ := ShrI(offset1).Imm(1).u32
243
244      src_str_data2 := AddI(str2).Imm(Constants::STRING_DATA_OFFSET).ptr
245      dst_str_data2 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset1_).ptr
246      copy_u8_chars(src_str_data2, dst_str_data2, count2)
247
248      offset2_ := ShrI(offset2).Imm(1).u32
249
250      src_str_data3 := AddI(str3).Imm(Constants::STRING_DATA_OFFSET).ptr
251      dst_str_data3 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset2_).ptr
252      copy_u8_chars(src_str_data3, dst_str_data3, count3)
253
254      offset3_ := ShrI(offset3).Imm(1).u32
255
256      src_str_data4 := AddI(str4).Imm(Constants::STRING_DATA_OFFSET).ptr
257      dst_str_data4 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset3_).ptr
258      copy_u8_chars(src_str_data4, dst_str_data4, count4)
259      Goto(:EndCopy)
260    }
261
262    If(AndI(length1).Imm(1).u32, 0).EQ.b {
263      expand_u8_to_u16_chars(src_str_data1, dst_str_data1, count1)
264    } Else {
265      copy_u16_chars(src_str_data1, dst_str_data1, count1)
266    }
267
268    src_str_data2 := AddI(str2).Imm(Constants::STRING_DATA_OFFSET).ptr
269    dst_str_data2 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset1).ptr
270
271    If(AndI(length2).Imm(1).u32, 0).EQ.b {
272      expand_u8_to_u16_chars(src_str_data2, dst_str_data2, count2)
273    } Else {
274      copy_u16_chars(src_str_data2, dst_str_data2, count2)
275    }
276
277    src_str_data3 := AddI(str3).Imm(Constants::STRING_DATA_OFFSET).ptr
278    dst_str_data3 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset2).ptr
279
280    If(AndI(length3).Imm(1).u32, 0).EQ.b {
281      expand_u8_to_u16_chars(src_str_data3, dst_str_data3, count3)
282    } Else {
283      copy_u16_chars(src_str_data3, dst_str_data3, count3)
284    }
285
286    src_str_data4 := AddI(str4).Imm(Constants::STRING_DATA_OFFSET).ptr
287    dst_str_data4 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset3).ptr
288
289    If(AndI(length4).Imm(1).u32, 0).EQ.b {
290      expand_u8_to_u16_chars(src_str_data4, dst_str_data4, count4)
291    } Else {
292      copy_u16_chars(src_str_data4, dst_str_data4, count4)
293    }
294Label(:EndCopy)
295    # String is supposed to be a constant object, so all its data should be visible by all threads
296    Intrinsic(:DATA_MEMORY_BARRIER_FULL).void
297    Return(new_str).ptr
298
299Label(:SlowPathEntrypoint)
300    ep_offset = get_entrypoint_offset("STRING_CONCAT4_SLOW_PATH")
301    Intrinsic(:SLOW_PATH_ENTRY, str1, str2, str3, str4).AddImm(ep_offset).MethodAsImm("StringConcat4UsualBridge").Terminator.ptr
302    Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG
303}
304
305function(:StringCompareTo,
306         params: {str1: 'ref', str2: 'ref'},
307         regmap: $full_regmap,
308         regalloc_set: $panda_mask,
309         mode: [:FastPath]) {
310  # Arm32 is not supported
311  if Options.arch == :arm32
312    Intrinsic(:UNREACHABLE).void.Terminator
313    next
314  end
315
316  If(str1, str2).EQ.Unlikely.b {
317    Return(0).i32
318  }
319
320  length1 := LoadI(str1).Imm(Constants::STRING_LENGTH_OFFSET).u32
321  length2 := LoadI(str2).Imm(Constants::STRING_LENGTH_OFFSET).u32
322
323  # cover the cases of length2 being zero and both lengths being zero
324  If(length2, 0).EQ.Unlikely.b {
325    Return(length1).i32
326  }
327
328  If(length1, 0).EQ.Unlikely.b {
329    Return(-1).i32
330  }
331
332  buf1 := AddI(str1).Imm(Constants::STRING_DATA_OFFSET).ptr
333  buf2 := AddI(str2).Imm(Constants::STRING_DATA_OFFSET).ptr
334
335  # get the least length in chars
336  length := ShrI(Cast(Min(length1, length2).i32).u64).Imm(1).u64
337
338  utf_1 := AndI(length1).Imm(1).u32
339  utf_2 := AndI(length2).Imm(1).u32
340
341  # in the unlikely case the strings are in different
342  # encodings the comparison gets more comlicated as
343  # we have to expand the latin string to u16 first
344  If(utf_1, utf_2).NE.Unlikely.b {
345    Return(compare_mixed_strings(buf1, buf2, length, utf_1).i32).i32
346  }
347  utf := Cast(utf_1).u64
348
349  # make length be in actual bytes now
350  length := Shl(length, utf).u64
351
352  # considering the data buffer to be allocated to
353  # 8-bytes alignment, we can safely read 8-bytes chunks
354  last_idx := SubI(length).Imm(8).i64
355  i1 := 0
356Label(:Loop)
357  i := Phi(i1, i2).i64
358  If(i, last_idx).GE.Unlikely.b {
359    # can safely read 8 bytes behind - length and hashcode
360    junk_bits := ShlI(Sub(i, last_idx).u64).Imm(3).u64
361    last_data1 := Shr(Load(buf1, last_idx).u64, junk_bits).u64
362    last_data2 := Shr(Load(buf2, last_idx).u64, junk_bits).u64
363    If(last_data1, last_data2).NE.b {
364      Goto(:NotEqual)
365    }
366    Return(Sub(length1, length2).i32).i32
367  }
368
369  data1 := Load(buf1, i).u64
370  data2 := Load(buf2, i).u64
371  If(data1, data2).NE.b {
372    Goto(:NotEqual)
373  }
374  i2 := AddI(i).Imm(8).i64
375  Goto(:Loop)
376
377Label(:NotEqual)
378  d1 := Phi(last_data1, data1).u64
379  d2 := Phi(last_data2, data2).u64
380  Return(calculate_chars_difference(d1, d2, utf).i32).i32
381}
382
383include_plugin 'ets_string'
384