• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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