• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# plugin interpreter_handlers
2# Copyright (c) 2021-2025 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 '../../plugins/ets/irtoc_scripts/common.irt'
16
17macro(:lookup_field_by_name) do |v, id|
18  res := cache_entry(id, false, false, :ptr, nil).ptr
19  res_u64 := Bitcast(res).SrcType("DataType::POINTER").u64
20  method_flag = AndI(res_u64).Imm(0x1).u64
21  If(res, 0).NE.Likely {
22    If(method_flag, 0x1).NE.Likely {
23      field_class := LoadI(res).Imm(Constants::FIELD_CLASS_OFFSET).ref
24      If(field_class, v).EQ.Likely {
25        field_0 := res
26        Goto(:Exit_0)
27      }
28    }
29  }
30  field_1 := call_runtime("LookupFieldByNameEntrypoint", get_cache_entry_ptr(), v, id, get_method_ptr(), %pc).ptr
31  Label(:Exit_0)
32  Phi(field_0.ptr, field_1.ptr).ptr
33end
34
35['short', 'long', 'obj'].each do |flavor|
36  macro(:"lookup_getter_by_name_#{flavor}") do |v, id|
37    res := cache_entry(id, false, false, :ptr, nil)
38    res_u64 := Bitcast(res).SrcType("DataType::POINTER").u64
39    method_flag = AndI(res_u64).Imm(0x1).u64
40    If(res, 0).NE.Likely {
41      If(method_flag, 0x1).EQ.Likely {
42        method_class := LoadI(res).Imm(Constants::METHOD_CLASS_OFFSET).ref
43        If(method_class, v).EQ.Likely {
44          method_0 := res
45          Goto(:Exit_1)
46        }
47      }
48    }
49    method_1 := call_runtime("LookupGetterByName#{flavor.capitalize}Entrypoint", get_cache_entry_ptr(), v, id, get_method_ptr(), %pc).ptr
50    Label(:Exit_1)
51    Phi(method_0, method_1).ptr
52  end
53end
54
55['short', 'long', 'obj'].each do |flavor|
56  macro(:"lookup_setter_by_name_#{flavor}") do |v, id|
57    res := cache_entry(id, false, false, :ptr, nil)
58    res_u64 := Bitcast(res).SrcType("DataType::POINTER").u64
59    method_flag = AndI(res_u64).Imm(0x1).u64
60    If(res, 0).NE.Likely {
61      If(method_flag, 0x1).EQ.Likely {
62        method_class := LoadI(res).Imm(Constants::METHOD_CLASS_OFFSET).ref
63        If(method_class, v).EQ.Likely {
64          method_0 := res
65          Goto(:Exit_1)
66        }
67      }
68    }
69    method_1 := call_runtime("LookupSetterByName#{flavor.capitalize}Entrypoint", get_cache_entry_ptr(), v, id, get_method_ptr(), %pc).ptr
70    Label(:Exit_1)
71    Phi(method_0, method_1).ptr
72  end
73end
74
75macro(:lookup_method_by_name) do |v, id|
76  res := cache_entry(id, false, false, :ptr, nil)
77  res_u64 := Bitcast(res).SrcType("DataType::POINTER").u64
78  method_flag = AndI(res_u64).Imm(0x1).u64
79  If(res, 0).NE.Likely {
80    If(method_flag, 0x1).EQ.Likely {
81      method_class := LoadI(res).Imm(Constants::METHOD_CLASS_OFFSET).ref
82      If(method_class, v).EQ.Likely {
83        method_0 := res
84        Goto(:Exit_1)
85      }
86    }
87  }
88  method_1 := call_runtime("LookupMethodByNameEntrypoint", get_cache_entry_ptr(), v, id, get_method_ptr(), %pc).ptr
89  Label(:Exit_1)
90  Phi(method_0, method_1).ptr
91end
92
93['static', 'virt'].each do |dispatch|
94  ['short', 'long', 'range'].each do |flavor|
95    macro(:"handle_ets_launch_#{dispatch}_#{flavor}") do |v, id|
96      # TODO(mbolshov): verify method
97      method_ptr = get_callee(id, dispatch == 'virt', false, v)
98      verify(method_ptr, false)
99      set_acc_object(call_runtime("LaunchFromInterpreter#{flavor.capitalize}", method_ptr, %frame, pc).ptr)
100      If(acc, 0).EQ {
101        move_to_exception
102      }
103    end
104  end
105end
106
107macro(:handle_ets_ldnullvalue) do
108  set_acc_object(ets_nullvalue).ref
109end
110
111macro(:handle_ets_movnullvalue) do |vd|
112  set_object(vd, ets_nullvalue).ref
113end
114
115macro(:handle_ets_isnullvalue) do
116  set_acc_primitive(btou32(Compare(acc.ref, ets_nullvalue).b))
117end
118
119macro(:"load_field_short") do |field, klass|
120  acc_field := acc
121  offset := LoadI(field).Imm(Constants::FIELD_OFFSET_OFFSET).u32
122  field_access_flags := LoadI(field).Imm(Constants::FIELD_ACCESS_FLAGS_OFFSET).u32
123  field_type_id := ShrI(AndI(field_access_flags).Imm("ACC_TYPE").u32).Imm("ACC_TYPE_SHIFT").u32
124  If(field_type_id, 0x7).LT {
125    [[0x2, "u8"], [0x3, "i8"], [0x4, "u8"], [0x5, "i16"], [0x6, "u16"]].each do |typeid, field_type|
126      If(field_type_id, typeid).EQ {
127        store_type = field_type[0] + "32"
128        value_0 := Load(klass, offset).send(:"#{field_type}")
129        acc_fast_value_0 := send(:"#{field_type}to#{store_type}", value_0.send(:"#{field_type}"))
130      }
131      acc_field := Phi(acc_field.u64, acc_fast_value_0.u64).u64
132    end
133    acc_fast_0 := acc_field
134  } Else {
135    value_1 := Load(klass, offset).u32
136    acc_fast_1 := u32tou64(value_1.u32)
137  }
138  Phi(acc_fast_0.u64, acc_fast_1.u64).u64
139end
140
141macro(:"load_field_long") do |field, klass|
142  offset := LoadI(field).Imm(Constants::FIELD_OFFSET_OFFSET).u32
143  Load(klass, offset).u64
144end
145
146macro(:"load_field_obj") do |field, klass|
147  offset := LoadI(field).Imm(Constants::FIELD_OFFSET_OFFSET).u32
148  Load(klass, offset).ref
149end
150
151['short', 'long', 'obj'].each do |flavor|
152  macro(:"handle_ets_ldobj_name_#{flavor}") do |v, id, size|
153    klass := vreg_value(v).ref
154    If(klass, 0).EQ.Unlikely {
155      call_runtime("ThrowNullPointerExceptionFromInterpreter").void
156      move_to_exception
157    }
158    field := lookup_field_by_name(klass, id)
159    if flavor != 'obj'
160      acc := acc.u64
161    else
162      acc := acc.ref
163    end
164    If(field, 0).NE.Likely {
165      acc_fast := send(:"load_field_#{flavor}", field, klass)
166    } Else {
167      method := send(:"lookup_getter_by_name_#{flavor}", klass, id)
168      If(method, 0).EQ.Unlikely {
169        call_runtime("ThrowEtsExceptionNoSuchGetterEntrypoint", klass, id, get_method_ptr()).void
170        move_to_exception
171      }
172      method_flags := LoadI(method).Imm(Constants::METHOD_ACCESS_FLAGS_OFFSET).u32
173      nargs := u32toword(LoadI(method).Imm(Constants::METHOD_NUM_ARGS_OFFSET).u32)
174      generic_call(id, size, false, method, nargs, lambda do |new_frame, num_vregs, _, new_moffset|
175        copy_reg(new_frame, num_vregs, v, new_moffset)
176      end)
177      dec_pc := SubI(pc).Imm(size).ptr
178    }
179    if flavor != 'obj'
180      set_acc_primitive(Phi(acc_fast.u64, acc.u64).u64)
181    else
182      set_acc_object(Phi(acc_fast.ref, acc.ref).ref)
183    end
184    frame := Phi(%frame, frame).ptr
185    if Options.arm64?
186      moffset := Phi(%moffset, moffset).word
187      method_ptr := Phi(%method_ptr, method_ptr).ptr
188    end
189    pc := Phi(%pc, dec_pc).ptr
190  end
191end
192
193macro(:"store_field_short") do |field, klass|
194  acc_field := acc
195  offset := LoadI(field).Imm(Constants::FIELD_OFFSET_OFFSET).u32
196  field_access_flags := LoadI(field).Imm(Constants::FIELD_ACCESS_FLAGS_OFFSET).u32
197  field_type_id := ShrI(AndI(field_access_flags).Imm("ACC_TYPE").u32).Imm("ACC_TYPE_SHIFT").u32
198  If(field_type_id, 0x7).LT {
199    [[0x2, "u8"], [0x3, "i8"], [0x4, "u8"], [0x5, "i16"], [0x6, "u16"]].each do |typeid, field_type|
200      If(field_type_id, typeid).EQ {
201        acc_type = field_type[0] + "32"
202        Store(klass, offset, acc_field.send(:"#{acc_type}")).send(:"#{field_type}")
203      }
204    end
205  } Else {
206    Store(klass, offset, acc_field.u32).u32
207  }
208end
209
210macro(:"store_field_long") do |field, klass|
211  offset := LoadI(field).Imm(Constants::FIELD_OFFSET_OFFSET).u32
212  Store(klass, offset, acc.u64).u64
213end
214
215macro(:"store_field_obj") do |field, klass|
216  offset := LoadI(field).Imm(Constants::FIELD_OFFSET_OFFSET).u32
217  Store(klass, offset, acc).SetNeedBarrier(true).ref
218end
219
220['short', 'long', 'obj'].each do |flavor|
221  macro(:"handle_ets_stobj_name_#{flavor}") do |v, id, size|
222    klass := vreg_value(v).ref
223    If(klass, 0).EQ.Unlikely {
224      call_runtime("ThrowNullPointerExceptionFromInterpreter").void
225      move_to_exception
226    }
227    field := lookup_field_by_name(klass, id)
228    If(field, 0).NE.Likely {
229      send(:"store_field_#{flavor}", field, klass)
230      Goto(:Exit_)
231    } Else {
232      method := send(:"lookup_setter_by_name_#{flavor}", klass, id)
233      If(method, 0).EQ.Unlikely {
234        call_runtime("ThrowEtsExceptionNoSuchSetterEntrypoint", klass, id, get_method_ptr()).void
235        move_to_exception
236      }
237      method_flags := LoadI(method).Imm(Constants::METHOD_ACCESS_FLAGS_OFFSET).u32
238      nargs := u32toword(LoadI(method).Imm(Constants::METHOD_NUM_ARGS_OFFSET).u32)
239      generic_call(id, size, false, method, nargs, lambda do |new_frame, num_vregs, _, new_moffset|
240        copy_reg(new_frame, num_vregs, v, new_moffset)
241        copy_acc_to_reg(new_frame, frame_vreg_ptr(new_frame, AddI(num_vregs).Imm(1).word), new_moffset)
242      end)
243      dec_pc := SubI(pc).Imm(size).ptr
244    }
245    Label(:Exit_)
246    if flavor == 'short'
247      acc := Phi(%acc, %acc, acc).u32
248    elsif flavor == 'long'
249      acc := Phi(%acc, %acc, acc).u64
250    elsif flavor == 'obj'
251      acc := Phi(%acc, %acc, acc).ref
252    end
253    acc_tag := Phi(%acc_tag, %acc_tag, acc_tag).u64
254    frame := Phi(%frame, %frame, frame).ptr
255    if Options.arm64?
256      moffset := Phi(%moffset, %moffset, moffset).word
257      method_ptr := Phi(%method_ptr, %method_ptr, method_ptr).ptr
258    end
259    pc := Phi(%pc, %pc, dec_pc).ptr
260  end
261end
262
263macro(:is_null) do |obj|
264  Compare(obj, 0).b
265end
266
267macro(:is_nullvalue) do |obj|
268  Compare(obj, ets_nullvalue).b
269end
270
271macro(:is_nullish) do |obj|
272  Or(is_null(obj).b, is_nullvalue(obj).b).b
273end
274
275macro(:is_string_cls) do |obj|
276  obj_flags := LoadI(obj).Imm(Constants::BASE_CLASS_FLAGS_OFFSET).u32
277  Compare(AndI(obj_flags).Imm("ark::Class::STRING_CLASS").u32, 0).NE.b
278end
279
280macro(:is_valuetyped_cls) do |obj|
281  call_runtime("IsClassValueTypedEntrypoint", obj).b
282end
283
284macro(:handle_ets_typeof) do |obj|
285  set_acc_object(call_runtime("EtsGetTypeofEntrypoint", %tr, obj).ref)
286end
287
288macro(:handle_ets_istrue) do |v|
289  set_acc_primitive(btou8(call_runtime("EtsIstrueEntrypoint", %tr, v).b))
290end
291
292['', '_strict'].each do |suffix|
293  macro(:"handle_ets_equals#{suffix}") do |v1, v2|
294    If(Compare(v1, v2).b, 0).NE.Unlikely {
295      result_0 := 1
296    } Else {
297      v1isnullish = is_nullish(v1)
298      v2isnullish = is_nullish(v2)
299      If(Or(v1isnullish, v2isnullish).b, 0).NE {
300        if suffix.empty?
301          result_1 := And(v1isnullish, v2isnullish).b
302        else
303          result_1 := 0
304        end
305      } Else {
306        v1cls := load_class(v1)
307        v2cls := load_class(v2)
308        If(And(is_valuetyped_cls(v1cls), is_valuetyped_cls(v2cls)).b, 0).EQ.Likely {
309          result_2 := 0
310        } Else {
311          result_3 := call_runtime("CompareETSValueTypedEntrypoint", %tr, v1, v2).b
312        }
313      }
314    }
315    result := Phi(result_0, result_1, result_2, result_3).b
316    set_acc_primitive(btou8(result))
317  end
318end
319
320macro(:"handle_ets_call_name_short") do |v1, v2, id, size|
321  klass := vreg_value(v1).ref
322  If(klass, 0).EQ.Unlikely {
323    call_runtime("ThrowNullPointerExceptionFromInterpreter").void
324    move_to_exception
325  }
326  method := lookup_method_by_name(klass, id)
327  If(method, 0).EQ.Unlikely {
328    call_runtime("ThrowEtsExceptionNoSuchMethodEntrypoint", klass, id, get_method_ptr()).void
329    move_to_exception
330  }
331  method_flags := LoadI(method).Imm(Constants::METHOD_ACCESS_FLAGS_OFFSET).u32
332  copy_lambda := lambda { |new_frame, num_vregs, _, new_moffset|
333    copy_reg(new_frame, num_vregs, v1, new_moffset)
334    copy_reg(new_frame, AddI(num_vregs).Imm(1).word, v2, new_moffset)
335  }
336  generic_call(id, size, false, method, 2, copy_lambda)
337end
338
339macro(:"handle_ets_call_name_long") do |v1, v2, v3, v4, id, size|
340  klass := vreg_value(v1).ref
341  If(klass, 0).EQ.Unlikely {
342    call_runtime("ThrowNullPointerExceptionFromInterpreter").void
343    move_to_exception
344  }
345  method := lookup_method_by_name(klass, id)
346  If(method, 0).EQ.Unlikely {
347    call_runtime("ThrowEtsExceptionNoSuchMethodEntrypoint", klass, id, get_method_ptr()).void
348    move_to_exception
349  }
350  method_flags := LoadI(method).Imm(Constants::METHOD_ACCESS_FLAGS_OFFSET).u32
351  copy_lambda := lambda { |new_frame, num_vregs, _, new_moffset|
352    copy_reg(new_frame, num_vregs, v1, new_moffset)
353    copy_reg(new_frame, AddI(num_vregs).Imm(1).word, v2, new_moffset)
354    copy_reg(new_frame, AddI(num_vregs).Imm(2).word, v3, new_moffset)
355    copy_reg(new_frame, AddI(num_vregs).Imm(3).word, v4, new_moffset)
356  }
357  generic_call(id, size, false, method, 4, copy_lambda)
358end
359
360macro(:"handle_ets_call_name_range") do |v, id, size|
361  klass := vreg_value(v).ref
362  If(klass, 0).EQ.Unlikely {
363    call_runtime("ThrowNullPointerExceptionFromInterpreter").void
364    move_to_exception
365  }
366  method := lookup_method_by_name(klass, id)
367  If(method, 0).EQ.Unlikely {
368    call_runtime("ThrowEtsExceptionNoSuchMethodEntrypoint", klass, id, get_method_ptr()).void
369    move_to_exception
370  }
371  method_flags := LoadI(method).Imm(Constants::METHOD_ACCESS_FLAGS_OFFSET).u32
372  copy_lambda := lambda { |new_frame, num_vregs, num_args, new_moffset|
373    dst_ptr_0 := frame_vreg_ptr(new_frame, num_vregs)
374    src_ptr_0 := vreg_ptr(v)
375    i0 := 0
376    Label(:Head)  # TODO(mbolshov): use While loops when they are ready
377    i := Phi(i0, i1).word
378    If(i, num_args).EQ.Unlikely do
379      Goto(:Exit)
380    end
381    offset := Mul(i, Constants::VREGISTER_SIZE).word
382    dst_ptr := Add(dst_ptr_0, offset).ptr
383    src_ptr := Add(src_ptr_0, offset).ptr
384    set_value(dst_ptr, get_value(src_ptr).i64)
385    set_tag_frame(new_frame, dst_ptr, get_tag(src_ptr), new_moffset)
386    i1 := Add(i, 1).word
387    Goto(:Head)
388    Label(:Exit)
389  }
390  generic_call(id, size, false, method, nil, copy_lambda)
391end
392