1# Copyright (c) 2021-2022 Huawei Device Co., Ltd. 2# Licensed under the Apache License, Version 2.0 (the "License"); 3# you may not use this file except in compliance with the License. 4# You may obtain a copy of the License at 5# 6# http://www.apache.org/licenses/LICENSE-2.0 7# 8# Unless required by applicable law or agreed to in writing, software 9# distributed under the License is distributed on an "AS IS" BASIS, 10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11# See the License for the specific language governing permissions and 12# limitations under the License. 13 14--- 15definitions: 16 - name: PandaAssembly 17 template: | 18 .language PandaAssembly 19 .record panda.Class <external> 20 .record panda.Object <external> 21 .record panda.String <external> 22 .record panda.Thread <external> 23 .record panda.Enum <external> 24 .record panda.Thread$State <external, panda.enum> 25 .record panda.Runnable <external, panda.interface> 26 .record panda.NullPointerException <external> 27 .record panda.IllegalMonitorStateException <external> 28 29 .function void panda.Object.ctor(panda.Object a0) <external, ctor> 30 31 .function panda.String panda.Enum.name(panda.Enum a0) <external> 32 33 .function u1 panda.Thread.holdsLock(panda.Object a0) <static, external> 34 .function panda.Thread$State panda.Thread.getState(panda.Thread a0) <external> 35 .function void panda.Thread.setDaemon(panda.Thread a0, u1 a1) <external> 36 .function void panda.Thread.start(panda.Thread a0) <external> 37 .function void panda.Thread.ctor(panda.Thread a0, panda.Runnable a1) <external, ctor> 38 39 .function u1 panda.String.equals(panda.String a0, panda.Object a1) <external> 40 41 .record RL <panda.implements=panda.Runnable> { 42 panda.Object monitor 43 u1 entered_monitor <panda.volatile> 44 } 45 46 .function void RL.ctor(RL a0, panda.Object a1) <ctor> { 47 call.short panda.Object.ctor, a0 48 lda.obj a1 49 stobj.obj a0, RL.monitor 50 return.void 51 } 52 53 .function void RL.run(RL a0) <panda.access=public> { 54 ldobj.obj a0, RL.monitor 55 monitorenter 56 loop: 57 ldai 1 58 stobj a0, RL.entered_monitor 59 jmp loop 60 return.void 61 } 62 63 .function panda.Object getNull() { 64 lda.null 65 return.obj 66 } 67tests: 68 - file-name: 'monitor' 69 isa: 70 title: Monitor instructions 71 description: | 72 Monitor instructions are used to synchronize object access between threads. Each object 73 is associated with a monitor, each monitor has a counter that allows to control access to 74 the monitor object. 75 These instructions take object reference in accumulator as input. If accumulator contains null then 76 NullPointerException is thrown. 77 On monitorenter VM thread tries to get ownership of the monitor in the following manner: if 78 monitor count is equal to zero, then it means that monitor doesn't belong to any thread. In 79 that case monitor is acquired by thread and monitor count is set to 1. The thread becomes the 80 monitor owner. If monitor is alread belongs to the thread, monitor count is incremented by one. 81 If monitor belongs to another thread, current thread is blocked and may try to enter the monitor 82 again. 83 On monitorexit VM thread release monitor in the following manner: thread decrements monitor count. 84 If monitor count turns out to be zero after than, then thread exits monitor and monitor is not longer 85 acquired. Other threads may try to gain ownership of the monitor. If thread tries to execute monitorexit 86 on the monitor which it doesn't own, IllegalMonitorStateException is thrown. 87 commands: 88 - file-name: 'null_monitor' 89 description: Check panda.NullPointerException when accumulator contains null 90 isa: 91 instructions: 92 - sig: monitorenter 93 acc: in:ref 94 prefix: PandaAssembly 95 exceptions: [x_null] 96 format: [pref_op_none] 97 - sig: monitorexit 98 acc: in:ref 99 prefix: PandaAssembly 100 format: [pref_op_none] 101 exceptions: 102 - x_null 103 header-template: ['PandaAssembly'] 104 tags: ['irtoc_ignore'] 105 runner-options: ['use-pa'] 106 check-type: empty 107 code-template: | 108 .function i32 main() { 109 jmp try_begin 110 catch_NPE_block_begin: 111 ldai 0 112 return 113 try_begin: 114 call.short getNull 115 %s 116 ldai 1 117 return 118 try_end: 119 .catch panda.NullPointerException, try_begin, try_end, catch_NPE_block_begin 120 } 121 cases: 122 - values: ['monitorenter'] 123 - values: ['monitorexit'] 124 125 - file-name: 'invalid_monitor_type' 126 description: Check verification failure when accumulator contains neither object, not null 127 isa: 128 instructions: 129 - sig: monitorenter 130 acc: in:ref 131 prefix: PandaAssembly 132 exceptions: [x_null] 133 format: [pref_op_none] 134 - sig: monitorexit 135 acc: in:ref 136 prefix: PandaAssembly 137 format: [pref_op_none] 138 verification: 139 - acc_obj_or_null 140 header-template: ['PandaAssembly'] 141 runner-options: ['verifier-failure', 'verifier-config', 'use-pa'] 142 check-type: exit-positive 143 tags: ['verifier', 'pa-verifier'] 144 code-template: | 145 .function i32 main() { 146 %s 147 *s 148 cases: 149 - values: ['monitorenter'] 150 - values: ['monitorexit'] 151 template-cases: 152 - values: 153 - ldai 0 154 - values: 155 - ldai.64 0 156 - values: 157 - fldai 0 158 - values: 159 - fldai.64 0 160 161 - file-name: 'exit_not_owned_monitor' 162 description: | 163 Check panda.IllegalMonitorStateException on monitorexit when a monitor associated with 164 a value in accumulator not owned by any thread 165 isa: 166 instructions: 167 - sig: monitorexit 168 acc: in:ref 169 prefix: PandaAssembly 170 format: [pref_op_none] 171 exceptions: 172 - x_monitor 173 header-template: ['PandaAssembly'] 174 tags: ['irtoc_ignore'] 175 runner-options: ['use-pa'] 176 check-type: empty 177 code-template: | 178 .function i32 main() { 179 jmp try_begin 180 catch_IMSE_block_begin: 181 ldai 0 182 return 183 try_begin: 184 newobj v0, panda.Object 185 lda.obj v0 186 monitorexit 187 ldai 1 188 return 189 try_end: 190 .catch panda.IllegalMonitorStateException, try_begin, try_end, catch_IMSE_block_begin 191 } 192 193 - file-name: 'exit_monitor_owned_by_other_thread' 194 description: | 195 Check panda.IllegalMonitorStateException on monitorexit when a monitor associated with 196 a value in accumulator is owned by other thread 197 isa: 198 instructions: 199 - sig: monitorexit 200 acc: in:ref 201 prefix: PandaAssembly 202 format: [pref_op_none] 203 exceptions: 204 - x_monitor 205 header-template: ['PandaAssembly'] 206 tags: ['irtoc_ignore'] 207 runner-options: ['use-pa'] 208 check-type: empty 209 code-template: | 210 .function i32 main() { 211 newobj v0, panda.Object 212 initobj.short RL.ctor, v0 213 sta.obj v1 214 initobj panda.Thread.ctor, v1 215 sta.obj v2 216 movi v3, 1 217 call.virt.short panda.Thread.setDaemon, v2, v3 218 219 call.virt.short panda.Thread.start, v2 220 loop: 221 ldobj v1, RL.entered_monitor 222 jeqz loop 223 try_begin: 224 lda.obj v0 225 monitorexit 226 try_end: 227 ldai 1 228 return 229 catch_IMSE_block_begin: 230 ldai 0 231 return 232 .catch panda.IllegalMonitorStateException, try_begin, try_end, catch_IMSE_block_begin 233 } 234 235 - file-name: 'enter_exit_monitor' 236 description: Check how monitor's ownership changed by monitorenter and monitorexit sequences 237 isa: 238 instructions: 239 - sig: monitorexit 240 acc: in:ref 241 prefix: PandaAssembly 242 format: [pref_op_none] 243 - sig: monitorexit 244 acc: in:ref 245 prefix: PandaAssembly 246 format: [pref_op_none] 247 header-template: ['PandaAssembly'] 248 runner-options: ['use-pa'] 249 code-template: | 250 .function i32 main() { 251 %s 252 *s 253 call.short panda.Thread.holdsLock, v0 254 template-cases: 255 - values: 256 - | 257 # 258 newobj v0, panda.Object 259 lda.obj v0 260 - values: 261 - | 262 # 263 movi v0, 0 264 newarr v0, v0, i32[] 265 lda.obj v0 266 cases: 267 - description: Check that a thread owns a monitor after first monitorexit instruction 268 values: 269 - 'monitorenter' 270 case-check-type: 'check-negative' 271 - description: Check that a thread owns a monitor after several executed monitorexit instructions 272 values: 273 - | 274 # 275 monitorenter 276 monitorenter 277 case-check-type: 'check-negative' 278 - description: | 279 Check that a thread does not own a monitor after execution of monitorenter instruction 280 followed by monitorexit instruction 281 values: 282 - | 283 # 284 monitorenter 285 monitorexit 286 case-check-type: 'check-positive' 287 - description: | 288 Check that a thread owns a monitor if amount of executed monitorexit instructions is less than amount 289 of executed monitorenter instructions 290 values: 291 - | 292 # 293 monitorenter 294 monitorenter 295 monitorexit 296 case-check-type: 'check-negative' 297 - description: | 298 Check that a thread does not own a monitor if amount of executed monitorexit instructions is the same as 299 amount of executed monitorenter instructions 300 values: 301 - | 302 # 303 monitorenter 304 monitorenter 305 monitorexit 306 monitorexit 307 case-check-type: 'check-positive' 308 309 - file-name: 'block_on_monitorenter' 310 description: | 311 Check that a thread will be blocked on monitorenter if a monitor is already owned by other thread and 312 will acquired the monitor when other thread will release it 313 isa: 314 instructions: 315 - sig: monitorexit 316 acc: in:ref 317 prefix: PandaAssembly 318 format: [pref_op_none] 319 - sig: monitorexit 320 acc: in:ref 321 prefix: PandaAssembly 322 format: [pref_op_none] 323 header-template: ['PandaAssembly'] 324 tags: ['irtoc_ignore'] 325 runner-options: ['use-pa'] 326 check-type: empty 327 code-template: | 328 .function i32 main() { 329 newobj v0, panda.Object 330 initobj.short RL.ctor, v0 331 sta.obj v1 332 initobj panda.Thread.ctor, v1 333 sta.obj v2 334 movi v3, 1 335 call.virt.short panda.Thread.setDaemon, v2, v3 336 337 lda.obj v0 338 monitorenter 339 340 call.virt.short panda.Thread.start, v2 341 342 lda.str "BLOCKED" 343 sta.obj v3 344 # wait until a thread is blocked on a monitor 345 wait_for_block_loop: 346 call.virt.short panda.Thread.getState, v2 347 sta.obj v4 348 call.short panda.Enum.name, v4 349 sta.obj v4 350 call.virt.short panda.String.equals, v3, v4 351 jeqz wait_for_block_loop 352 # release the monitor 353 lda.obj v0 354 monitorexit 355 # wait until the thread enter the monitor 356 wait_for_enter_loop: 357 ldobj v1, RL.entered_monitor 358 jeqz wait_for_enter_loop 359 360 ldai 0 361 return 362 } 363