• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# plugin interpreter_handlers
2# Copyright (c) 2021-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 '../../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
75['static', 'virt'].each do |dispatch|
76  ['short', 'long', 'range'].each do |flavor|
77    macro(:"handle_ets_launch_#{dispatch}_#{flavor}") do |v, id|
78      # TODO(mbolshov): verify method
79      method_ptr = get_callee(id, dispatch == 'virt', false, v)
80      verify(method_ptr, false)
81      set_acc_object(call_runtime("LaunchFromInterpreter#{flavor.capitalize}", method_ptr, %frame, pc).ptr)
82      If(acc, 0).EQ {
83        move_to_exception
84      }
85    end
86  end
87end
88
89macro(:handle_ets_ldundefined) do
90  set_acc_object(ets_undefined).ref
91end
92
93macro(:handle_ets_movundefined) do |vd|
94  set_object(vd, ets_undefined).ref
95end
96
97macro(:handle_ets_isundefined) do
98  set_acc_primitive(btou32(Compare(acc.ref, ets_undefined).b))
99end
100
101macro(:"load_field_short") do |field, klass|
102  acc_field := acc
103  offset := LoadI(field).Imm(Constants::FIELD_OFFSET_OFFSET).u32
104  field_access_flags := LoadI(field).Imm(Constants::FIELD_ACCESS_FLAGS_OFFSET).u32
105  field_type_id := ShrI(AndI(field_access_flags).Imm("ACC_TYPE").u32).Imm("ACC_TYPE_SHIFT").u32
106  If(field_type_id, 0x7).LT {
107    [[0x2, "u8"], [0x3, "i8"], [0x4, "u8"], [0x5, "i16"], [0x6, "u16"]].each do |typeid, field_type|
108      If(field_type_id, typeid).EQ {
109        store_type = field_type[0] + "32"
110        value_0 := Load(klass, offset).send(:"#{field_type}")
111        acc_fast_value_0 := send(:"#{field_type}to#{store_type}", value_0.send(:"#{field_type}"))
112      }
113      acc_field := Phi(acc_field.u64, acc_fast_value_0.u64).u64
114    end
115    acc_fast_0 := acc_field
116  } Else {
117    value_1 := Load(klass, offset).u32
118    acc_fast_1 := u32tou64(value_1.u32)
119  }
120  Phi(acc_fast_0.u64, acc_fast_1.u64).u64
121end
122
123macro(:"load_field_long") do |field, klass|
124  offset := LoadI(field).Imm(Constants::FIELD_OFFSET_OFFSET).u32
125  Load(klass, offset).u64
126end
127
128macro(:"load_field_obj") do |field, klass|
129  offset := LoadI(field).Imm(Constants::FIELD_OFFSET_OFFSET).u32
130  Load(klass, offset).ref
131end
132
133['short', 'long', 'obj'].each do |flavor|
134  macro(:"handle_ets_ldobj_name_#{flavor}") do |v, id, size|
135    klass := vreg_value(v).ref
136    If(klass, 0).EQ.Unlikely {
137      call_runtime("ThrowNullPointerExceptionFromInterpreter").void
138      move_to_exception
139    }
140    field := lookup_field_by_name(klass, id)
141    if flavor != 'obj'
142      acc := acc.u64
143    else
144      acc := acc.ref
145    end
146    If(field, 0).NE.Likely {
147      acc_fast := send(:"load_field_#{flavor}", field, klass)
148    } Else {
149      method := send(:"lookup_getter_by_name_#{flavor}", klass, id)
150      If(method, 0).EQ.Unlikely {
151        call_runtime("ThrowEtsExceptionNoSuchGetterEntrypoint", klass, id, get_method_ptr()).void
152        move_to_exception
153      }
154      method_flags := LoadI(method).Imm(Constants::METHOD_ACCESS_FLAGS_OFFSET).u32
155      nargs := u32toword(LoadI(method).Imm(Constants::METHOD_NUM_ARGS_OFFSET).u32)
156      generic_call(id, size, false, method, nargs, lambda do |new_frame, num_vregs, _, new_moffset|
157        copy_reg(new_frame, num_vregs, v, new_moffset)
158      end)
159      dec_pc := SubI(pc).Imm(size).ptr
160    }
161    if flavor != 'obj'
162      set_acc_primitive(Phi(acc_fast.u64, acc.u64).u64)
163    else
164      set_acc_object(Phi(acc_fast.ref, acc.ref).ref)
165    end
166    frame := Phi(%frame, frame).ptr
167    if Options.arm64?
168      moffset := Phi(%moffset, moffset).word
169      method_ptr := Phi(%method_ptr, method_ptr).ptr
170    end
171    pc := Phi(%pc, dec_pc).ptr
172  end
173end
174
175macro(:"store_field_short") do |field, klass|
176  acc_field := acc
177  offset := LoadI(field).Imm(Constants::FIELD_OFFSET_OFFSET).u32
178  field_access_flags := LoadI(field).Imm(Constants::FIELD_ACCESS_FLAGS_OFFSET).u32
179  field_type_id := ShrI(AndI(field_access_flags).Imm("ACC_TYPE").u32).Imm("ACC_TYPE_SHIFT").u32
180  If(field_type_id, 0x7).LT {
181    [[0x2, "u8"], [0x3, "i8"], [0x4, "u8"], [0x5, "i16"], [0x6, "u16"]].each do |typeid, field_type|
182      If(field_type_id, typeid).EQ {
183        acc_type = field_type[0] + "32"
184        Store(klass, offset, acc_field.send(:"#{acc_type}")).send(:"#{field_type}")
185      }
186    end
187  } Else {
188    Store(klass, offset, acc_field.u32).u32
189  }
190end
191
192macro(:"store_field_long") do |field, klass|
193  offset := LoadI(field).Imm(Constants::FIELD_OFFSET_OFFSET).u32
194  Store(klass, offset, acc.u64).u64
195end
196
197macro(:"store_field_obj") do |field, klass|
198  offset := LoadI(field).Imm(Constants::FIELD_OFFSET_OFFSET).u32
199  Store(klass, offset, acc).SetNeedBarrier(true).ref
200end
201
202['short', 'long', 'obj'].each do |flavor|
203  macro(:"handle_ets_stobj_name_#{flavor}") do |v, id, size|
204    klass := vreg_value(v).ref
205    If(klass, 0).EQ.Unlikely {
206      call_runtime("ThrowNullPointerExceptionFromInterpreter").void
207      move_to_exception
208    }
209    field := lookup_field_by_name(klass, id)
210    If(field, 0).NE.Likely {
211      send(:"store_field_#{flavor}", field, klass)
212      Goto(:Exit_)
213    } Else {
214      method := send(:"lookup_setter_by_name_#{flavor}", klass, id)
215      If(method, 0).EQ.Unlikely {
216        call_runtime("ThrowEtsExceptionNoSuchSetterEntrypoint", klass, id, get_method_ptr()).void
217        move_to_exception
218      }
219      method_flags := LoadI(method).Imm(Constants::METHOD_ACCESS_FLAGS_OFFSET).u32
220      nargs := u32toword(LoadI(method).Imm(Constants::METHOD_NUM_ARGS_OFFSET).u32)
221      generic_call(id, size, false, method, nargs, lambda do |new_frame, num_vregs, _, new_moffset|
222        copy_reg(new_frame, num_vregs, v, new_moffset)
223        copy_acc_to_reg(new_frame, frame_vreg_ptr(new_frame, AddI(num_vregs).Imm(1).word), new_moffset)
224      end)
225      dec_pc := SubI(pc).Imm(size).ptr
226    }
227    Label(:Exit_)
228    if flavor == 'short'
229      acc := Phi(%acc, %acc, acc).u32
230    elsif flavor == 'long'
231      acc := Phi(%acc, %acc, acc).u64
232    elsif flavor == 'obj'
233      acc := Phi(%acc, %acc, acc).ref
234    end
235    acc_tag := Phi(%acc_tag, %acc_tag, acc_tag).u64
236    frame := Phi(%frame, %frame, frame).ptr
237    if Options.arm64?
238      moffset := Phi(%moffset, %moffset, moffset).word
239      method_ptr := Phi(%method_ptr, %method_ptr, method_ptr).ptr
240    end
241    pc := Phi(%pc, %pc, dec_pc).ptr
242  end
243end
244
245macro(:is_null) do |obj|
246  Compare(obj, 0).b
247end
248
249macro(:is_undefined) do |obj|
250  Compare(obj, ets_undefined).b
251end
252
253macro(:is_nullish) do |obj|
254  Or(is_null(obj).b, is_undefined(obj).b).b
255end
256
257macro(:is_string_cls) do |obj|
258  obj_flags := LoadI(obj).Imm(Constants::BASE_CLASS_FLAGS_OFFSET).u32
259  Compare(AndI(obj_flags).Imm("ark::Class::STRING_CLASS").u32, 0).NE.b
260end
261
262macro(:is_valuetyped_cls) do |obj|
263  call_runtime("IsClassValueTypedEntrypoint", obj).b
264end
265
266macro(:handle_ets_equals) do |v1, v2|
267  If(Compare(v1, v2).b, 0).NE.Unlikely {
268    result_0 := 1
269  } Else {
270    v1isnullish = is_nullish(v1)
271    v2isnullish = is_nullish(v2)
272    If(Or(v1isnullish, v2isnullish).b, 0).NE {
273      result_1 := And(v1isnullish, v2isnullish).b
274    } Else {
275      v1cls := GetInstanceClass(v1).ref
276      v2cls := GetInstanceClass(v2).ref
277      If(And(is_valuetyped_cls(v1cls), is_valuetyped_cls(v2cls)).b, 0).EQ.Likely {
278        result_2 := 0
279      } Else {
280        result_3 := call_runtime("CompareETSValueTypedEntrypoint", %tr, v1, v2).b
281      }
282    }
283  }
284  result := Phi(result_0, result_1, result_2, result_3).b
285  set_acc_primitive(btou8(result))
286end
287