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