| /* |
| * Copyright (C) 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| class MultipleObject { |
| Object inner; |
| Object inner2; |
| |
| static Object inner_static; |
| } |
| |
| public class Main { |
| public static void main(String[] args) throws Error { |
| // Several sets, same receiver. |
| $noinline$testInstanceFieldSets(new Main(), new Object(), new Object(), new Object()); |
| $noinline$testStaticFieldSets(new Object(), new Object(), new Object()); |
| // Object ArraySets can throw since they need a type check so we cannot perform the |
| // optimization. |
| $noinline$testArraySets(new Object[3], new Object(), new Object(), new Object()); |
| // If we are swapping elements in the array, no need for a type check. |
| $noinline$testSwapArray(new Object[3]); |
| // If the array and the values have the same RTI, no need for a type check. |
| $noinline$testArraySetsSameRTI(); |
| |
| // We cannot rely on `null` sets to perform the optimization. |
| $noinline$testNullInstanceFieldSets(new Main(), new Object()); |
| $noinline$testNullStaticFieldSets(new Object()); |
| $noinline$testNullArraySets(new Object[3], new Object()); |
| |
| // Several sets, multiple receivers. (set obj1, obj2, obj1 and see that the card of obj1 |
| // gets eliminated) |
| $noinline$testInstanceFieldSetsMultipleReceivers( |
| new Main(), new Object(), new Object(), new Object()); |
| $noinline$testStaticFieldSetsMultipleReceivers(new Object(), new Object(), new Object()); |
| $noinline$testArraySetsMultipleReceiversSameRTI(); |
| |
| // The write barrier elimination optimization is blocked by invokes, suspend checks, and |
| // instructions that can throw. |
| $noinline$testInstanceFieldSetsBlocked( |
| new Main(), new Object(), new Object(), new Object()); |
| $noinline$testStaticFieldSetsBlocked(new Object(), new Object(), new Object()); |
| $noinline$testArraySetsSameRTIBlocked(); |
| } |
| |
| /// CHECK-START: Main Main.$noinline$testInstanceFieldSets(Main, java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after) |
| /// CHECK: InstanceFieldSet field_name:Main.inner field_type:Reference write_barrier_kind:EmitNoNullCheck |
| /// CHECK: InstanceFieldSet field_name:Main.inner2 field_type:Reference write_barrier_kind:DontEmit |
| /// CHECK: InstanceFieldSet field_name:Main.inner3 field_type:Reference write_barrier_kind:DontEmit |
| |
| /// CHECK-START: Main Main.$noinline$testInstanceFieldSets(Main, java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after) |
| /// CHECK: ; card_table |
| /// CHECK-NOT: ; card_table |
| private static Main $noinline$testInstanceFieldSets(Main m, Object o, Object o2, Object o3) { |
| m.inner = o; |
| m.inner2 = o2; |
| m.inner3 = o3; |
| return m; |
| } |
| |
| /// CHECK-START: void Main.$noinline$testStaticFieldSets(java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after) |
| /// CHECK: StaticFieldSet field_name:Main.inner_static field_type:Reference write_barrier_kind:EmitNoNullCheck |
| /// CHECK: StaticFieldSet field_name:Main.inner_static2 field_type:Reference write_barrier_kind:DontEmit |
| /// CHECK: StaticFieldSet field_name:Main.inner_static3 field_type:Reference write_barrier_kind:DontEmit |
| |
| /// CHECK-START: void Main.$noinline$testStaticFieldSets(java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after) |
| /// CHECK: ; card_table |
| /// CHECK-NOT: ; card_table |
| private static void $noinline$testStaticFieldSets(Object o, Object o2, Object o3) { |
| inner_static = o; |
| inner_static2 = o2; |
| inner_static3 = o3; |
| } |
| |
| /// CHECK-START: java.lang.Object[] Main.$noinline$testArraySets(java.lang.Object[], java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after) |
| /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true write_barrier_kind:EmitNoNullCheck |
| /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true write_barrier_kind:EmitNoNullCheck |
| /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true write_barrier_kind:EmitNoNullCheck |
| |
| /// CHECK-START: java.lang.Object[] Main.$noinline$testArraySets(java.lang.Object[], java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after) |
| /// CHECK: ; card_table |
| /// CHECK: ; card_table |
| /// CHECK: ; card_table |
| /// CHECK-NOT: ; card_table |
| private static java.lang.Object[] $noinline$testArraySets( |
| Object[] arr, Object o, Object o2, Object o3) { |
| arr[0] = o; |
| arr[1] = o2; |
| arr[2] = o3; |
| return arr; |
| } |
| |
| /// CHECK-START: java.lang.Object[] Main.$noinline$testSwapArray(java.lang.Object[]) disassembly (after) |
| /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck |
| /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit |
| /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit |
| |
| /// CHECK-START: java.lang.Object[] Main.$noinline$testSwapArray(java.lang.Object[]) disassembly (after) |
| /// CHECK: ; card_table |
| /// CHECK-NOT: ; card_table |
| private static java.lang.Object[] $noinline$testSwapArray(Object[] arr) { |
| arr[0] = arr[1]; |
| arr[1] = arr[2]; |
| arr[2] = arr[0]; |
| return arr; |
| } |
| |
| /// CHECK-START: java.lang.Object[] Main.$noinline$testArraySetsSameRTI() disassembly (after) |
| /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck |
| /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit |
| /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit |
| |
| /// CHECK-START: java.lang.Object[] Main.$noinline$testArraySetsSameRTI() disassembly (after) |
| /// CHECK: ; card_table |
| /// CHECK-NOT: ; card_table |
| private static java.lang.Object[] $noinline$testArraySetsSameRTI() { |
| Object[] arr = new Object[3]; |
| arr[0] = inner_static; |
| arr[1] = inner_static2; |
| arr[2] = inner_static3; |
| return arr; |
| } |
| |
| /// CHECK-START: Main Main.$noinline$testNullInstanceFieldSets(Main, java.lang.Object) disassembly (after) |
| /// CHECK: InstanceFieldSet field_name:Main.inner field_type:Reference write_barrier_kind:DontEmit |
| /// CHECK: InstanceFieldSet field_name:Main.inner2 field_type:Reference write_barrier_kind:EmitWithNullCheck |
| /// CHECK: InstanceFieldSet field_name:Main.inner3 field_type:Reference write_barrier_kind:DontEmit |
| |
| /// CHECK-START: Main Main.$noinline$testNullInstanceFieldSets(Main, java.lang.Object) disassembly (after) |
| /// CHECK: ; card_table |
| /// CHECK-NOT: ; card_table |
| private static Main $noinline$testNullInstanceFieldSets(Main m, Object o) { |
| m.inner = null; |
| m.inner2 = o; |
| m.inner3 = null; |
| return m; |
| } |
| |
| /// CHECK-START: void Main.$noinline$testNullStaticFieldSets(java.lang.Object) disassembly (after) |
| /// CHECK: StaticFieldSet field_name:Main.inner_static field_type:Reference write_barrier_kind:DontEmit |
| /// CHECK: StaticFieldSet field_name:Main.inner_static2 field_type:Reference write_barrier_kind:EmitWithNullCheck |
| /// CHECK: StaticFieldSet field_name:Main.inner_static3 field_type:Reference write_barrier_kind:DontEmit |
| |
| /// CHECK-START: void Main.$noinline$testNullStaticFieldSets(java.lang.Object) disassembly (after) |
| /// CHECK: ; card_table |
| /// CHECK-NOT: ; card_table |
| private static void $noinline$testNullStaticFieldSets(Object o) { |
| inner_static = null; |
| inner_static2 = o; |
| inner_static3 = null; |
| } |
| |
| /// CHECK-START: java.lang.Object[] Main.$noinline$testNullArraySets(java.lang.Object[], java.lang.Object) disassembly (after) |
| /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit |
| /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true write_barrier_kind:EmitNoNullCheck |
| /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit |
| |
| /// CHECK-START: java.lang.Object[] Main.$noinline$testNullArraySets(java.lang.Object[], java.lang.Object) disassembly (after) |
| /// CHECK: ; card_table |
| /// CHECK-NOT: ; card_table |
| private static Object[] $noinline$testNullArraySets(Object[] arr, Object o) { |
| arr[0] = null; |
| arr[1] = o; |
| arr[2] = null; |
| return arr; |
| } |
| |
| /// CHECK-START: Main Main.$noinline$testInstanceFieldSetsMultipleReceivers(Main, java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after) |
| // There are two extra card_tables for the initialization of the MultipleObject. |
| /// CHECK: InstanceFieldSet field_name:MultipleObject.inner field_type:Reference write_barrier_kind:EmitNoNullCheck |
| /// CHECK: InstanceFieldSet field_name:MultipleObject.inner field_type:Reference write_barrier_kind:EmitWithNullCheck |
| /// CHECK: InstanceFieldSet field_name:MultipleObject.inner2 field_type:Reference write_barrier_kind:DontEmit |
| |
| // Each one of the two NewInstance instructions have their own `card_table` reference. |
| /// CHECK-START: Main Main.$noinline$testInstanceFieldSetsMultipleReceivers(Main, java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after) |
| /// CHECK: ; card_table |
| /// CHECK: ; card_table |
| /// CHECK: ; card_table |
| /// CHECK: ; card_table |
| /// CHECK-NOT: ; card_table |
| private static Main $noinline$testInstanceFieldSetsMultipleReceivers( |
| Main m, Object o, Object o2, Object o3) throws Error { |
| m.mo = new MultipleObject(); |
| m.mo2 = new MultipleObject(); |
| |
| m.mo.inner = o; |
| // This card table for `m.mo2` can't me removed. Note that in `m.mo2 = new |
| // MultipleObject();` above the receiver is `m`, not `m.mo2. |
| m.mo2.inner = o2; |
| // This card table for `m.mo` can me removed. |
| m.mo.inner2 = o3; |
| return m; |
| } |
| |
| /// CHECK-START: void Main.$noinline$testStaticFieldSetsMultipleReceivers(java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after) |
| /// CHECK: StaticFieldSet field_name:MultipleObject.inner_static field_type:Reference write_barrier_kind:EmitWithNullCheck |
| /// CHECK: StaticFieldSet field_name:Main.inner_static2 field_type:Reference write_barrier_kind:EmitNoNullCheck |
| /// CHECK: StaticFieldSet field_name:Main.inner_static3 field_type:Reference write_barrier_kind:DontEmit |
| |
| /// CHECK-START: void Main.$noinline$testStaticFieldSetsMultipleReceivers(java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after) |
| /// CHECK: ; card_table |
| /// CHECK: ; card_table |
| /// CHECK-NOT: ; card_table |
| private static void $noinline$testStaticFieldSetsMultipleReceivers( |
| Object o, Object o2, Object o3) { |
| MultipleObject.inner_static = o; |
| inner_static2 = o2; |
| inner_static3 = o3; |
| } |
| |
| /// CHECK-START: java.lang.Object[][] Main.$noinline$testArraySetsMultipleReceiversSameRTI() disassembly (after) |
| // Initializing the values |
| /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck |
| /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck |
| /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit |
| // Setting the `array_of_arrays`. |
| /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck |
| /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit |
| |
| /// CHECK-START: java.lang.Object[][] Main.$noinline$testArraySetsMultipleReceiversSameRTI() disassembly (after) |
| // Two array sets can't eliminate the write barrier |
| /// CHECK: ; card_table |
| /// CHECK: ; card_table |
| // One write barrier for the array of arrays' sets |
| /// CHECK: ; card_table |
| /// CHECK-NOT: ; card_table |
| private static java.lang.Object[][] $noinline$testArraySetsMultipleReceiversSameRTI() { |
| Object[] arr = new Object[3]; |
| Object[] other_arr = new Object[3]; |
| |
| arr[0] = inner_static; |
| other_arr[1] = inner_static2; |
| arr[2] = inner_static3; |
| |
| // Return them so that LSE doesn't delete them |
| Object[][] array_of_arrays = {arr, other_arr}; |
| return array_of_arrays; |
| } |
| |
| private static void $noinline$emptyMethod() {} |
| |
| /// CHECK-START: Main Main.$noinline$testInstanceFieldSetsBlocked(Main, java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after) |
| /// CHECK: InstanceFieldSet field_name:Main.inner field_type:Reference write_barrier_kind:EmitWithNullCheck |
| /// CHECK: InvokeStaticOrDirect method_name:Main.$noinline$emptyMethod |
| /// CHECK: InstanceFieldSet field_name:Main.inner2 field_type:Reference write_barrier_kind:EmitWithNullCheck |
| /// CHECK: MonitorOperation kind:enter |
| /// CHECK: InstanceFieldSet field_name:Main.inner3 field_type:Reference write_barrier_kind:EmitWithNullCheck |
| |
| /// CHECK-START: Main Main.$noinline$testInstanceFieldSetsBlocked(Main, java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after) |
| /// CHECK: ; card_table |
| /// CHECK: ; card_table |
| /// CHECK: ; card_table |
| /// CHECK-NOT: ; card_table |
| private static Main $noinline$testInstanceFieldSetsBlocked( |
| Main m, Object o, Object o2, Object o3) { |
| m.inner = o; |
| $noinline$emptyMethod(); |
| m.inner2 = o2; |
| synchronized (m) { |
| m.inner3 = o3; |
| } |
| return m; |
| } |
| |
| /// CHECK-START: void Main.$noinline$testStaticFieldSetsBlocked(java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after) |
| /// CHECK: StaticFieldSet field_name:Main.inner_static field_type:Reference write_barrier_kind:EmitWithNullCheck |
| /// CHECK: InvokeStaticOrDirect method_name:Main.$noinline$emptyMethod |
| /// CHECK: StaticFieldSet field_name:Main.inner_static2 field_type:Reference write_barrier_kind:EmitWithNullCheck |
| /// CHECK: MonitorOperation kind:enter |
| /// CHECK: StaticFieldSet field_name:Main.inner_static3 field_type:Reference write_barrier_kind:EmitWithNullCheck |
| |
| /// CHECK-START: void Main.$noinline$testStaticFieldSetsBlocked(java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after) |
| /// CHECK: ; card_table |
| /// CHECK: ; card_table |
| /// CHECK: ; card_table |
| /// CHECK-NOT: ; card_table |
| private static void $noinline$testStaticFieldSetsBlocked(Object o, Object o2, Object o3) { |
| inner_static = o; |
| $noinline$emptyMethod(); |
| inner_static2 = o2; |
| Main m = new Main(); |
| synchronized (m) { |
| inner_static3 = o3; |
| } |
| } |
| |
| /// CHECK-START: java.lang.Object[] Main.$noinline$testArraySetsSameRTIBlocked() disassembly (after) |
| /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck |
| /// CHECK: InvokeStaticOrDirect method_name:Main.$noinline$emptyMethod |
| /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck |
| /// CHECK: MonitorOperation kind:enter |
| /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck |
| |
| /// CHECK-START: java.lang.Object[] Main.$noinline$testArraySetsSameRTIBlocked() disassembly (after) |
| /// CHECK: ; card_table |
| /// CHECK: ; card_table |
| /// CHECK: ; card_table |
| /// CHECK-NOT: ; card_table |
| private static java.lang.Object[] $noinline$testArraySetsSameRTIBlocked() { |
| Object[] arr = new Object[3]; |
| arr[0] = inner_static; |
| $noinline$emptyMethod(); |
| arr[1] = inner_static2; |
| Main m = new Main(); |
| synchronized (m) { |
| arr[2] = inner_static3; |
| } |
| return arr; |
| } |
| |
| Object inner; |
| Object inner2; |
| Object inner3; |
| |
| MultipleObject mo; |
| MultipleObject mo2; |
| |
| static Object inner_static; |
| static Object inner_static2; |
| static Object inner_static3; |
| } |