1// check-cast vAA, type@BBBB
2// Format 21c: AA|1f BBBB
3// Throw a ClassCastException if the reference in the given register cannot be cast to the indicated
4// type.
5%def op_check_cast():
6   FETCH_FROM_THREAD_CACHE /*expected klass*/a1, .L${opcode}_miss, t0, t1
7.L${opcode}_miss_resume:
8
9   srliw t0, xINST, 8         // t0 := AA
10%  get_vreg("a0", "t0", is_unsigned=True)  # a0 := fp[AA], zext
11   beqz a0, .L${opcode}_next  // null
12   lwu a2, MIRROR_OBJECT_CLASS_OFFSET(a0)  // a2 := actual klass
13   UNPOISON_HEAP_REF a2
14   // Fast path: compare without read barrier.
15   bne a1, a2, .L${opcode}_slow
16
17.L${opcode}_next:
18   FETCH_ADVANCE_INST 2
19   GET_INST_OPCODE t0
20   GOTO_OPCODE t0
21
22.L${opcode}_miss:
23   EXPORT_PC
24   mv a0, xSELF
25   ld a1, (sp)  // caller ArtMethod*
26   mv a2, xPC
27   call nterp_get_class
28   mv a1, a0
29   j .L${opcode}_miss_resume
30
31.L${opcode}_slow:
32   // A0 and A1 in position for quick call.
33%  slow_path = add_slow_path(op_check_cast_slow_path, "t0", "t1", "t2")
34   tail $slow_path  // slow offset exceeds branch imm
35                    // args a0, a1, a2
36
37
38// Checks cases for (1) interface, (2) array, and (3) super classes.
39// Hardcoded: a0 (obj), a1 (expected klass), a2 (actual klass)
40//
41// Note. We don't do read barriers for simplicity. However, this means that fetched objects may be a
42// from-space reference. That's OK as we only fetch constant information from the references. This
43// also means that some of the comparisons below may lead to false negative due to stale data, so
44// all negative cases must pass through the runtime, via potential read barrier.
45%def op_check_cast_slow_path(z0, z1, z2):
46   // Interface check: cut to runtime.
47   lwu $z0, MIRROR_CLASS_ACCESS_FLAGS_OFFSET(a1)
48   BRANCH_IF_BIT_SET $z0, $z0, MIRROR_CLASS_IS_INTERFACE_FLAG_BIT, .L${opcode}_runtime
49
50   // Array check handled below.
51   lwu $z0, MIRROR_CLASS_COMPONENT_TYPE_OFFSET(a1)
52   // Defer z0 unpoison to array path.
53   bnez $z0, .L${opcode}_array
54
55   // Super check: find expected class, else cut to runtime.
56.L${opcode}_super:
57   lwu a2, MIRROR_CLASS_SUPER_CLASS_OFFSET(a2)
58   UNPOISON_HEAP_REF a2
59   beq a2, a1, .L${opcode}_slow_next
60   bnez a2, .L${opcode}_super
61
62.L${opcode}_runtime:
63   TEST_IF_MARKING $z0, .L${opcode}_mark
64.L${opcode}_mark_resume:
65   EXPORT_PC
66   call art_quick_check_instance_of  // args a0 (obj), a1 (expected klass)
67
68   // Advancement logic replicated here for branch distance.
69.L${opcode}_slow_next:
70   FETCH_ADVANCE_INST 2
71   GET_INST_OPCODE $z0
72   GOTO_OPCODE $z0
73
74.L${opcode}_mark:
75   call art_quick_read_barrier_mark_reg11  // a1, expected klass
76   j .L${opcode}_mark_resume
77
78.L${opcode}_array:
79   UNPOISON_HEAP_REF $z0          // z0 = expected.component
80   lwu $z1, MIRROR_CLASS_COMPONENT_TYPE_OFFSET(a2)  // z1 := actual.component
81   beqz $z1, .L${opcode}_runtime  // null: actual not an array
82   UNPOISON_HEAP_REF $z1
83   lwu $z2, MIRROR_CLASS_SUPER_CLASS_OFFSET($z0)  // z2 := expected.component.super
84   // z2 can skip unpoison for null check
85   bnez $z2, .L${opcode}_runtime  // super type exists
86   // expected.component.super is null: expected is either Object[] or primitive array.
87   lhu $z2, MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET($z0)  // z2 := expected.component.primitive
88   bnez $z2, .L${opcode}_runtime  // expected's component is primitive
89   lwu $z2, MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET($z1)  // z2 := actual.component.primitive
90   bnez $z2, .L${opcode}_runtime  // actual's component is primitive
91   // Here, z0 is Object, and z1 is a subclass of Object.
92   j .L${opcode}_slow_next
93
94
95// instance-of vA, vB, type@CCCC
96// Format 22c: B|A|20 CCCC
97// vA := 1 if vB instance-of CCCC, else 0
98// Store in the given destination register 1 if the indicated reference is an instance of the given
99// type, or 0 if not.
100%def op_instance_of():
101   srliw s7, xINST, 8    // s7 := B|A
102   srliw s8, xINST, 12   // s8 := B
103   andi s7, s7, 0xF      // s7 := A, used in slow path
104   FETCH_FROM_THREAD_CACHE /*expected klass*/ a1, .L${opcode}_miss, t0, t1
105.L${opcode}_miss_resume:
106
107%  get_vreg("a0", "s8", is_unsigned=True)  # a0 := fp[B], zext
108   beqz a0, .L${opcode}_next  // a0 = null = dst value "false"
109   lwu a2, MIRROR_OBJECT_CLASS_OFFSET(a0)  // a2 := actual klass
110   UNPOISON_HEAP_REF a2
111   // Fast path: compare without read barrier.
112   bne a1, a2, .L${opcode}_slow
113
114   li a0, 1  // dst value "true"
115
116.L${opcode}_next:
117%  set_vreg("a0", "s7", z0="t1")  # fp[A] := a0
118   FETCH_ADVANCE_INST 2
119   GET_INST_OPCODE t0
120   GOTO_OPCODE t0
121
122.L${opcode}_miss:
123   EXPORT_PC
124   mv a0, xSELF
125   ld a1, (sp)  // caller ArtMethod*
126   mv a2, xPC
127   call nterp_get_class
128   mv a1, a0
129   j .L${opcode}_miss_resume
130
131.L${opcode}_slow:
132   // A0 and A1 in position for quick call.
133%  slow_path = add_slow_path(op_instance_of_slow_path, "s7", "t0", "t1", "t2")
134   tail $slow_path  // slow offset exceeds branch imm
135                    // args a0, a1, a2
136
137
138// Checks cases for (1) interface, (2) array, and (3) super classes.
139// Hardcoded: a0 (obj), a1 (expected klass), a2 (actual klass)
140//
141// Npte. If marking, don't bother with read barrier calls - cut to runtime. This arrangement allows
142// the (non marking) super class fast path's negative case to skip the read barrier and runtime
143// call, and correctly diagnose the situation with fp[A] := 0.
144%def op_instance_of_slow_path(vA, z0, z1, z2):
145   TEST_IF_MARKING $z0, .L${opcode}_runtime_with_read_barrier
146
147   // Interface check: cut to runtime.
148   lwu $z0, MIRROR_CLASS_ACCESS_FLAGS_OFFSET(a1)
149   BRANCH_IF_BIT_SET $z0, $z0, MIRROR_CLASS_IS_INTERFACE_FLAG_BIT, .L${opcode}_runtime
150
151   // Array check handled below.
152   lwu $z0, MIRROR_CLASS_COMPONENT_TYPE_OFFSET(a1)
153   // Defer z0 unpoison to array path.
154   bnez $z0, .L${opcode}_array
155
156   // Super check: find klass up the hierarchy.
157.L${opcode}_super:
158   lwu a2, MIRROR_CLASS_SUPER_CLASS_OFFSET(a2)
159   UNPOISON_HEAP_REF a2
160   beq a2, a1, .L${opcode}_super_exit
161   bnez a2, .L${opcode}_super
162.L${opcode}_super_exit:
163   snez a0, a2  // a0 := 1 if (a1 = a2 != null), else 0 (because a2 = null)
164
165.L${opcode}_slow_next:
166%  set_vreg("a0", vA, z0=z0)  # fp[A] := a0
167   FETCH_ADVANCE_INST 2
168   GET_INST_OPCODE $z0
169   GOTO_OPCODE $z0
170
171.L${opcode}_runtime_with_read_barrier:
172   call art_quick_read_barrier_mark_reg11  // a1, expected klass
173.L${opcode}_runtime:
174   EXPORT_PC
175   call artInstanceOfFromCode  // args a0 (obj), a1 (expected klass)
176       // return a0: 1 if true, else 0
177   j .L${opcode}_slow_next
178
179.L${opcode}_array:
180   UNPOISON_HEAP_REF $z0           // z0 = expected.component
181   lwu $z1, MIRROR_CLASS_SUPER_CLASS_OFFSET($z0)  // z1 := expected.component.super
182   // z1 can skip unpoison for null check
183   bnez $z1, .L${opcode}_runtime   // super type exists
184   // Here, expected.component.super is null: expected is either Object[] or primitive array.
185   lwu a0, MIRROR_CLASS_COMPONENT_TYPE_OFFSET(a2)  // a0 := actual.component
186   beqz a0, .L${opcode}_slow_next  // actual not an array, a0 = null = dst value "false"
187   UNPOISON_HEAP_REF a0
188   lhu $z1, MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET($z0)  // z1 := expected.component.primitive
189   lhu $z2, MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET(a0)   // z2 := actual.component.primitive
190   or a0, $z1, $z2  // a0 := 0 if z1 = z2 = 0, else non-zero (Primitive::Type enum)
191   seqz a0, a0      // a0 := 1 if both are class types, else 0
192   // Here, when a0 = 1, expected.component is Object, and actual.component is a subclass of Object.
193   j .L${opcode}_slow_next
194
195
196// new-instance vAA, type@BBBB
197// Format 21c: AA|22 BBBB
198// Construct a new instance of the indicated type, storing a reference to it in the destination. The
199// type must refer to a non-array class.
200%def op_new_instance():
201   EXPORT_PC
202   srliw s7, xINST, 8  // s7 := AA
203   FETCH_FROM_THREAD_CACHE /*resolved klass*/a0, .L${opcode}_miss, t0, t1
204   TEST_IF_MARKING t0, .L${opcode}_mark
205.L${opcode}_mark_resume:
206
207   ld t0, THREAD_ALLOC_OBJECT_ENTRYPOINT_OFFSET(xSELF)
208   jalr t0     // arg a0 (klass)
209               // return a0 := new-obj
210   fence w, w  // constructor fence; main.S has details
211
212.L${opcode}_miss_resume:
213   SET_VREG_OBJECT a0, s7, z0=t0  // refs[AA] := new-obj
214   FETCH_ADVANCE_INST 2
215   GET_INST_OPCODE t0
216   GOTO_OPCODE t0
217
218.L${opcode}_mark:
219   call art_quick_read_barrier_mark_reg10  // a0, klass
220   j .L${opcode}_mark_resume
221
222.L${opcode}_miss:
223   EXPORT_PC
224   mv a0, xSELF
225   ld a1, (sp)  // caller ArtMethod*
226   mv a2, xPC
227   call nterp_allocate_object
228       // return a0 := new-obj, plus cache entry is updated with the resolved klass
229   j .L${opcode}_miss_resume
230
231
232// *** iget ***
233
234%def load(dst, src, width, zext):
235%  if width == 8 and zext:
236     lbu $dst, ($src)
237%  elif width == 8:
238     lb $dst, ($src)
239%  elif width == 16 and zext:
240     lhu $dst, ($src)
241%  elif width == 16:
242     lh $dst, ($src)
243%  elif width == 32:
244     lw $dst, ($src)
245%  elif width == 64:
246     ld $dst, ($src)
247%  else:
248%    assert False, width
249%#:
250
251
252%def store(src, dst, width):
253%  if width == 8:
254     sb $src, ($dst)
255%  elif width == 16:
256     sh $src, ($dst)
257%  elif width == 32:
258     sw $src, ($dst)
259%  elif width == 64:
260     sd $src, ($dst)
261%  else:
262%    assert False, width
263%#:
264
265
266// iget vA, vB, field@CCCC
267// Format 22c: B|A|52 CCCC
268// vA := vB.field
269%def op_iget(width=32, zext=False):
270   srliw s8, xINST, 8
271   srliw s7, xINST, 12     // s7 := B
272   andi s8, s8, 0xF        // s8 := A
273
274   // Fast path: NterpGetInstanceFieldOffset's byte offset from thread-local cache.
275   FETCH_FROM_THREAD_CACHE /*field_offset*/a0, .L${opcode}_slow, t1, t2
276.L${opcode}_slow_resume:
277
278%  get_vreg("t0", "s7", is_unsigned=True)  # t0 := holder
279   beqz t0, .L${opcode}_null
280   add t0, a0, t0          // t0 := field addr
281%  load(dst="t1", src="t0", width=width, zext=zext)
282                           // t1 := value
283   FETCH_ADVANCE_INST 2
284%  set_vreg("t1", "s8", z0="t0", width=width)
285   GET_INST_OPCODE t0
286   GOTO_OPCODE t0
287
288.L${opcode}_slow:
289   mv a0, xSELF
290   ld a1, (sp)  // a1 := caller ArtMethod*
291   mv a2, xPC
292   mv a3, zero
293   EXPORT_PC
294   call nterp_get_instance_field_offset  // result a0 := field_offset
295
296   // Test for volatile (negative value).
297   bgez a0, .L${opcode}_slow_resume
298%  volatile_path = add_slow_path(op_iget_volatile, width, zext, "s7", "s8", "t0", "t1")
299   tail $volatile_path
300
301.L${opcode}_null:
302   tail common_errNullObject
303
304
305%def op_iget_volatile(width, zext, holder, dst, z0, z1):
306%  get_vreg(z0, holder, is_unsigned=True)  # z0 := holder
307   beqz $z0, .L${opcode}_volatile_null
308   sub $z0, $z0, a0           // z0 := field addr (holder - (-offset))
309   // Atomic load: "fence rw,rw ; LOAD ; fence r,rw"
310   fence rw, rw
311%  load(dst=z1, src=z0, width=width, zext=zext)
312   fence r, rw
313                              // t1 := value
314   FETCH_ADVANCE_INST 2
315%  set_vreg(z1, dst, z0=z0, width=width)
316   GET_INST_OPCODE $z0
317   GOTO_OPCODE $z0
318
319.L${opcode}_volatile_null:
320   tail common_errNullObject
321
322
323// iget-wide vA, vB, field@CCCC
324// Format 22c: B|A|53 CCCC
325%def op_iget_wide():
326%  op_iget(width=64)
327
328
329// iget-object vA, vB, field@CCCC
330// Format 22c: B|A|54 CCCC
331%def op_iget_object():
332   srliw s8, xINST, 8
333   srliw s7, xINST, 12     // s7 := B
334   andi s8, s8, 0xF        // s8 := A
335
336   // Fast path: NterpGetInstanceFieldOffset's byte offset from thread-local cache.
337   FETCH_FROM_THREAD_CACHE /*field_offset*/a0, .L${opcode}_slow, t1, t2
338.L${opcode}_slow_resume:
339
340%  get_vreg("t0", "s7", is_unsigned=True)  # t0 := holder
341   beqz t0, .L${opcode}_null
342   add t0, a0, t0          // t0 := field addr
343   lwu a0, (t0)            // a0 := object
344   UNPOISON_HEAP_REF a0
345   TEST_IF_MARKING t1, .L${opcode}_mark
346.L${opcode}_mark_resume:
347
348   FETCH_ADVANCE_INST 2
349   SET_VREG_OBJECT a0, s8, z0=t0
350   GET_INST_OPCODE t0
351   GOTO_OPCODE t0
352
353.L${opcode}_mark:
354   call art_quick_read_barrier_mark_reg10  // a0, object
355   j .L${opcode}_mark_resume
356
357.L${opcode}_slow:
358%  slow_path = add_slow_path(op_iget_object_slow_path, "s7", "s8", "t0", "t1")
359   tail $slow_path
360
361.L${opcode}_null:
362   tail common_errNullObject
363
364
365%def op_iget_object_slow_path(holder, dst, z0, z1):
366   mv a0, xSELF
367   ld a1, (sp)  // a1 := caller ArtMethod*
368   mv a2, xPC
369   mv a3, zero
370   EXPORT_PC
371   call nterp_get_instance_field_offset  // result a0 := field_offset
372
373   // Test for volatile (negative value).
374   bltz a0, .L${opcode}_volatile
375   tail .L${opcode}_slow_resume  // resume offset exceeds branch imm
376
377.L${opcode}_volatile:
378%  get_vreg(z0, holder, is_unsigned=True)  # z0 := holder
379   beqz $z0, .L${opcode}_volatile_null
380   sub $z0, $z0, a0           // z0 := field addr (holder - (-offset))
381   // Atomic load: "fence rw,rw ; LOAD ; fence r,rw"
382   fence rw, rw
383   lwu a0, ($z0)              // a0 := object
384   fence r, rw
385   UNPOISON_HEAP_REF a0
386   TEST_IF_MARKING t1, .L${opcode}_volatile_mark
387.L${opcode}_volatile_mark_resume:
388
389   FETCH_ADVANCE_INST 2
390   SET_VREG_OBJECT a0, $dst, z0=$z0
391   GET_INST_OPCODE $z0
392   GOTO_OPCODE $z0
393
394.L${opcode}_volatile_mark:
395   call art_quick_read_barrier_mark_reg10  // a0, object
396   j .L${opcode}_volatile_mark_resume
397
398.L${opcode}_volatile_null:
399   tail common_errNullObject
400
401
402// iget-boolean vA, vB, field@CCCC
403// Format 22c: B|A|55 CCCC
404%def op_iget_boolean():
405%  op_iget(width=8, zext=True)
406
407
408// iget-byte vA, vB, field@CCCC
409// Format 22c: B|A|56 CCCC
410%def op_iget_byte():
411%  op_iget(width=8)
412
413
414// iget-char vA, vB, field@CCCC
415// Format 22c: B|A|57 CCCC
416%def op_iget_char():
417%  op_iget(width=16, zext=True)
418
419
420// iget-short vA, vB, field@CCCC
421// Format 22c: B|A|58 CCCC
422%def op_iget_short():
423%  op_iget(width=16)
424
425
426// *** iput ***
427
428// iput vA, vB, field@CCCC
429// Format 22c: B|A|59 CCCC
430// vB.field := vA
431%def op_iput(width=32):
432   srliw s8, xINST, 8
433   srliw s7, xINST, 12     // s7 := B
434   andi s8, s8, 0xF        // s8 := A
435%  get_vreg("s8", "s8", width=width)
436                           // s8 := value, held across slow path call
437
438   // Fast path: NterpGetInstanceFieldOffset's byte offset from thread-local cache.
439   FETCH_FROM_THREAD_CACHE /*resolved_field*/a0, .L${opcode}_slow, t0, t1
440.L${opcode}_slow_resume:
441
442%  get_vreg("t0", "s7", is_unsigned=True)  # t0 := holder
443   beqz t0, .L${opcode}_null
444   add t0, a0, t0          // t0 := field addr
445   FETCH_ADVANCE_INST 2
446%  store(src="s8", dst="t0", width=width)
447   GET_INST_OPCODE t0
448   GOTO_OPCODE t0
449
450.L${opcode}_slow:
451   mv a0, xSELF
452   ld a1, (sp)  // a1 := caller ArtMethod*
453   mv a2, xPC
454   mv a3, zero
455   EXPORT_PC
456   call nterp_get_instance_field_offset  // result a0 := field_offset
457
458   // Test for volatile (negative value).
459   bgez a0, .L${opcode}_slow_resume
460%  volatile_path = add_slow_path(op_iput_volatile, width, "s7", "s8", "t0", "t1")
461   tail $volatile_path
462
463.L${opcode}_null:
464   tail common_errNullObject
465
466
467%def op_iput_volatile(width, holder, value, z0, z1):
468%  get_vreg(z0, holder, is_unsigned=True)  # z0 := holder
469   beqz $z0, .L${opcode}_volatile_null
470   sub $z0, $z0, a0              // z0 := field addr (holder - (-offset))
471   // Ensure the volatile store is released.
472%  if width == 8:
473     fence rw, w
474     sb $value, ($z0)
475     fence rw, rw
476%  elif width == 16:
477     fence rw, w
478     sh $value, ($z0)
479     fence rw, rw
480%  elif width == 32:
481     amoswap.w.rl zero, $value, ($z0)
482%  elif width == 64:
483     amoswap.d.rl zero, $value, ($z0)
484%  else:
485%    assert False, width
486%#:
487
488   FETCH_ADVANCE_INST 2
489   GET_INST_OPCODE $z0
490   GOTO_OPCODE $z0
491
492.L${opcode}_volatile_null:
493   tail common_errNullObject
494
495
496// iput-wide vA, vB, field@CCCC
497// Format 22c: B|A|5a CCCC
498%def op_iput_wide():
499%  op_iput(width=64)
500
501
502// iput-object vA, vB, field@CCCC
503// Format 22c: B|A|5b CCCC
504%def op_iput_object():
505   srliw s8, xINST, 8
506   srliw s7, xINST, 12     // s7 := B
507   andi s8, s8, 0xF        // s8 := A
508%  get_vreg("s9", "s8", is_unsigned=True)  # s9 := reference
509
510   // Fast path: NterpGetInstanceFieldOffset's byte offset from thread-local cache.
511   FETCH_FROM_THREAD_CACHE /*resolved_field*/a0, .L${opcode}_slow, t0, t1
512.L${opcode}_slow_resume:   // s9 := reference (slow path only)
513
514%  get_vreg("t0", "s7", is_unsigned=True)  # t0 := holder
515   beqz t0, .L${opcode}_null
516   add t1, a0, t0          // t1 := field addr
517   POISON_HEAP_REF s9      // Poisoning maps null to null for the null check in write barrier.
518   sw s9, (t1)
519%  object_write_barrier(value="s9", holder="t0", z0="t1", z1="t2", uniq=f"{opcode}")
520
521   FETCH_ADVANCE_INST 2
522   GET_INST_OPCODE t0
523   GOTO_OPCODE t0
524
525.L${opcode}_slow:
526%  slow_path = add_slow_path(op_iput_object_slow_path, "s7", "s8", "s9", "t0", "t1", "t2")
527   tail $slow_path
528
529.L${opcode}_null:
530   tail common_errNullObject
531
532
533%def op_iput_object_slow_path(holder, src, value, z0, z1, z2):
534   mv a0, xSELF
535   ld a1, (sp)  // a1 := caller ArtMethod*
536   mv a2, xPC
537   mv a3, $value
538   EXPORT_PC
539   call nterp_get_instance_field_offset  // result a0 := field_offset
540
541   // Reload value, object may have moved.
542%  get_vreg(value, src, is_unsigned=True)  # value := fp[A] zext
543
544   // Test for volatile (negative value).
545   bltz a0, .L${opcode}_volatile
546   tail .L${opcode}_slow_resume  // resume offset exceeds branch imm
547
548.L${opcode}_volatile:
549%  get_vreg(z0, holder, is_unsigned=True)  # z0 := holder
550   beqz $z0, .L${opcode}_volatile_null
551   sub $z1, $z0, a0              // z1 := field addr (holder - (-offset))
552   // Ensure the volatile store is released.
553   POISON_HEAP_REF $value  // Poisoning maps null to null for the null check in write barrier.
554   amoswap.w.rl zero, $value, ($z1)
555%  object_write_barrier(value=value, holder=z0, z0=z1, z1=z2, uniq=f"slow_{opcode}")
556
557   FETCH_ADVANCE_INST 2
558   GET_INST_OPCODE $z0
559   GOTO_OPCODE $z0
560
561.L${opcode}_volatile_null:
562   tail common_errNullObject
563
564
565// iput-boolean vA, vB, field@CCCC
566// Format 22c: B|A|5c CCCC
567%def op_iput_boolean():
568%  op_iput(width=8)
569
570
571// iput-byte vA, vB, field@CCCC
572// Format 22c: B|A|5d CCCC
573%def op_iput_byte():
574%  op_iput(width=8)
575
576
577// iput-char vA, vB, field@CCCC
578// Format 22c: B|A|5e CCCC
579%def op_iput_char():
580%  op_iput(width=16)
581
582
583// iput-short vA, vB, field@CCCC
584// Format 22c: B|A|5f CCCC
585%def op_iput_short():
586%  op_iput(width=16)
587
588
589// *** sget ***
590
591.macro CLEAR_STATIC_VOLATILE_MARKER reg
592    andi \reg, \reg, ~0x1
593.endm
594
595// sget vAA, field@BBBB
596// Format 21c: AA|60 BBBB
597// vAA := klass.field
598%def op_sget(width=32, zext=False):
599   srliw s7, xINST, 8  // s7 := AA (live through volatile path)
600   // Fast path: NterpGetStaticField's resolved_field from thread-local cache.
601   // Stores cache value in a0 to match slow path's return from NterpGetStaticField.
602   FETCH_FROM_THREAD_CACHE /*resolved_field*/a0, .L${opcode}_slow, t0, t1
603.L${opcode}_slow_resume:
604
605   lwu t0, ART_FIELD_OFFSET_OFFSET(a0)           // t0 := field offset
606   lwu a0, ART_FIELD_DECLARING_CLASS_OFFSET(a0)  // a0 := holder
607   TEST_IF_MARKING t2, .L${opcode}_mark
608.L${opcode}_mark_resume:
609
610   add t0, t0, a0      // t0 := field addr, after possible a0 update
611%  load(dst="t1", src="t0", width=width, zext=zext)
612                       // t1 := value
613   FETCH_ADVANCE_INST 2
614%  set_vreg("t1", "s7", z0="t0", width=width)
615   GET_INST_OPCODE t0
616   GOTO_OPCODE t0
617
618.L${opcode}_mark:
619   call art_quick_read_barrier_mark_reg10  // a0, holder
620   j .L${opcode}_mark_resume
621
622.L${opcode}_slow:
623   mv a0, xSELF
624   ld a1, (sp)  // a1 := caller ArtMethod*
625   mv a2, xPC
626   mv a3, zero
627   EXPORT_PC
628   call nterp_get_static_field  // result a0 := resolved_field
629
630   // Test for volatile bit
631   slli t0, a0, 63
632   bgez t0, .L${opcode}_slow_resume
633%  volatile_path = add_slow_path(op_sget_volatile, width, zext, "s7", "t0", "t1")
634   tail $volatile_path
635
636
637// Volatile static load.
638// Temporaries: z0, z1, z2
639%def op_sget_volatile(width, zext, dst_vreg, z0, z1):
640   CLEAR_STATIC_VOLATILE_MARKER a0
641   lwu $z0, ART_FIELD_OFFSET_OFFSET(a0)          // z0 := field offset
642   lwu a0, ART_FIELD_DECLARING_CLASS_OFFSET(a0)  // a0 := holder
643   TEST_IF_MARKING $z1, .L${opcode}_volatile_mark
644.L${opcode}_volatile_mark_resume:
645
646   add $z0, $z0, a0  // z0 := field addr, after possible a0 update
647   // Atomic load: "fence rw,rw ; LOAD ; fence r,rw"
648   fence rw, rw
649%  load(dst=z1, src=z0, width=width, zext=zext)
650   fence r, rw
651       // z1 := value
652   FETCH_ADVANCE_INST 2
653%  set_vreg(z1, dst_vreg, z0=z0, width=width)
654   GET_INST_OPCODE $z0
655   GOTO_OPCODE $z0
656
657.L${opcode}_volatile_mark:
658   call art_quick_read_barrier_mark_reg10  // a0, holder
659   j .L${opcode}_volatile_mark_resume
660
661
662// sget-wide vAA, field@BBBB
663// Format 21c: AA|61 BBBB
664%def op_sget_wide():
665%  op_sget(width=64)
666
667
668// sget-object vAA, field@BBBB
669// Format 21c: AA|62 BBBB
670// Variant for object load contains extra logic for GC mark.
671%def op_sget_object():
672   srliw s7, xINST, 8  // s7 := AA (live through volatile path)
673   // Fast path: NterpGetStaticField's resolved_field from thread-local cache.
674   // Stores cache value in a0 to match slow path's return from NterpGetStaticField.
675   FETCH_FROM_THREAD_CACHE /*resolved_field*/a0, .L${opcode}_slow, t0, t1
676.L${opcode}_slow_resume:
677
678   lwu t0, ART_FIELD_OFFSET_OFFSET(a0)           // t0 := field offset
679   lwu a0, ART_FIELD_DECLARING_CLASS_OFFSET(a0)  // a0 := holder
680   TEST_IF_MARKING t1, .L${opcode}_mark
681
682   add t0, t0, a0      // t0 := field addr
683   lwu a0, (t0)        // a0 := value (ref)
684   UNPOISON_HEAP_REF a0
685
686.L${opcode}_mark_resume:
687   FETCH_ADVANCE_INST 2
688   SET_VREG_OBJECT a0, s7, z0=t0
689   GET_INST_OPCODE t0
690   GOTO_OPCODE t0
691
692.L${opcode}_mark:
693   call art_quick_read_barrier_mark_reg10  // a0, holder
694   add t0, t0, a0      // t0 := field addr, after a0 update
695   lwu a0, (t0)        // a0 := value (ref)
696   UNPOISON_HEAP_REF a0
697   call art_quick_read_barrier_mark_reg10  // a0, object
698   j .L${opcode}_mark_resume
699
700.L${opcode}_slow:
701%  slow_path = add_slow_path(op_sget_object_slow_path, "s7", "t0", "t1")
702   tail $slow_path
703
704
705// Static load, object variant. Contains both slow path and volatile path
706// due to handler size limitation in op_sget_object.
707// Hardcoded: a0, a1, a2, a3, xSELF, xPC, xINST, xFP, xREFS
708// Temporaries: z0, z1
709%def op_sget_object_slow_path(dst_vreg, z0, z1):
710   mv a0, xSELF
711   ld a1, (sp)  // a1 := caller ArtMethod*
712   mv a2, xPC
713   mv a3, zero
714   EXPORT_PC
715   call nterp_get_static_field  // result a0 := resolved_field
716
717   // Test for volatile bit
718   slli $z0, a0, 63
719   bltz $z0, .L${opcode}_volatile
720   tail .L${opcode}_slow_resume  // resume offset exceeds branch imm
721
722.L${opcode}_volatile:
723   CLEAR_STATIC_VOLATILE_MARKER a0
724   lwu $z0, ART_FIELD_OFFSET_OFFSET(a0)          // z0 := field offset
725   lwu a0, ART_FIELD_DECLARING_CLASS_OFFSET(a0)  // a0 := holder
726   TEST_IF_MARKING $z1, .L${opcode}_volatile_mark
727
728   add $z0, $z0, a0  // z0 := field addr
729   fence rw, rw
730   lwu a0, ($z0)  // Atomic ref load: "fence rw,rw, ; LOAD ; fence r,rw"
731   fence r, rw
732   UNPOISON_HEAP_REF a0
733
734.L${opcode}_volatile_mark_resume:
735   FETCH_ADVANCE_INST 2
736   SET_VREG_OBJECT a0, $dst_vreg, z0=$z0
737   GET_INST_OPCODE $z0
738   GOTO_OPCODE $z0
739
740.L${opcode}_volatile_mark:
741   call art_quick_read_barrier_mark_reg10  // a0, holder
742   add $z0, $z0, a0  // z0 := field addr, after a0 update
743   fence rw, rw
744   lwu a0, ($z0)  // Atomic ref load: "fence rw,rw, ; LOAD ; fence r,rw"
745   fence r, rw
746   UNPOISON_HEAP_REF a0
747   call art_quick_read_barrier_mark_reg10  // a0, object
748   j .L${opcode}_volatile_mark_resume
749
750
751// sget-boolean vAA, field@BBBB
752// Format 21c: AA|63 BBBB
753%def op_sget_boolean():
754%  op_sget(width=8, zext=True)
755
756
757// sget-byte vAA, field@BBBB
758// Format 21c: AA|64 BBBB
759%def op_sget_byte():
760%  op_sget(width=8)
761
762
763// sget-char vAA, field@BBBB
764// Format 21c: AA|65 BBBB
765%def op_sget_char():
766%  op_sget(width=16, zext=True)
767
768
769// sget-short vAA, field@BBBB
770// Format 21c: AA|66 BBBB
771%def op_sget_short():
772%  op_sget(width=16)
773
774
775// *** sput ***
776
777// sput vAA, field@BBBB
778// Format 21c: AA|67 BBBB
779// klass.field := vAA
780%def op_sput(width=32):
781   srliw t2, xINST, 8  // t2 := AA
782%  get_vreg("s7", "t2", width=width)
783                       // s7 := value, held across slow path call
784
785   // Fast path: NterpGetStaticField's resolved_field from thread-local cache.
786   // Stores cache value in a0 to match slow path's return from NterpGetStaticField.
787   FETCH_FROM_THREAD_CACHE /*resolved_field*/a0, .L${opcode}_slow, t0, t1
788.L${opcode}_slow_resume:
789
790   lwu t0, ART_FIELD_OFFSET_OFFSET(a0)
791   lwu a0, ART_FIELD_DECLARING_CLASS_OFFSET(a0)  // a0 := holder
792   TEST_IF_MARKING t1, .L${opcode}_mark
793.L${opcode}_mark_resume:
794
795   add t0, t0, a0  // t0 := field addr, after possible a0 update
796   FETCH_ADVANCE_INST 2
797%  store(src="s7", dst="t0", width=width)
798   GET_INST_OPCODE t0
799   GOTO_OPCODE t0
800
801.L${opcode}_mark:
802   call art_quick_read_barrier_mark_reg10  // a0, holder
803   j .L${opcode}_mark_resume
804
805.L${opcode}_slow:
806   mv a0, xSELF
807   ld a1, (sp)
808   mv a2, xPC
809   mv a3, zero
810   EXPORT_PC
811   call nterp_get_static_field  // result a0 := resolved_field
812
813   // Test for volatile bit
814   slli t0, a0, 63
815   bgez t0, .L${opcode}_slow_resume
816%  volatile_path = add_slow_path(op_sput_volatile, width, "s7", "t0", "t1")
817   tail $volatile_path
818
819
820// Volatile static store.
821// Temporaries: z0, z1
822%def op_sput_volatile(width, value, z0, z1):
823   CLEAR_STATIC_VOLATILE_MARKER a0
824   lwu $z0, ART_FIELD_OFFSET_OFFSET(a0)          // z0 := field offset
825   lwu a0, ART_FIELD_DECLARING_CLASS_OFFSET(a0)  // a0 := holder
826   TEST_IF_MARKING $z1, .L${opcode}_volatile_mark
827.L${opcode}_volatile_mark_resume:
828
829   add $z0, $z0, a0                              // z0 := field addr, after possible a0 update
830   // Ensure the volatile store is released.
831%  if width == 8:
832     fence rw, w
833     sb $value, ($z0)
834     fence rw, rw
835%  elif width == 16:
836     fence rw, w
837     sh $value, ($z0)
838     fence rw, rw
839%  elif width == 32:
840     amoswap.w.rl zero, $value, ($z0)
841%  elif width == 64:
842     amoswap.d.rl zero, $value, ($z0)
843%  else:
844%    assert False, width
845%#:
846
847   FETCH_ADVANCE_INST 2
848   GET_INST_OPCODE $z0
849   GOTO_OPCODE $z0
850
851.L${opcode}_volatile_mark:
852   call art_quick_read_barrier_mark_reg10  // a0, holder
853   j .L${opcode}_volatile_mark_resume
854
855
856// sput-wide vAA, field@BBBB
857// Format 21c: AA|68 BBBB
858%def op_sput_wide():
859%  op_sput(width=64)
860
861
862// sput-object vAA, field@BBBB
863// Format 21c: AA|69 BBBB
864%def op_sput_object():
865   srliw s7, xINST, 8      // s7 := AA (live through slow path)
866%  get_vreg("s8", "s7", is_unsigned=True)  # s8 := reference, replaced in slow path
867
868   // Fast path: NterpGetStaticField's resolved_field from thread-local cache.
869   // Stores cache value in a0 to match slow path's return from NterpGetStaticField.
870   FETCH_FROM_THREAD_CACHE /*resolved_field*/a0, .L${opcode}_slow, t0, t1
871.L${opcode}_slow_resume:   // s8 := reference (slow path only)
872
873   lwu t0, ART_FIELD_OFFSET_OFFSET(a0)
874   lwu a0, ART_FIELD_DECLARING_CLASS_OFFSET(a0)  // a0 := holder
875   TEST_IF_MARKING t1, .L${opcode}_mark
876.L${opcode}_mark_resume:
877
878   add t0, t0, a0          // t0 := field addr, after possible a0 update
879   POISON_HEAP_REF s8      // Poisoning maps null to null for the null check in write barrier.
880   sw s8, (t0)             // store reference
881%  object_write_barrier(value="s8", holder="a0", z0="t0", z1="t1", uniq=f"{opcode}")
882   FETCH_ADVANCE_INST 2
883   GET_INST_OPCODE t0
884   GOTO_OPCODE t0
885
886.L${opcode}_mark:
887   call art_quick_read_barrier_mark_reg10  // a0, holder
888   j .L${opcode}_mark_resume
889
890.L${opcode}_slow:
891%  slow_path = add_slow_path(op_sput_object_slow_path, "s7", "s8", "t0", "t1")
892   tail $slow_path  // slow path offset exceeds regular branch imm in FETCH_FROM_THREAD_CACHE
893
894
895// Static store, object variant. Contains both slow path and volatile path
896// due to handler size limitation in op_sput_object.
897// Hardcoded: a0, a1, a2, a3, xSELF, xPC, xINST, xFP, xREFS
898// Temporaries z0, z1
899%def op_sput_object_slow_path(src_vreg, value, z0, z1):
900   // Args for nterp_get_static_field
901   mv a0, xSELF
902   ld a1, (sp)
903   mv a2, xPC
904   mv a3, $value
905   EXPORT_PC
906   call nterp_get_static_field  // result a0 := resolved_field
907
908   // Reload value, it may have moved.
909%  get_vreg(value, src_vreg, is_unsigned=True)  # value := fp[AA], zext
910
911   // Test for volatile bit
912   slli $z0, a0, 63
913   bltz $z0, .L${opcode}_volatile
914   tail .L${opcode}_slow_resume  // resume offset exceeds branch imm
915
916.L${opcode}_volatile:
917   CLEAR_STATIC_VOLATILE_MARKER a0
918   lwu $z0, ART_FIELD_OFFSET_OFFSET(a0)          // z0 := field offset
919   lwu a0, ART_FIELD_DECLARING_CLASS_OFFSET(a0)  // a0 := holder
920   TEST_IF_MARKING $z1, .L${opcode}_volatile_mark
921.L${opcode}_volatile_mark_resume:
922
923   add $z0, $z0, a0  // z0 := field addr, after possible a0 update
924   // Ensure the volatile store is released.
925   // \value must NOT be the destination register, the destination gets clobbered!
926   // \value's original value is used in the write barrier below.
927   POISON_HEAP_REF $value  // Poisoning maps null to null for the null check in write barrier.
928   amoswap.w.rl zero, $value, ($z0)
929%  object_write_barrier(value=value, holder="a0", z0=z0, z1=z1, uniq=f"slow_{opcode}")
930
931   FETCH_ADVANCE_INST 2
932   GET_INST_OPCODE $z0
933   GOTO_OPCODE $z0
934
935.L${opcode}_volatile_mark:
936   call art_quick_read_barrier_mark_reg10  // a0, holder
937   j .L${opcode}_volatile_mark_resume
938
939
940%def object_write_barrier(value, holder, z0, z1, uniq):
941   beqz $value, .L${uniq}_skip_write_barrier  // No object, skip out.
942   ld $z0, THREAD_CARD_TABLE_OFFSET(xSELF)
943   srli $z1, $holder, CARD_TABLE_CARD_SHIFT
944   add $z1, $z0, $z1
945   sb $z0, ($z1)
946.L${uniq}_skip_write_barrier:
947
948
949// sput-object vAA, field@BBBB
950// Format 21c: AA|6a BBBB
951%def op_sput_boolean():
952%  op_sput(width=8)
953
954
955// sput-object vAA, field@BBBB
956// Format 21c: AA|6b BBBB
957%def op_sput_byte():
958%  op_sput(width=8)
959
960
961// sput-object vAA, field@BBBB
962// Format 21c: AA|6c BBBB
963%def op_sput_char():
964%  op_sput(width=16)
965
966
967// sput-object vAA, field@BBBB
968// Format 21c: AA|6d BBBB
969%def op_sput_short():
970%  op_sput(width=16)
971
972