1# plugin ets_string 2# Copyright (c) 2024 Huawei Device Co., Ltd. 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15include_relative 'string_helpers.irt' 16 17# It is assumed that _begin_index and _end_index are safe and does not check/normalize them. 18# The range is [_begin_index, _end_index). 19# Note, a caller of this macro must provide a corresponding 'SlowPathEntrypoint' 20# for the case when 'allocate_string_tlab' fails (see StringTrim as an example) 21# Now TLAB implementation initializes memory with zero, so the hashcode field 22# is not initialized with zero explicitly. 23macro(:fast_substring) do |_str, _str_len, _begin_index, _end_index, _not_compressed| 24 _char_count := Sub(_end_index, _begin_index).u32 25 If(_char_count, Cast(_str_len).u32).EQ.Unlikely.b { 26 # Return the string itself 27 _same_str := Cast(_str).SrcType(Constants::COMPILER_REFERENCE).ptr 28 Goto(:_Fast_Substring_Result_No_Barrier) 29 } 30 _klass := LoadI(_str).Imm(Constants::OBJECT_CLASS_OFFSET).ref 31 If(_char_count, 0).EQ.Unlikely.b { 32 # Allocate and return an empty string 33 _empty_str := allocate_string_tlab(_klass, 0) 34 Goto(:_Fast_Substring_Result) 35 } 36 # Allocate a new normal string 37 _offset := Shl(_begin_index, _not_compressed).u32 38 _src_str_data := Add(Cast(_str).SrcType(Constants::COMPILER_REFERENCE).ptr, Cast(Constants::STRING_DATA_OFFSET).u64).ptr 39 _src_str_data := Add(_src_str_data, Cast(_offset).u64).ptr 40 If(_not_compressed, 1).EQ.Unlikely.b { 41 _compressable := is_array_of_compressable_chars(_src_str_data, Cast(_char_count).u64) 42 If(_compressable, 1).EQ.Likely.b { 43 _data_size1 := Cast(_char_count).word 44 Goto(:_L1) 45 } 46 _data_size2 := Cast(ShlI(_char_count).Imm(1).u32).word 47Label(:_L1) 48 _data_size := Phi(_data_size1, _data_size2).word 49 _new_str1 := allocate_string_tlab(_klass, _data_size) 50 _new_str_data := Add(_new_str1, Cast(Constants::STRING_DATA_OFFSET).u64).ptr 51 If(_compressable, 1).EQ.Likely.b { 52 compress_u16_to_u8_chars(_src_str_data, _new_str_data, Cast(_char_count).u64) 53 StoreI(_new_str1, ShlI(_char_count).Imm(1).u32).Imm(Constants::STRING_LENGTH_OFFSET).u32 54 Goto(:_Fast_Substring_Result) 55 } 56 copy_u16_chars(_src_str_data, _new_str_data, Cast(_char_count).u64) 57 StoreI(_new_str1, OrI(ShlI(_char_count).Imm(1).u32).Imm(1).u32).Imm(Constants::STRING_LENGTH_OFFSET).u32 58 Goto(:_Fast_Substring_Result) 59 } 60 # Source string is already compressed 61 _new_str2 := allocate_string_tlab(_klass, Cast(_char_count).word) 62 _new_str_data2 := Add(_new_str2, Cast(Constants::STRING_DATA_OFFSET).u64).ptr 63 copy_u8_chars(_src_str_data, _new_str_data2, Cast(_char_count).u64) 64 StoreI(_new_str2, ShlI(_char_count).Imm(1).u32).Imm(Constants::STRING_LENGTH_OFFSET).u32 65Label(:_Fast_Substring_Result) 66 # String is supposed to be a constant object, so all its data should be visible by all threads 67 Intrinsic(:DATA_MEMORY_BARRIER_FULL).void 68 _substring := Phi(_empty_str, _new_str1, _new_str1, _new_str2).ptr 69Label(:_Fast_Substring_Result_No_Barrier) 70 _result := Phi(_same_str, _substring).ptr 71end 72 73 74# 75# Test if u16 char is a white space 76# 77scoped_macro(:is_white_space_u16) do |ch| 78 IfImm(Compare(ch, 0x0020).EQ.b).Imm(0).NE.b { 79 Goto(:LabelWhiteSpaceChar) 80 } 81 # 0x000E..0x009F -- common non-whitespace chars 82 IfImm(Compare(ch, 0x000E).AE.b).Imm(0).NE.b { 83 IfImm(Compare(ch, 0x00A0).B.b).Imm(0).NE.b { 84 Goto(:LabelNotWhiteSpaceChar) 85 } 86 } 87 # 0x0009 -- horizontal tab 88 # 0x000A -- line feed or new line 89 # 0x000B -- vertical tab 90 # 0x000C -- formfeed 91 # 0x000D -- carriage return 92 IfImm(Compare(ch, 0x0009).B.b).Imm(0).NE.b { 93 Goto(:LabelNotWhiteSpaceChar) 94 } 95 IfImm(Compare(ch, 0x000D).BE.b).Imm(0).NE.b { 96 Goto(:LabelWhiteSpaceChar) 97 } 98 # 0x00A0 -- non-breaking space 99 IfImm(Compare(ch, 0x00A0).EQ.b).Imm(0).NE.b { 100 Goto(:LabelWhiteSpaceChar) 101 } 102 # 0x1680 -- Ogham space mark 103 If(ch, 0x1680).EQ.Unlikely.b { 104 Goto(:LabelWhiteSpaceChar) 105 } 106 # 0x2000 -- en quad 107 # 0x2001 -- em quad 108 # 0x2002 -- en space 109 # 0x2003 -- em space 110 # 0x2004 -- three-per-em space 111 # 0x2005 -- four-per-em space 112 # 0x2006 -- six-per-em space 113 # 0x2007 -- figure space 114 # 0x2008 -- punctuation space 115 # 0x2009 -- thin space 116 # 0x200A -- hair space 117 If(ch, 0x2000).B.Unlikely.b { 118 Goto(:LabelNotWhiteSpaceChar) 119 } 120 If(ch, 0x200A).BE.Unlikely.b { 121 Goto(:LabelWhiteSpaceChar) 122 } 123 # 0x2028 -- line separator 124 If(ch, 0x2028).EQ.Unlikely.b { 125 Goto(:LabelWhiteSpaceChar) 126 } 127 # 0x2029 -- paragraph separator 128 If(ch, 0x2029).EQ.Unlikely.b { 129 Goto(:LabelWhiteSpaceChar) 130 } 131 # 0x202F -- narrow no-break space 132 If(ch, 0x202F).EQ.Unlikely.b { 133 Goto(:LabelWhiteSpaceChar) 134 } 135 # 0x205F -- medium mathematical space 136 If(ch, 0x205F).EQ.Unlikely.b { 137 Goto(:LabelWhiteSpaceChar) 138 } 139 # 0xFEFF -- byte order mark 140 If(ch, 0xFEFF).EQ.Unlikely.b { 141 Goto(:LabelWhiteSpaceChar) 142 } 143 # 0x3000 -- ideographic space 144 If(ch, 0x3000).EQ.Unlikely.b { 145 Goto(:LabelWhiteSpaceChar) 146 } 147Label(:LabelNotWhiteSpaceChar) 148 whiteSpace0 := 0 149 Goto(:LabelReturn) 150Label(:LabelWhiteSpaceChar) 151 whiteSpace1 := 1 152Label(:LabelReturn) 153 result := Phi(whiteSpace0, whiteSpace1).b 154end 155 156# 157# Test if u8 char is a white space 158# 159scoped_macro(:is_white_space_u8) do |ch| 160 IfImm(Compare(ch, 0x20).EQ.b).Imm(0).NE.b { 161 Goto(:LabelWhiteSpaceChar) 162 } 163 # 0x0E..0x9F -- common non-whitespace chars 164 IfImm(Compare(ch, 0x0E).AE.b).Imm(0).NE.b { 165 IfImm(Compare(ch, 0xA0).B.b).Imm(0).NE.b { 166 Goto(:LabelNotWhiteSpaceChar) 167 } 168 } 169 # 0x09 -- horizontal tab 170 # 0x0A -- line feed or new line 171 # 0x0B -- vertical tab 172 # 0x0C -- formfeed 173 # 0x0D -- carriage return 174 IfImm(Compare(ch, 0x09).B.b).Imm(0).NE.b { 175 Goto(:LabelNotWhiteSpaceChar) 176 } 177 IfImm(Compare(ch, 0x0D).BE.b).Imm(0).NE.b { 178 Goto(:LabelWhiteSpaceChar) 179 } 180 # 0xA0 -- non-breaking space 181 IfImm(Compare(ch, 0xA0).EQ.b).Imm(0).NE.b { 182 Goto(:LabelWhiteSpaceChar) 183 } 184Label(:LabelNotWhiteSpaceChar) 185 whiteSpace0 := 0 186 Goto(:LabelReturn) 187Label(:LabelWhiteSpaceChar) 188 whiteSpace1 := 1 189Label(:LabelReturn) 190 result := Phi(whiteSpace0, whiteSpace1).b 191end 192 193 194function(:CharIsWhiteSpace, 195 params: {ch: 'u16'}, 196 regmap: $full_regmap, 197 regalloc_set: $panda_mask, 198 mode: [:FastPath]) { 199 200 if Options.arch == :arm32 201 Intrinsic(:UNREACHABLE).Terminator.void 202 ReturnVoid().void 203 next 204 end 205 Return(is_white_space_u16(ch)).b 206} 207 208 209function(:StringEmpty, 210 params: {str: 'ref'}, 211 regmap: $full_regmap, 212 regalloc_set: $panda_mask, 213 mode: [:FastPath]) { 214 215 if Options.arch == :arm32 216 Intrinsic(:UNREACHABLE).Terminator.void 217 ReturnVoid().void 218 next 219 end 220 221 klass := LoadI(str).Imm(Constants::OBJECT_CLASS_OFFSET).ref 222 empty_str := allocate_string_tlab(klass, 0) 223 # String is supposed to be a constant object, so all its data should be visible by all threads 224 Intrinsic(:DATA_MEMORY_BARRIER_FULL).void 225 Return(empty_str).ptr 226Label(:SlowPathEntrypoint) 227 entrypoint = get_entrypoint_offset("CREATE_EMPTY_STRING_SLOW_PATH") 228 Intrinsic(:SLOW_PATH_ENTRY).AddImm(entrypoint).MethodAsImm("CreateEmptyString1ArgBridge").Terminator.ptr 229 Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG 230} 231 232 233if Options.arch == :arm64 234 trim_left_regs = $temps_mask + :callee0 + :callee1 235 trim_right_regs = $temps_mask + :callee0 + :callee1 236else 237 trim_left_regs = $temps_mask + :callee0 + :caller0 + :caller1 238 trim_right_regs = $temps_mask + :callee0 + :caller0 + :caller1 239end 240 241 242function(:StringTrimLeftBase, 243 params: {str: 'ref', unused1: 'i32', unused2: 'i32'}, 244 regmap: $full_regmap, 245 regalloc_set: $panda_mask, 246 mode: [:FastPath]) { 247 248 if Options.arch == :arm32 249 Intrinsic(:UNREACHABLE).Terminator.void 250 ReturnVoid().void 251 next 252 end 253 254 length_packed := LoadI(str).Imm(Constants::STRING_LENGTH_OFFSET).u32 255 str_data := Add(Cast(str).SrcType(Constants::COMPILER_REFERENCE).ptr, Cast(Constants::STRING_DATA_OFFSET).word).ptr 256 not_compressed := AndI(length_packed).Imm(1).i32 257 length := ShrI(length_packed).Imm(1).i32 258 start_index := Cast(1).i32 259 If(not_compressed, 0).EQ.Likely.b { 260 # String contains 8-bit chars 261Label(:Loop1) 262 i := Phi(start_index, i1).i32 263 ws1 := is_white_space_u8(Load(str_data, i).u8) 264 If(ws1, 0).NE.Likely.b { 265 i1 := AddI(i).Imm(1).i32 266 If(i1, length).LT.Likely.b { 267 Goto(:Loop1) 268 } 269 } 270 index1 := Phi(i, i1).i32 271 Goto(:TrimLeft) 272 } 273 # String contains 16-bit chars 274Label(:Loop2) 275 j := Phi(start_index, j1).i32 276 ws2 := is_white_space_u16(Load(str_data, ShlI(j).Imm(1).i32).u16) 277 If(ws2, 0).NE.Likely.b { 278 j1 := AddI(j).Imm(1).i32 279 If(j1, length).LT.Likely.b { 280 Goto(:Loop2) 281 } 282 } 283 index2 := Phi(j, j1).i32 284Label(:TrimLeft) 285 index := Phi(index1, index2).i32 286 trimmed := fast_substring(str, length, index, length, not_compressed) 287 Return(trimmed).ptr 288Label(:SlowPathEntrypoint) 289 entrypoint = get_entrypoint_offset("SUB_STRING_FROM_STRING_SLOW_PATH") 290 Intrinsic(:SLOW_PATH_ENTRY, str, index, length).AddImm(entrypoint).MethodAsImm("SubStringFromStringOddSavedBridge").Terminator.ptr 291 Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG 292} 293 294 295function(:StringTrimLeft, 296 params: {str: 'ref', unused1: 'i32', unused2: 'i32'}, 297 regmap: $full_regmap, 298 regalloc_set: $trim_left_regs, 299 mode: [:FastPath]) { 300 301 if Options.arch == :arm32 302 Intrinsic(:UNREACHABLE).Terminator.void 303 ReturnVoid().void 304 next 305 end 306 307 length_packed := LoadI(str).Imm(Constants::STRING_LENGTH_OFFSET).u32 308 If(length_packed, 1).LE.Unlikely.b { 309 Return(Cast(str).SrcType(Constants::COMPILER_REFERENCE).ptr).ptr 310 } 311 str_data := Add(Cast(str).SrcType(Constants::COMPILER_REFERENCE).ptr, Cast(Constants::STRING_DATA_OFFSET).word).ptr 312 not_compressed := AndI(length_packed).Imm(1).i32 313 If(not_compressed, 0).EQ.Likely.b { 314 ws1 := is_white_space_u8(Load(str_data, 0).u8) 315 Goto(:L1) 316 } 317 ws2 := is_white_space_u16(Load(str_data, 0).u16) 318Label(:L1) 319 ws := Phi(ws1, ws2).b 320 If(ws, 0).EQ.Likely.b { 321 Return(Cast(str).SrcType(Constants::COMPILER_REFERENCE).ptr).ptr 322 } 323 If(ShrI(length_packed).Imm(1).i32, 1).EQ.Unlikely.b { 324 LiveOut(str).DstReg(regmap[:arg0]).ref 325 entrypoint1 = get_entrypoint_offset("STRING_EMPTY") 326 Intrinsic(:TAIL_CALL).AddImm(entrypoint1).MethodAsImm("StringEmpty").Terminator.ptr 327 } 328 LiveOut(str).DstReg(regmap[:arg0]).ref 329 LiveOut(unused1).DstReg(regmap[:arg1]).i32 330 LiveOut(unused2).DstReg(regmap[:arg2]).i32 331 entrypoint2 = get_entrypoint_offset("STRING_TRIM_LEFT_BASE") 332 Intrinsic(:TAIL_CALL).AddImm(entrypoint2).MethodAsImm("StringTrimLeftBase").Terminator.ptr 333} 334 335 336function(:StringTrimRightBase, 337 params: {str: 'ref', unused1: 'i32', unused2: 'i32'}, 338 regmap: $full_regmap, 339 regalloc_set: $panda_mask, 340 mode: [:FastPath]) { 341 342 if Options.arch == :arm32 343 Intrinsic(:UNREACHABLE).Terminator.void 344 ReturnVoid().void 345 next 346 end 347 348 length_packed := LoadI(str).Imm(Constants::STRING_LENGTH_OFFSET).u32 349 length := ShrI(length_packed).Imm(1).i32 350 start_index := SubI(length).Imm(2).i32 351 str_data := Add(Cast(str).SrcType(Constants::COMPILER_REFERENCE).ptr, Cast(Constants::STRING_DATA_OFFSET).word).ptr 352 not_compressed := AndI(length_packed).Imm(1).i32 353 If(not_compressed, 0).EQ.Likely.b { 354 # String contains 8-bit chars 355Label(:Loop1) 356 i := Phi(start_index, i1).i32 357 ws1 := is_white_space_u8(Load(str_data, i).u8) 358 If(ws1, 0).NE.Likely.b { 359 i1 := SubI(i).Imm(1).i32 360 If(i1, 0).GE.Likely.b { 361 Goto(:Loop1) 362 } 363 } 364 index1 := Phi(i, i1).i32 365 Goto(:TrimRight) 366 } 367 # String contains 16-bit chars 368Label(:Loop2) 369 j := Phi(start_index, j1).i32 370 ws2 := is_white_space_u16(Load(str_data, ShlI(j).Imm(1).i32).u16) 371 If(ws2, 0).NE.Likely.b { 372 j1 := SubI(j).Imm(1).i32 373 If(j1, 0).GE.Likely.b { 374 Goto(:Loop2) 375 } 376 } 377 index2 := Phi(j, j1).i32 378Label(:TrimRight) 379 index := Phi(index1, index2).i32 380 index := AddI(index).Imm(1).i32 381 trimmed := fast_substring(str, length, 0, index, not_compressed) 382 Return(trimmed).ptr 383Label(:SlowPathEntrypoint) 384 entrypoint = get_entrypoint_offset("SUB_STRING_FROM_STRING_SLOW_PATH") 385 Intrinsic(:SLOW_PATH_ENTRY, str, Cast(0).i32, index).AddImm(entrypoint).MethodAsImm("SubStringFromStringOddSavedBridge").Terminator.ptr 386 Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG 387} 388 389 390function(:StringTrimRight, 391 params: {str: 'ref', unused1: 'i32', unused2: 'i32'}, 392 regmap: $full_regmap, 393 regalloc_set: $trim_right_regs, 394 mode: [:FastPath]) { 395 396 if Options.arch == :arm32 397 Intrinsic(:UNREACHABLE).Terminator.void 398 ReturnVoid().void 399 next 400 end 401 402 length_packed := LoadI(str).Imm(Constants::STRING_LENGTH_OFFSET).u32 403 If(length_packed, 1).LE.Unlikely.b { 404 Return(Cast(str).SrcType(Constants::COMPILER_REFERENCE).ptr).ptr 405 } 406 str_data := Add(Cast(str).SrcType(Constants::COMPILER_REFERENCE).ptr, Cast(Constants::STRING_DATA_OFFSET).word).ptr 407 not_compressed := AndI(length_packed).Imm(1).i32 408 length := ShrI(length_packed).Imm(1).i32 409 last_char_index := SubI(length).Imm(1).i32 410 If(not_compressed, 0).EQ.Likely.b { 411 ws1 := is_white_space_u8(Load(str_data, last_char_index).u8) 412 Goto(:L1) 413 } 414 ws2 := is_white_space_u16(Load(str_data, ShlI(last_char_index).Imm(1).i32).u16) 415Label(:L1) 416 ws := Phi(ws1, ws2).b 417 If(ws, 0).EQ.Likely.b { 418 Return(Cast(str).SrcType(Constants::COMPILER_REFERENCE).ptr).ptr 419 } 420 If(length, 1).EQ.Unlikely.b { 421 LiveOut(str).DstReg(regmap[:arg0]).ref 422 entrypoint1 = get_entrypoint_offset("STRING_EMPTY") 423 Intrinsic(:TAIL_CALL).AddImm(entrypoint1).MethodAsImm("StringEmpty").Terminator.ptr 424 } 425 LiveOut(str).DstReg(regmap[:arg0]).ref 426 LiveOut(unused1).DstReg(regmap[:arg1]).i32 427 LiveOut(unused2).DstReg(regmap[:arg2]).i32 428 entrypoint2 = get_entrypoint_offset("STRING_TRIM_RIGHT_BASE") 429 Intrinsic(:TAIL_CALL).AddImm(entrypoint2).MethodAsImm("StringTrimRightBase").Terminator.ptr 430} 431 432 433function(:StringTrimBase, 434 params: {str: 'ref', unused1: 'i32', unused2: 'i32'}, 435 regmap: $full_regmap, 436 regalloc_set: $panda_mask, 437 mode: [:FastPath]) { 438 439 if Options.arch == :arm32 440 Intrinsic(:UNREACHABLE).Terminator.void 441 ReturnVoid().void 442 next 443 end 444 445 length_packed := LoadI(str).Imm(Constants::STRING_LENGTH_OFFSET).u32 446 length := ShrI(length_packed).Imm(1).i32 447 left := 0 448 right := SubI(length).Imm(2).i32 449 str_data := Add(Cast(str).SrcType(Constants::COMPILER_REFERENCE).ptr, Cast(Constants::STRING_DATA_OFFSET).word).ptr 450 not_compressed := AndI(length_packed).Imm(1).i32 451 If(not_compressed, 0).EQ.Likely.b { 452 # String contains 8-bit chars 453Label(:Loop1) # while (utf::IsWhiteSpaceChar(str->At(right))) 454 right1 := Phi(right, right2).i32 455 If(is_white_space_u8(Load(str_data, right1).u8), 0).NE.Likely.b { 456 If(right1, 0).EQ.Unlikely.b { 457 Goto(:Trim) 458 } 459 right2 := SubI(right1).Imm(1).i32 460 Goto(:Loop1) 461 } 462Label(:Loop2) # while (left < right && utf::IsWhiteSpaceChar(str->At(left))) 463 left1 := Phi(left, left2).i32 464 If(left1, right1).LT.Unlikely.b { 465 If(is_white_space_u8(Load(str_data, left1).u8), 0).NE.Likely.b { 466 left2 := AddI(left1).Imm(1).i32 467 Goto(:Loop2) 468 } 469 } 470 right3 := AddI(right1).Imm(1).i32 471 Goto(:Trim) 472 } 473 # String contains 16-bit chars 474Label(:Loop3) # while (utf::IsWhiteSpaceChar(str->At(right))) 475 right11 := Phi(right, right22).i32 476 If(is_white_space_u16(Load(str_data, ShlI(right11).Imm(1).i32).u16), 0).NE.Likely.b { 477 If(right11, 0).EQ.Unlikely.b { 478 Goto(:Trim) 479 } 480 right22 := SubI(right11).Imm(1).i32 481 Goto(:Loop3) 482 } 483Label(:Loop4) # while (left < right && utf::IsWhiteSpaceChar(str->At(left))) 484 left11 := Phi(left, left22).i32 485 If(left11, right11).LT.Unlikely.b { 486 If(is_white_space_u16(Load(str_data, ShlI(left11).Imm(1).i32).u16), 0).NE.Likely.b { 487 left22 := AddI(left11).Imm(1).i32 488 Goto(:Loop4) 489 } 490 } 491 right33 := AddI(right11).Imm(1).i32 492Label(:Trim) 493 l := Phi(left, left1, left, left11).i32 494 r := Phi(right1, right3, right11, right33).i32 495 trimmed := fast_substring(str, length, l, r, not_compressed) 496 Return(trimmed).ptr 497Label(:SlowPathEntrypoint) 498 entrypoint = get_entrypoint_offset("SUB_STRING_FROM_STRING_SLOW_PATH") 499 Intrinsic(:SLOW_PATH_ENTRY, str, l, r).AddImm(entrypoint).MethodAsImm("SubStringFromStringOddSavedBridge").Terminator.ptr 500 Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG 501} 502 503 504function(:StringTrim, 505 params: {str: 'ref', unused1: 'i32', unused2: 'i32'}, 506 regmap: $full_regmap, 507 regalloc_set: $panda_mask, 508 mode: [:FastPath]) { 509 510 if Options.arch == :arm32 511 Intrinsic(:UNREACHABLE).Terminator.void 512 ReturnVoid().void 513 next 514 end 515 516 length_packed := LoadI(str).Imm(Constants::STRING_LENGTH_OFFSET).u32 517 # length == 0 518 If(length_packed, 1).LE.Unlikely.b { 519 Return(Cast(str).SrcType(Constants::COMPILER_REFERENCE).ptr).ptr 520 } 521 str_data := Add(Cast(str).SrcType(Constants::COMPILER_REFERENCE).ptr, Cast(Constants::STRING_DATA_OFFSET).word).ptr 522 not_compressed := AndI(length_packed).Imm(1).i32 523 length := ShrI(length_packed).Imm(1).i32 524 # length == 1 525 If(length, 1).EQ.b { 526 If(not_compressed, 0).EQ.Likely.b { 527 ws1 := is_white_space_u8(Load(str_data, 0).u8) 528 Goto(:L1) 529 } 530 ws2 := is_white_space_u16(Load(str_data, 0).u16) 531Label(:L1) 532 ws3 := Phi(ws1, ws2).b 533 If(ws3, 0).EQ.Likely.b { 534 Return(Cast(str).SrcType(Constants::COMPILER_REFERENCE).ptr).ptr 535 } 536 LiveOut(str).DstReg(regmap[:arg0]).ref 537 entrypoint1 = get_entrypoint_offset("STRING_EMPTY") 538 Intrinsic(:TAIL_CALL).AddImm(entrypoint1).MethodAsImm("StringEmpty").Terminator.ptr 539 } 540 # length > 1 541 last_char_index := SubI(length).Imm(1).i32 542 If(not_compressed, 0).EQ.Likely.b { 543 ws4 := is_white_space_u8(Load(str_data, last_char_index).u8) 544 Goto(:L2) 545 } 546 ws5 := is_white_space_u16(Load(str_data, ShlI(last_char_index).Imm(1).i32).u16) 547Label(:L2) 548 ws6 := Phi(ws4, ws5).b 549 If(ws6, 0).EQ.Likely.b { 550 # last char is not whitespace, so check the first char 551 If(not_compressed, 0).EQ.Likely.b { 552 ws7 := is_white_space_u8(Load(str_data, 0).u8) 553 Goto(:L3) 554 } 555 ws8 := is_white_space_u16(Load(str_data, 0).u16) 556Label(:L3) 557 ws9 := Phi(ws7, ws8).b 558 If(ws9, 0).EQ.Likely.b { 559 # first char is not white space, so return 'str' 560 Return(Cast(str).SrcType(Constants::COMPILER_REFERENCE).ptr).ptr 561 } 562 Goto(:FirstCharWhitespace) 563 } 564 # last char is whitespace, so call StringTrimBase 565 LiveOut(str).DstReg(regmap[:arg0]).ref 566 LiveOut(unused1).DstReg(regmap[:arg1]).i32 567 LiveOut(unused2).DstReg(regmap[:arg2]).i32 568 entrypoint2 = get_entrypoint_offset("STRING_TRIM_BASE") 569 Intrinsic(:TAIL_CALL).AddImm(entrypoint2).MethodAsImm("StringTrimBase").Terminator.ptr 570Label(:FirstCharWhitespace) 571 LiveOut(str).DstReg(regmap[:arg0]).ref 572 LiveOut(unused1).DstReg(regmap[:arg1]).i32 573 LiveOut(unused2).DstReg(regmap[:arg2]).i32 574 entrypoint3 = get_entrypoint_offset("STRING_TRIM_LEFT_BASE") 575 Intrinsic(:TAIL_CALL).AddImm(entrypoint3).MethodAsImm("StringTrimLeftBase").Terminator.ptr 576} 577