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