#!/usr/bin/env ruby # Copyright (c) 2021-2024 Huawei Device Co., Ltd. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. include_relative 'common.irt' include_relative 'string_helpers.irt' GenerateStringEquals(lang='', dynamic=false, compressed=true) GenerateStringEquals(lang='', dynamic=false, compressed=false) GenerateCreateStringFromStringTlab(string_compression_enabled=true) GenerateCreateStringFromStringTlab(string_compression_enabled=false) GenerateCreateStringFromCharArrayTlab(string_compression_enabled=true) GenerateCreateStringFromCharArrayTlab(string_compression_enabled=false) GenerateCreateStringFromZeroBasedCharArrayTlab(string_compression_enabled=true) GenerateCreateStringFromZeroBasedCharArrayTlab(string_compression_enabled=false) GenerateSubstringFromStringTlab(string_compression_enabled=true) GenerateSubstringFromStringTlab(string_compression_enabled=false) GenerateStringGetCharsTlab(string_compression_enabled=true) GenerateStringGetCharsTlab(string_compression_enabled=false) GenerateStringHashCode(string_compression_enabled=true) GenerateStringHashCode(string_compression_enabled=false) available_regs = $panda_mask function(:StringConcat2Tlab, params: {str1: 'ref', str2: 'ref'}, regmap: $full_regmap, regalloc_set: available_regs, mode: [:FastPath]) { if Options.arch == :arm32 Intrinsic(:UNREACHABLE).void.Terminator next end klass := load_class(str1) length1 := LoadI(str1).Imm(Constants::STRING_LENGTH_OFFSET).u32 length2 := LoadI(str2).Imm(Constants::STRING_LENGTH_OFFSET).u32 # any of the strings is uncompressed (resulted string is uncompressed) has_uncompressed := AndI(Or(length1, length2).u32).Imm(1).u32 count1 := ShrI(length1).Imm(1).u32 count2 := ShrI(length2).Imm(1).u32 size := Add(count1, count2).u32 data_size := Shl(size, has_uncompressed).u32 length := Or(ShlI(size).Imm(1).u32, has_uncompressed).u32 new_str := allocate_string_tlab(klass, data_size) StoreI(new_str, length).Imm(Constants::STRING_LENGTH_OFFSET).u32 StoreI(new_str, Cast(0).u32).Imm(Constants::STRING_HASHCODE_OFFSET).u32 src_str_data1 := AddI(str1).Imm(Constants::STRING_DATA_OFFSET).ptr dst_str_data1 := AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr # has no uncompressed then everyting is compressed, data is stored as bytes If(has_uncompressed, 0).EQ.b { copy_u8_chars(src_str_data1, dst_str_data1, count1) offset := count1 src_str_data2 := AddI(str2).Imm(Constants::STRING_DATA_OFFSET).ptr dst_str_data2 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset).ptr copy_u8_chars(src_str_data2, dst_str_data2, count2) Goto(:EndCopy) } If(AndI(length1).Imm(1).u32, 0).EQ.b { expand_u8_to_u16_chars(src_str_data1, dst_str_data1, count1) } Else { copy_u16_chars(src_str_data1, dst_str_data1, count1) } offset := ShlI(count1).Imm(1).u32 src_str_data2 := AddI(str2).Imm(Constants::STRING_DATA_OFFSET).ptr dst_str_data2 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset).ptr If(AndI(length2).Imm(1).u32, 0).EQ.b { expand_u8_to_u16_chars(src_str_data2, dst_str_data2, count2) } Else { copy_u16_chars(src_str_data2, dst_str_data2, count2) } Label(:EndCopy) # String is supposed to be a constant object, so all its data should be visible by all threads Intrinsic(:DATA_MEMORY_BARRIER_FULL).void Return(new_str).ptr Label(:SlowPathEntrypoint) ep_offset = get_entrypoint_offset("STRING_CONCAT2_SLOW_PATH") Intrinsic(:SLOW_PATH_ENTRY, str1, str2).AddImm(ep_offset).MethodAsImm("StringConcat2UsualBridge").Terminator.ptr Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG } available_regs = $panda_mask function(:StringConcat3Tlab, params: {str1: 'ref', str2: 'ref', str3: 'ref'}, regmap: $full_regmap, regalloc_set: available_regs, mode: [:FastPath]) { if Options.arch == :arm32 Intrinsic(:UNREACHABLE).void.Terminator next end klass := load_class(str1) length1 := LoadI(str1).Imm(Constants::STRING_LENGTH_OFFSET).u32 length2 := LoadI(str2).Imm(Constants::STRING_LENGTH_OFFSET).u32 length3 := LoadI(str3).Imm(Constants::STRING_LENGTH_OFFSET).u32 has_uncompressed := AndI(Or(length3, Or(length1, length2).u32).u32).Imm(1).u32 count1 := ShrI(length1).Imm(1).u32 count2 := ShrI(length2).Imm(1).u32 count3 := ShrI(length3).Imm(1).u32 size := Add(count1, Add(count2, count3).u32).u32 data_size := Shl(size, has_uncompressed).u32 length := Or(ShlI(size).Imm(1).u32, has_uncompressed).u32 new_str := allocate_string_tlab(klass, data_size) StoreI(new_str, length).Imm(Constants::STRING_LENGTH_OFFSET).u32 StoreI(new_str, Cast(0).u32).Imm(Constants::STRING_HASHCODE_OFFSET).u32 src_str_data1 := AddI(str1).Imm(Constants::STRING_DATA_OFFSET).ptr dst_str_data1 := AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr offset1 := ShlI(count1).Imm(1).u32 offset2 := Add(offset1, ShlI(count2).Imm(1).u32).u32 # everything is compressed If(has_uncompressed, 0).EQ.b { copy_u8_chars(src_str_data1, dst_str_data1, count1) offset1_ := ShrI(offset1).Imm(1).u32 src_str_data2 := AddI(str2).Imm(Constants::STRING_DATA_OFFSET).ptr dst_str_data2 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset1_).ptr copy_u8_chars(src_str_data2, dst_str_data2, count2) offset2_ := ShrI(offset2).Imm(1).u32 src_str_data3 := AddI(str3).Imm(Constants::STRING_DATA_OFFSET).ptr dst_str_data3 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset2_).ptr copy_u8_chars(src_str_data3, dst_str_data3, count3) Goto(:EndCopy) } If(AndI(length1).Imm(1).u32, 0).EQ.b { expand_u8_to_u16_chars(src_str_data1, dst_str_data1, count1) } Else { copy_u16_chars(src_str_data1, dst_str_data1, count1) } src_str_data2 := AddI(str2).Imm(Constants::STRING_DATA_OFFSET).ptr dst_str_data2 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset1).ptr If(AndI(length2).Imm(1).u32, 0).EQ.b { expand_u8_to_u16_chars(src_str_data2, dst_str_data2, count2) } Else { copy_u16_chars(src_str_data2, dst_str_data2, count2) } src_str_data3 := AddI(str3).Imm(Constants::STRING_DATA_OFFSET).ptr dst_str_data3 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset2).ptr If(AndI(length3).Imm(1).u32, 0).EQ.b { expand_u8_to_u16_chars(src_str_data3, dst_str_data3, count3) } Else { copy_u16_chars(src_str_data3, dst_str_data3, count3) } Label(:EndCopy) # String is supposed to be a constant object, so all its data should be visible by all threads Intrinsic(:DATA_MEMORY_BARRIER_FULL).void Return(new_str).ptr Label(:SlowPathEntrypoint) ep_offset = get_entrypoint_offset("STRING_CONCAT3_SLOW_PATH") Intrinsic(:SLOW_PATH_ENTRY, str1, str2, str3).AddImm(ep_offset).MethodAsImm("StringConcat3OddSavedBridge").Terminator.ptr Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG } available_regs = $panda_mask function(:StringConcat4Tlab, params: {str1: 'ref', str2: 'ref', str3: 'ref', str4: 'ref'}, regmap: $full_regmap, regalloc_set: available_regs, mode: [:FastPath]) { if Options.arch == :arm32 Intrinsic(:UNREACHABLE).void.Terminator next end klass := load_class(str1) length1 := LoadI(str1).Imm(Constants::STRING_LENGTH_OFFSET).u32 length2 := LoadI(str2).Imm(Constants::STRING_LENGTH_OFFSET).u32 length3 := LoadI(str3).Imm(Constants::STRING_LENGTH_OFFSET).u32 length4 := LoadI(str4).Imm(Constants::STRING_LENGTH_OFFSET).u32 # any of the strings is uncompressed (resulted string is uncompressed) has_uncompressed := Or(length3, Or(length1, length2).u32).u32 has_uncompressed := AndI(Or(length4, has_uncompressed).u32).Imm(1).u32 count1 := ShrI(length1).Imm(1).u32 count2 := ShrI(length2).Imm(1).u32 count3 := ShrI(length3).Imm(1).u32 count4 := ShrI(length4).Imm(1).u32 size := Add(count1, Add(count2, Add(count3, count4).u32).u32).u32 data_size := Shl(size, has_uncompressed).u32 length := Or(ShlI(size).Imm(1).u32, has_uncompressed).u32 new_str := allocate_string_tlab(klass, data_size) StoreI(new_str, length).Imm(Constants::STRING_LENGTH_OFFSET).u32 StoreI(new_str, Cast(0).u32).Imm(Constants::STRING_HASHCODE_OFFSET).u32 src_str_data1 := AddI(str1).Imm(Constants::STRING_DATA_OFFSET).ptr dst_str_data1 := AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr offset1 := ShlI(count1).Imm(1).u32 offset2 := Add(offset1, ShlI(count2).Imm(1).u32).u32 offset3 := Add(offset2, ShlI(count3).Imm(1).u32).u32 # has no uncompressed then everything is compressed, data is stored as bytes If(has_uncompressed, 0).EQ.b { copy_u8_chars(src_str_data1, dst_str_data1, count1) offset1_ := ShrI(offset1).Imm(1).u32 src_str_data2 := AddI(str2).Imm(Constants::STRING_DATA_OFFSET).ptr dst_str_data2 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset1_).ptr copy_u8_chars(src_str_data2, dst_str_data2, count2) offset2_ := ShrI(offset2).Imm(1).u32 src_str_data3 := AddI(str3).Imm(Constants::STRING_DATA_OFFSET).ptr dst_str_data3 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset2_).ptr copy_u8_chars(src_str_data3, dst_str_data3, count3) offset3_ := ShrI(offset3).Imm(1).u32 src_str_data4 := AddI(str4).Imm(Constants::STRING_DATA_OFFSET).ptr dst_str_data4 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset3_).ptr copy_u8_chars(src_str_data4, dst_str_data4, count4) Goto(:EndCopy) } If(AndI(length1).Imm(1).u32, 0).EQ.b { expand_u8_to_u16_chars(src_str_data1, dst_str_data1, count1) } Else { copy_u16_chars(src_str_data1, dst_str_data1, count1) } src_str_data2 := AddI(str2).Imm(Constants::STRING_DATA_OFFSET).ptr dst_str_data2 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset1).ptr If(AndI(length2).Imm(1).u32, 0).EQ.b { expand_u8_to_u16_chars(src_str_data2, dst_str_data2, count2) } Else { copy_u16_chars(src_str_data2, dst_str_data2, count2) } src_str_data3 := AddI(str3).Imm(Constants::STRING_DATA_OFFSET).ptr dst_str_data3 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset2).ptr If(AndI(length3).Imm(1).u32, 0).EQ.b { expand_u8_to_u16_chars(src_str_data3, dst_str_data3, count3) } Else { copy_u16_chars(src_str_data3, dst_str_data3, count3) } src_str_data4 := AddI(str4).Imm(Constants::STRING_DATA_OFFSET).ptr dst_str_data4 := Add(AddI(new_str).Imm(Constants::STRING_DATA_OFFSET).ptr, offset3).ptr If(AndI(length4).Imm(1).u32, 0).EQ.b { expand_u8_to_u16_chars(src_str_data4, dst_str_data4, count4) } Else { copy_u16_chars(src_str_data4, dst_str_data4, count4) } Label(:EndCopy) # String is supposed to be a constant object, so all its data should be visible by all threads Intrinsic(:DATA_MEMORY_BARRIER_FULL).void Return(new_str).ptr Label(:SlowPathEntrypoint) ep_offset = get_entrypoint_offset("STRING_CONCAT4_SLOW_PATH") Intrinsic(:SLOW_PATH_ENTRY, str1, str2, str3, str4).AddImm(ep_offset).MethodAsImm("StringConcat4UsualBridge").Terminator.ptr Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG } function(:StringCompareTo, params: {str1: 'ref', str2: 'ref'}, regmap: $full_regmap, regalloc_set: $panda_mask, mode: [:FastPath]) { # Arm32 is not supported if Options.arch == :arm32 Intrinsic(:UNREACHABLE).void.Terminator next end If(str1, str2).EQ.Unlikely.b { Return(0).i32 } length1 := LoadI(str1).Imm(Constants::STRING_LENGTH_OFFSET).u32 length2 := LoadI(str2).Imm(Constants::STRING_LENGTH_OFFSET).u32 # cover the cases of length2 being zero and both lengths being zero If(length2, 0).EQ.Unlikely.b { Return(length1).i32 } If(length1, 0).EQ.Unlikely.b { Return(-1).i32 } buf1 := AddI(str1).Imm(Constants::STRING_DATA_OFFSET).ptr buf2 := AddI(str2).Imm(Constants::STRING_DATA_OFFSET).ptr # get the least length in chars length := ShrI(Cast(Min(length1, length2).i32).u64).Imm(1).u64 utf_1 := AndI(length1).Imm(1).u32 utf_2 := AndI(length2).Imm(1).u32 # in the unlikely case the strings are in different # encodings the comparison gets more comlicated as # we have to expand the latin string to u16 first If(utf_1, utf_2).NE.Unlikely.b { Return(compare_mixed_strings(buf1, buf2, length, utf_1).i32).i32 } utf := Cast(utf_1).u64 # make length be in actual bytes now length := Shl(length, utf).u64 # considering the data buffer to be allocated to # 8-bytes alignment, we can safely read 8-bytes chunks last_idx := SubI(length).Imm(8).i64 i1 := 0 Label(:Loop) i := Phi(i1, i2).i64 If(i, last_idx).GE.Unlikely.b { # can safely read 8 bytes behind - length and hashcode junk_bits := ShlI(Sub(i, last_idx).u64).Imm(3).u64 last_data1 := Shr(Load(buf1, last_idx).u64, junk_bits).u64 last_data2 := Shr(Load(buf2, last_idx).u64, junk_bits).u64 If(last_data1, last_data2).NE.b { Goto(:NotEqual) } Return(Sub(length1, length2).i32).i32 } data1 := Load(buf1, i).u64 data2 := Load(buf2, i).u64 If(data1, data2).NE.b { Goto(:NotEqual) } i2 := AddI(i).Imm(8).i64 Goto(:Loop) Label(:NotEqual) d1 := Phi(last_data1, data1).u64 d2 := Phi(last_data2, data2).u64 Return(calculate_chars_difference(d1, d2, utf).i32).i32 } include_plugin 'ets_string'