1# plugin ets_string_builder 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 17module Constants 18 ETS_SB_BUFFER_OFFSET = "ark::ObjectHeader::ObjectHeaderSize()" 19 ETS_SB_INDEX_OFFSET = ETS_SB_BUFFER_OFFSET + " + ark::OBJECT_POINTER_SIZE" 20 ETS_SB_LENGTH_OFFSET = ETS_SB_INDEX_OFFSET + " + sizeof(int32_t)" 21 ETS_SB_COMPRESS_OFFSET = ETS_SB_LENGTH_OFFSET + " + sizeof(int32_t)" 22end 23 24class SynchronizationType 25 :Sync 26 :Async 27 :AsyncManual 28end 29 30# 31# Counts a number of digits in the long v (+1 for the sign if v is negative) 32# 33scoped_macro(:count_digits) do |v| 34 negative := Compare(v, 0).SrcType("DataType::INT64").LT.b 35 n_digits1 := Add(Cast(1).u64, Cast(negative).u64).u64 36 val := Cast(Abs(v).i64).u64 37 pow10_4 := Cast(10000).u64 38Label(:Loop) 39 val1 := Phi(val, val2).u64 40 n_digits := Phi(n_digits1, n_digits5).u32 41 If(val1, 10).AE.Likely.b { 42 If(val1, 100).B.b { 43 n_digits2 := AddI(n_digits).Imm(1).u32 44 Goto(:Done) 45 } 46 If(val1,1000).B.b { 47 n_digits3 := AddI(n_digits).Imm(2).u32 48 Goto(:Done) 49 } 50 If(val1, 10000).B.b { 51 n_digits4 := AddI(n_digits).Imm(3).u32 52 Goto(:Done) 53 } 54 n_digits5 := AddI(n_digits).Imm(4).u32 55 val2 := Div(val1, pow10_4).u64 56 Goto(:Loop) 57 } 58Label(:Done) 59 result := Phi(n_digits, n_digits2, n_digits3, n_digits4).u32 60end 61 62 63# Converts long to array of chars 64# Ex: '-123' is converted to array of chars as follows: 65# chars[0] = 0x002D // '-' 66# chars[1] = 0x0031 // '1' 67# chars[2] = 0x0032 // '2' 68# chars[3] = 0x0033 // '3' 69scoped_macro(:convert_long_to_char_array) do |v, chars, n_digits| 70 ten := Cast(10).u64 71 uv := Cast(Abs(v).i64).u64 72 offs := SubI(ShlI(n_digits).Imm(1).u32).Imm(2).u32 73Label(:NextDigit) 74 uv1 := Phi(uv, uv2).u64 75 dig := Mod(uv1, ten).u64 # get least significant digit as 'uv1 % 10' 76 c := AddI(dig).Imm(0x0030).u16 # convert it to utf16 char 77 offs1 := Phi(offs, offs2).u32 # select its offset 78 Store(chars, offs1, c).u16 # store char to array 79 offs2 := SubI(offs1).Imm(2).u32 # decrease offset 80 uv2 := Div(uv1, ten).u64 # prepare for the next decimal digit 81 If(uv2, 0).NE.Likely.b { 82 Goto(:NextDigit) 83 } 84 # Convert sign if any 85 If(v, 0).LT.b { 86 minus := Cast(0x002D).u16 87 StoreI(chars, minus).Imm(0).u16 88 } 89end 90 91 92scoped_macro(:convert_bool_to_char_array) do |v, chars| 93 If(v, 0).EQ.b { 94 Goto(:BoolFalse) 95 } 96 true_code := Cast(0x0065007500720074).u64 97 StoreI(chars, true_code).Imm(0).u64 98 Goto(:Done) 99Label(:BoolFalse) 100 fals_code := Cast(0x0073006c00610066).u64 101 StoreI(chars, fals_code).Imm(0).u64 102 e_code := Cast(0x0065).u16 103 StoreI(chars, e_code).Imm(8).u16 104Label(:Done) 105end 106 107macro(:ref_is_null) do |obj| 108 Compare(obj, 0).SrcType(Constants::COMPILER_REFERENCE).EQ.b 109end 110 111scoped_macro(:store_with_barrier) do |buffer, buffer_uint, offset, obj, sync_type| 112 Store(buffer, offset, obj).SetNeedBarrier(sync_type == :Async).ref 113 if sync_type == :AsyncManual 114 min_addr := LoadI(%tr).Imm(Constants::TLS_CARD_TABLE_MIN_ADDR_OFFSET).word 115 cards := LoadI(%tr).Imm(Constants::TLS_CARD_TABLE_ADDR_OFFSET).ptr 116 117 mem_word := Cast(buffer_uint).SrcType(Constants::REF_UINT).word 118 card_offset := ShrI(Sub(mem_word, min_addr).word).Imm(Constants::CARD_TABLE_CARD_BITS).word 119 card := Add(cards, card_offset).ptr 120 StoreI(card, Constants::CARD_DIRTY_VALUE).Imm(Constants::CARD_VALUE_OFFSET).u8 121 end 122end 123 124function(:StringBuilderAppendLong, 125 params: {sb: 'ref', v: 'i64', array_klass: 'ref'}, 126 regmap: $full_regmap, 127 regalloc_set: $panda_mask, 128 mode: [:FastPath]) { 129 130 # Arm32 is not supported 131 if Options.arch == :arm32 132 Intrinsic(:UNREACHABLE).void.Terminator 133 next 134 end 135 136 # 1. Check if there is a free slot in the buffer 137 index := LoadI(sb).Imm(Constants::ETS_SB_INDEX_OFFSET).i32 138 buffer := LoadI(sb).Imm(Constants::ETS_SB_BUFFER_OFFSET).ref 139 If(index, LoadI(buffer).Imm(Constants::ARRAY_LENGTH_OFFSET).i32).GE.Unlikely.b { 140 Goto(:SlowPathEntrypoint) 141 } 142 # 2. CountDigits 143 n_digits := count_digits(v) 144 # 3. Allocate array of chars in TLAB 145 char_array := allocate_array_of_chars_tlab(array_klass, Cast(n_digits).word) 146 # Let the memory writes (TLAB) be visible to other threads 147 Intrinsic(:DATA_MEMORY_BARRIER_FULL).void 148 # 4. Convert Long to utf16 chars 149 chars := Add(char_array, Constants::ARRAY_DATA_OFFSET).ptr 150 convert_long_to_char_array(v, chars, n_digits) 151 # 5. Remember array in the buffer 152 slot_offs := AddI(ShlI(index).Imm(Constants::REFERENCE_TYPE_SHIFT).i32).Imm(Constants::ARRAY_DATA_OFFSET).i32 153 Store(buffer, slot_offs, char_array).SetNeedBarrier(true).ref 154 # 6. Increment 'index' field 155 StoreI(sb, AddI(index).Imm(1).i32).Imm(Constants::ETS_SB_INDEX_OFFSET).i32 156 # 7. Update 'length' field 157 length := LoadI(sb).Imm(Constants::ETS_SB_LENGTH_OFFSET).i32 158 length := Add(length, n_digits).i32 159 StoreI(sb, length).Imm(Constants::ETS_SB_LENGTH_OFFSET).i32 160 # Do not update 'compress' field, as a string representation of a number is always compressable 161 Return(sb).ref 162Label(:SlowPathEntrypoint) 163 entrypoint = get_entrypoint_offset("STRING_BUILDER_APPEND_LONG_SLOW_PATH") 164 Intrinsic(:SLOW_PATH_ENTRY, sb, v).AddImm(entrypoint).MethodAsImm("StringBuilderAppendLong3ArgBridge").Terminator.ref 165 Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG 166} 167 168 169function(:StringBuilderAppendBool, 170 params: {sb: 'ref', v: 'u8', array_klass: 'ref'}, 171 regmap: $full_regmap, 172 regalloc_set: $panda_mask, 173 mode: [:FastPath]) { 174 175 # Arm32 is not supported 176 if Options.arch == :arm32 177 Intrinsic(:UNREACHABLE).void.Terminator 178 next 179 end 180 181 # 1. Check if there is a free slot in the buffer 182 index := LoadI(sb).Imm(Constants::ETS_SB_INDEX_OFFSET).i32 183 buffer := LoadI(sb).Imm(Constants::ETS_SB_BUFFER_OFFSET).ref 184 If(index, LoadI(buffer).Imm(Constants::ARRAY_LENGTH_OFFSET).i32).GE.Unlikely.b { 185 Goto(:SlowPathEntrypoint) 186 } 187 # 2. Allocate array of chars in TLAB 188 If(v, 0).EQ.b { 189 n_digits1 := Cast(5).word # false 190 } Else { 191 n_digits2 := Cast(4).word # true 192 } 193 n_digits := Phi(n_digits1, n_digits2).word 194 char_array := allocate_array_of_chars_tlab(array_klass, n_digits) 195 # Let the memory writes (TLAB) be visible to other threads 196 Intrinsic(:DATA_MEMORY_BARRIER_FULL).void 197 # 3. Store 'true' or 'false' to array 198 chars := Add(char_array, Constants::ARRAY_DATA_OFFSET).ptr 199 convert_bool_to_char_array(v, chars) 200 # 4. Remember array in the buffer 201 slot_offs := AddI(ShlI(index).Imm(Constants::REFERENCE_TYPE_SHIFT).i32).Imm(Constants::ARRAY_DATA_OFFSET).i32 202 Store(buffer, slot_offs, char_array).SetNeedBarrier(true).ref 203 # 5. Increment 'index' field 204 StoreI(sb, AddI(index).Imm(1).i32).Imm(Constants::ETS_SB_INDEX_OFFSET).i32 205 # 6. Update 'length' field 206 length := LoadI(sb).Imm(Constants::ETS_SB_LENGTH_OFFSET).i32 207 length := Add(length, Cast(n_digits).i32).i32 208 StoreI(sb, length).Imm(Constants::ETS_SB_LENGTH_OFFSET).i32 209 # Do not update 'compress' field, as 'true' and 'false' are always compressable 210 Return(sb).ref 211Label(:SlowPathEntrypoint) 212 entrypoint = get_entrypoint_offset("STRING_BUILDER_APPEND_BOOL_SLOW_PATH") 213 Intrinsic(:SLOW_PATH_ENTRY, sb, v).AddImm(entrypoint).MethodAsImm("StringBuilderAppendBool3ArgBridge").Terminator.ref 214 Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG 215} 216 217 218def GenerateStringBuilderAppendChar(string_compression_enabled) 219 suffix = (string_compression_enabled ? "Compressed" : "") 220 function("StringBuilderAppendChar#{suffix}".to_sym, 221 params: {sb: 'ref', ch: 'u16', array_klass: 'ref'}, 222 regmap: $full_regmap, 223 regalloc_set: $panda_mask, 224 mode: [:FastPath]) { 225 226 # Arm32 is not supported 227 if Options.arch == :arm32 228 Intrinsic(:UNREACHABLE).void.Terminator 229 next 230 end 231 232 # 1. Check if there is a free slot in the buffer 233 index := LoadI(sb).Imm(Constants::ETS_SB_INDEX_OFFSET).i32 234 buffer := LoadI(sb).Imm(Constants::ETS_SB_BUFFER_OFFSET).ref 235 If(index, LoadI(buffer).Imm(Constants::ARRAY_LENGTH_OFFSET).i32).GE.Unlikely.b { 236 Goto(:SlowPathEntrypoint) 237 } 238 # 2. Allocate array of chars in TLAB 239 char_array := allocate_array_of_chars_tlab(array_klass, Cast(1).word) 240 # Let the memory writes (TLAB) be visible to other threads 241 Intrinsic(:DATA_MEMORY_BARRIER_FULL).void 242 # 3. Store char to array 243 chars := Add(char_array, Constants::ARRAY_DATA_OFFSET).ptr 244 StoreI(chars, ch).Imm(0).u16 245 # 4. Remember array in the buffer 246 slot_offs := AddI(ShlI(index).Imm(Constants::REFERENCE_TYPE_SHIFT).i32).Imm(Constants::ARRAY_DATA_OFFSET).i32 247 Store(buffer, slot_offs, char_array).SetNeedBarrier(true).ref 248 # 5. Increment 'index' field 249 StoreI(sb, AddI(index).Imm(1).i32).Imm(Constants::ETS_SB_INDEX_OFFSET).i32 250 # 6. Update 'length' field 251 length := LoadI(sb).Imm(Constants::ETS_SB_LENGTH_OFFSET).i32 252 length := AddI(length).Imm(1).i32 253 StoreI(sb, length).Imm(Constants::ETS_SB_LENGTH_OFFSET).i32 254 # 7. Update 'compress' field 255 compress := LoadI(sb).Imm(Constants::ETS_SB_COMPRESS_OFFSET).u8 256 If(compress, 0).NE.Likely.b { 257 if string_compression_enabled 258 compressable := Compare(SubI(ch).Imm(1).u16, Cast(Constants::STRING_MUTF8_1B_MAX).u16).B.b 259 If(compressable, 0).EQ.Unlikely.b { 260 StoreI(sb, 0).Imm(Constants::ETS_SB_COMPRESS_OFFSET).u8 261 } 262 else 263 StoreI(sb, 0).Imm(Constants::ETS_SB_COMPRESS_OFFSET).u8 264 end 265 } 266 Return(sb).ref 267Label(:SlowPathEntrypoint) 268 entrypoint = get_entrypoint_offset("STRING_BUILDER_APPEND_CHAR_SLOW_PATH") 269 Intrinsic(:SLOW_PATH_ENTRY, sb, ch).AddImm(entrypoint).MethodAsImm("StringBuilderAppendChar3ArgBridge").Terminator.ref 270 Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG 271} 272end 273 274GenerateStringBuilderAppendChar(true) 275GenerateStringBuilderAppendChar(false) 276 277def GenerateStringBuilderAppendStrings(num_args, sync_type) 278 case num_args 279 when 1 280 params = {sb: 'ref', str0: 'ref'} 281 when 2 282 params = {sb: 'ref', str0: 'ref', str1: 'ref'} 283 when 3 284 params = {sb: 'ref', str0: 'ref', str1: 'ref', str2: 'ref'} 285 when 4 286 params = {sb: 'ref', str0: 'ref', str1: 'ref', str2: 'ref', str3: 'ref'} 287 else 288 raise "Unexpected number of arguments: #{num_args}" 289 end 290 function("StringBuilderAppendString#{num_args}#{sync_type}".to_sym, 291 params: params, 292 regmap: $full_regmap, 293 regalloc_set: $panda_mask, 294 mode: [:FastPath]) { 295 296 # Arm32 is not supported 297 if Options.arch == :arm32 298 Intrinsic(:UNREACHABLE).void.Terminator 299 next 300 end 301 302 # 1. Check if any input string is null 303 if num_args > 0 304 str_is_null := ref_is_null(str0) 305 end 306 if num_args > 1 307 str_is_null := Or(str_is_null, ref_is_null(str1)).b 308 end 309 if num_args > 2 310 str_is_null := Or(str_is_null, ref_is_null(str2)).b 311 end 312 if num_args > 3 313 str_is_null := Or(str_is_null, ref_is_null(str3)).b 314 end 315 IfImm(str_is_null).Imm(0).SrcType(Constants::COMPILER_BOOL).NE.Unlikely.b { 316 Goto(:SlowPathEntrypoint) 317 } 318 319 # 2. Check if there is a free slot in the buffer 320 if num_args > 0 321 index0 := LoadI(sb).Imm(Constants::ETS_SB_INDEX_OFFSET).i32 322 end 323 if num_args > 1 324 index1 := AddI(index0).Imm(1).i32 325 end 326 if num_args > 2 327 index2 := AddI(index1).Imm(1).i32 328 end 329 if num_args > 3 330 index3 := AddI(index2).Imm(1).i32 331 end 332 case num_args 333 when 1 334 index := index0 335 when 2 336 index := index1 337 when 3 338 index := index2 339 when 4 340 index := index3 341 else 342 raise "Unexpected number of arguments: #{num_args}" 343 end 344 345 buffer := LoadI(sb).Imm(Constants::ETS_SB_BUFFER_OFFSET).ref 346 If(index, LoadI(buffer).Imm(Constants::ARRAY_LENGTH_OFFSET).i32).GE.Unlikely.b { 347 Goto(:SlowPathEntrypoint) 348 } 349 buffer_uint := LoadI(sb).Imm(Constants::ETS_SB_BUFFER_OFFSET).ref_uint 350 351 # 3. Increment 'index' field 352 StoreI(sb, AddI(index).Imm(1).i32).Imm(Constants::ETS_SB_INDEX_OFFSET).i32 353 354 # 4. Store input strings into buffer 355 if num_args > 0 356 Intrinsic(:DATA_MEMORY_BARRIER_FULL).void 357 offset0 := AddI(ShlI(index0).Imm(Constants::REFERENCE_TYPE_SHIFT).i32).Imm(Constants::ARRAY_DATA_OFFSET).i32 358 store_with_barrier(buffer, buffer_uint, offset0, str0, sync_type) 359 end 360 if num_args > 1 361 Intrinsic(:DATA_MEMORY_BARRIER_FULL).void 362 offset1 := AddI(ShlI(index1).Imm(Constants::REFERENCE_TYPE_SHIFT).i32).Imm(Constants::ARRAY_DATA_OFFSET).i32 363 store_with_barrier(buffer, buffer_uint, offset1, str1, sync_type) 364 end 365 if num_args > 2 366 Intrinsic(:DATA_MEMORY_BARRIER_FULL).void 367 offset2 := AddI(ShlI(index2).Imm(Constants::REFERENCE_TYPE_SHIFT).i32).Imm(Constants::ARRAY_DATA_OFFSET).i32 368 store_with_barrier(buffer, buffer_uint, offset2, str2, sync_type) 369 end 370 if num_args > 3 371 Intrinsic(:DATA_MEMORY_BARRIER_FULL).void 372 offset3 := AddI(ShlI(index3).Imm(Constants::REFERENCE_TYPE_SHIFT).i32).Imm(Constants::ARRAY_DATA_OFFSET).i32 373 store_with_barrier(buffer, buffer_uint, offset3, str3, sync_type) 374 end 375 376 # 5. Load raw string 'length' field 377 if num_args > 0 378 length0 := LoadI(str0).Imm(Constants::STRING_LENGTH_OFFSET).i32 379 end 380 if num_args > 1 381 length1 := LoadI(str1).Imm(Constants::STRING_LENGTH_OFFSET).i32 382 end 383 if num_args > 2 384 length2 := LoadI(str2).Imm(Constants::STRING_LENGTH_OFFSET).i32 385 end 386 if num_args > 3 387 length3 := LoadI(str3).Imm(Constants::STRING_LENGTH_OFFSET).i32 388 end 389 390 # 6. Update 'compress' field 391 compress := LoadI(sb).Imm(Constants::ETS_SB_COMPRESS_OFFSET).u8 392 If(compress, 0).NE.Likely.b { 393 if num_args > 0 394 compress0 := XorI(length0).Imm(1).i32 395 compress := And(compress, compress0).i32 396 end 397 if num_args > 1 398 compress1 := XorI(length1).Imm(1).i32 399 compress := And(compress, compress1).i32 400 end 401 if num_args > 2 402 compress2 := XorI(length2).Imm(1).i32 403 compress := And(compress, compress2).i32 404 end 405 if num_args > 3 406 compress3 := XorI(length3).Imm(1).i32 407 compress := And(compress, compress3).i32 408 end 409 If(compress, 0).EQ.Unlikely.b { 410 StoreI(sb, 0).Imm(Constants::ETS_SB_COMPRESS_OFFSET).u8 411 } 412 } 413 414 # 7. Update 'length' field 415 length := LoadI(sb).Imm(Constants::ETS_SB_LENGTH_OFFSET).i32 416 if num_args > 0 417 length0 := ShrI(length0).Imm(1).i32 418 length := Add(length, length0).i32 419 end 420 if num_args > 1 421 length1 := ShrI(length1).Imm(1).i32 422 length := Add(length, length1).i32 423 end 424 if num_args > 2 425 length2 := ShrI(length2).Imm(1).i32 426 length := Add(length, length2).i32 427 end 428 if num_args > 3 429 length3 := ShrI(length3).Imm(1).i32 430 length := Add(length, length3).i32 431 end 432 433 StoreI(sb, length).Imm(Constants::ETS_SB_LENGTH_OFFSET).i32 434 435 Return(sb).ref 436Label(:SlowPathEntrypoint) 437 entrypoint = get_entrypoint_offset("STRING_BUILDER_APPEND_STRING#{num_args}_SLOW_PATH") 438 case num_args 439 when 1 440 method = "StringBuilderAppendString#{num_args}UsualBridge" 441 Intrinsic(:SLOW_PATH_ENTRY, sb, str0).AddImm(entrypoint).MethodAsImm(method).Terminator.ref 442 when 2 443 method = "StringBuilderAppendString#{num_args}OddSavedBridge" 444 Intrinsic(:SLOW_PATH_ENTRY, sb, str0, str1).AddImm(entrypoint).MethodAsImm(method).Terminator.ref 445 when 3 446 method = "StringBuilderAppendString#{num_args}UsualBridge" 447 Intrinsic(:SLOW_PATH_ENTRY, sb, str0, str1, str2).AddImm(entrypoint).MethodAsImm(method).Terminator.ref 448 when 4 449 method = "StringBuilderAppendString#{num_args}OddSavedBridge" 450 Intrinsic(:SLOW_PATH_ENTRY, sb, str0, str1, str2, str3).AddImm(entrypoint).MethodAsImm(method).Terminator.ref 451 else 452 raise "Unexpected number of arguments: #{num_args}" 453 end 454 Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG 455} 456end 457 458GenerateStringBuilderAppendStrings(2, :Sync) 459GenerateStringBuilderAppendStrings(3, :Sync) 460GenerateStringBuilderAppendStrings(4, :Sync) 461 462GenerateStringBuilderAppendStrings(2, :Async) 463GenerateStringBuilderAppendStrings(3, :Async) 464GenerateStringBuilderAppendStrings(4, :Async) 465 466GenerateStringBuilderAppendStrings(2, :AsyncManual) 467GenerateStringBuilderAppendStrings(3, :AsyncManual) 468GenerateStringBuilderAppendStrings(4, :AsyncManual) 469 470# 471# StringBuilder.toString 472# 473# Assumtion: string compression is enabled. 474# 475function(:StringBuilderToString, 476 params: {sb: 'ref', string_klass: 'ref'}, 477 regmap: $full_regmap, 478 regalloc_set: $panda_mask, 479 mode: [:FastPath]) { 480 481 # Arm32 is not supported 482 if Options.arch == :arm32 483 Intrinsic(:UNREACHABLE).void.Terminator 484 next 485 end 486 487 # Compute data size and length of a new string. 488 n_chars := LoadI(sb).Imm(Constants::ETS_SB_LENGTH_OFFSET).u32 489 len_compressed := ShlI(n_chars).Imm(1).u32 # set 'uncompressed' bit to 0 490 sb_compress := LoadI(sb).Imm(Constants::ETS_SB_COMPRESS_OFFSET).u8 491 If(sb_compress, 0).EQ.Unlikely.b { 492 size := len_compressed 493 len_uncompressed := OrI(len_compressed).Imm(1).u32 # set 'uncompressed' bit to 1 494 } 495 data_size := Phi(n_chars, size).u32 496 packed_length := Phi(len_compressed, len_uncompressed).u32 497 # Allocate a string 498 new_str := allocate_string_tlab(string_klass, Cast(data_size).word) 499 # Let the memory writes (TLAB) be visible to other threads 500 Intrinsic(:DATA_MEMORY_BARRIER_FULL).void 501 # Set new string's length 502 StoreI(new_str,packed_length).Imm(Constants::STRING_LENGTH_OFFSET).u32 503 # Set new string's hashcode to 0, so as not to spend time on its computation 504 StoreI(new_str, Cast(0).u32).Imm(Constants::STRING_HASHCODE_OFFSET).u32 505 # If string is empty, then there is nothing to do anymore 506 If(n_chars, 0).EQ.Unlikely.b { 507 Return(new_str).ptr 508 } 509 510 # Walk through the buffer elements and append their content to new_str 511 index := LoadI(sb).Imm(Constants::ETS_SB_INDEX_OFFSET).i32 512 buffer := LoadI(sb).Imm(Constants::ETS_SB_BUFFER_OFFSET).ref 513 dst_data1 := AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr 514 i1 := Cast(0).i32 515Label(:ForEachBufferSlot) 516 i := Phi(i1, i2).i32 517 dst_data := Phi(dst_data1, dst_data2).ptr 518 slot := AddI(ShlI(i).Imm(Constants::REFERENCE_TYPE_SHIFT).i32).Imm(Constants::ARRAY_DATA_OFFSET).i32 519 obj := Load(buffer, slot).ref 520 klass := load_class(obj) 521 klass_flags := LoadI(klass).Imm(Constants::BASE_CLASS_FLAGS_OFFSET).u32 522 If(AndI(klass_flags).Imm("ark::Class::STRING_CLASS").u32, 0).EQ.b { 523 Goto(:ArrayObject) 524 } 525 # ------------------- 526 # Object is a string 527 # ------------------- 528 str_len := LoadI(obj).Imm(Constants::STRING_LENGTH_OFFSET).i32 529 src_data := AddI(Cast(obj).SrcType(Constants::COMPILER_REFERENCE).ptr).Imm(Constants::STRING_DATA_OFFSET).ptr 530 src_len := ShrI(str_len).Imm(1).i32 531 If(sb_compress, 0).EQ.Unlikely.b { 532 Goto(:DoNotCompressString) 533 } 534 copy_u8_chars(src_data, dst_data, Cast(src_len).u64) 535 n_bytes1 := src_len 536 Goto(:NextObject) 537Label(:DoNotCompressString) 538 If(AndI(str_len).Imm(1).u32, 1).EQ.b { 539 Goto(:SrcNotCompressed) 540 } 541 expand_u8_to_u16_chars(src_data, dst_data, Cast(src_len).u64) 542 n_bytes2 := ShlI(src_len).Imm(1).i32 543 Goto(:NextObject) 544Label(:SrcNotCompressed) 545 copy_u16_chars(src_data, dst_data, Cast(src_len).u64) 546 n_bytes3 := ShlI(src_len).Imm(1).i32 547 Goto(:NextObject) 548 # ------------------- 549 # Object is an array 550 # ------------------- 551Label(:ArrayObject) 552 src_arr_data := AddI(Cast(obj).SrcType(Constants::COMPILER_REFERENCE).ptr).Imm(Constants::ARRAY_DATA_OFFSET).ptr 553 src_arr_len := LoadI(obj).Imm(Constants::ARRAY_LENGTH_OFFSET).i32 554 If(sb_compress, 0).EQ.Unlikely.b { 555 Goto(:DoNotCompressArray) 556 } 557 compress_u16_to_u8_chars(src_arr_data, dst_data, Cast(src_arr_len).u64) 558 n_bytes4 := src_arr_len 559 Goto(:NextObject) 560Label(:DoNotCompressArray) 561 copy_u16_chars(src_arr_data, dst_data, Cast(src_arr_len).u64) 562 n_bytes5 := ShlI(src_arr_len).Imm(1).i32 563 # Go to the next buffer slot if any 564Label(:NextObject) 565 n_bytes := Phi(n_bytes1, n_bytes2, n_bytes3, n_bytes4, n_bytes5).i32 566 dst_data2 := Add(dst_data, n_bytes).ptr 567 i2 := AddI(i).Imm(1).i32 568 If(i2, index).LT.Likely.b { 569 Goto(:ForEachBufferSlot) 570 } 571 # Everything is done 572 Return(new_str).ptr 573 # SlowPath if new string can not be allocated in TLAB 574Label(:SlowPathEntrypoint) 575 entrypoint = get_entrypoint_offset("STRING_BUILDER_TO_STRING_SLOW_PATH") 576 Intrinsic(:SLOW_PATH_ENTRY, sb).AddImm(entrypoint).MethodAsImm("StringBuilderToString2ArgBridge").Terminator.ptr 577 Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG 578} 579