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 := LoadI(str1).Imm(Constants::OBJECT_CLASS_OFFSET).ref 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 := LoadI(str1).Imm(Constants::OBJECT_CLASS_OFFSET).ref 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 := LoadI(str1).Imm(Constants::OBJECT_CLASS_OFFSET).ref 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 IfImm(Compare(length2, 0).EQ.b).Imm(0).Unlikely.NE.b { 325 Return(length1).i32 326 } 327 328 IfImm(Compare(length1, 0).EQ.b).Imm(0).Unlikely.NE.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 IfImm(Compare(length, 4).LE.b).Imm(0).NE.b { 353 d1 := LoadI(buf1).Imm(0).u32 354 d2 := LoadI(buf2).Imm(0).u32 355 If(d1, d2).EQ.b { 356 Return(Sub(length1, length2).i32).i32 357 } 358 Return(calculate_chars_difference(d1, d2, utf).i32).i32 359 } 360 361 # considering the data buffer to be allocated in size 362 # that is a multiple of 16 and being zeroed initially 363 # we can safely operate on 8-bytes chunks 364 i1 := 0 365Label(:Loop) 366 i := Phi(i1, i2).u64 367 data1 := Load(buf1, i).u64 368 data2 := Load(buf2, i).u64 369 i2 := AddI(i).Imm(8).u64 370 If(data1, data2).NE.b { 371 Goto(:NotEqual) 372 } 373 If(i2, length).LT.b { 374 Goto(:Loop) 375 } 376 377 Return(Sub(length1, length2).i32).i32 378 379Label(:NotEqual) 380 Return(calculate_chars_difference(data1, data2, utf).i32).i32 381} 382 383include_plugin 'ets_string' 384