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