diff options
author | Eugene Rozenfeld <erozen@microsoft.com> | 2019-01-04 21:27:20 -0800 |
---|---|---|
committer | Eugene Rozenfeld <erozen@microsoft.com> | 2019-01-14 17:33:21 -0800 |
commit | f2d3dfeda5140ea003f0dae166cc62f0fb1d31d8 (patch) | |
tree | 6c954bcc7cc967504aadec0fe92876b117fa0404 /tests/src/JIT | |
parent | 9cdde2f3f410673e42b541fe86e6a3f9acc86272 (diff) | |
download | coreclr-f2d3dfeda5140ea003f0dae166cc62f0fb1d31d8.tar.gz coreclr-f2d3dfeda5140ea003f0dae166cc62f0fb1d31d8.tar.bz2 coreclr-f2d3dfeda5140ea003f0dae166cc62f0fb1d31d8.zip |
Improvements for object stack allocation.
This change enables object stack allocation for more cases.
1. Objects with gc fields can now be stack-allocated.
2. Object stack allocation is enabled for x86.
ObjectAllocator updates the types of trees containing references
to possibly-stack-allocated objects to TYP_BYREF or TYP_I_IMPL as appropriate.
That allows us to remove the hacks in gcencode.cpp and refine reporting of pointers:
the pointer is not reported when we can prove that it always points to a stack-allocated object or is null (typed as TYP_I_IMPL);
the pointer is reported as an interior pointer when it may point to either a stack-allocated object or a heap-allocated object (typed as TYP_BYREF);
the pointer is reported as a normal pointer when it points to a heap-allocated object (typed as TYP_REF).
ObjectAllocator also adds flags to indirections:
GTF_IND_TGTANYWHERE when the indirection may be the heap or the stack
(that results in checked write barriers used for writes)
or the new GTF_IND_TGT_NOT_HEAP when the indirection is null or stack memory
(that results in no barrier used for writes).
Diffstat (limited to 'tests/src/JIT')
-rw-r--r-- | tests/src/JIT/opt/ObjectStackAllocation/ObjectStackAllocationTests.cs | 92 |
1 files changed, 80 insertions, 12 deletions
diff --git a/tests/src/JIT/opt/ObjectStackAllocation/ObjectStackAllocationTests.cs b/tests/src/JIT/opt/ObjectStackAllocation/ObjectStackAllocationTests.cs index 9e66524467..7c524ba033 100644 --- a/tests/src/JIT/opt/ObjectStackAllocation/ObjectStackAllocationTests.cs +++ b/tests/src/JIT/opt/ObjectStackAllocation/ObjectStackAllocationTests.cs @@ -33,7 +33,7 @@ namespace ObjectStackAllocation } } - class SimpleClassWithGCField : SimpleClassA + sealed class SimpleClassWithGCField : SimpleClassA { public object o; @@ -80,6 +80,13 @@ namespace ObjectStackAllocation { static volatile int f1 = 5; static volatile int f2 = 7; + static SimpleClassA classA; + static SimpleClassWithGCField classWithGCField; + static string str0; + static string str1; + static string str2; + static string str3; + static string str4; delegate int Test(); @@ -89,12 +96,24 @@ namespace ObjectStackAllocation { AllocationKind expectedAllocationKind = AllocationKind.Stack; if (GCStressEnabled()) { + Console.WriteLine("GCStress is enabled"); expectedAllocationKind = AllocationKind.Undefined; } else if (!SPCOptimizationsEnabled()) { + Console.WriteLine("System.Private.CoreLib.dll optimizations are disabled"); expectedAllocationKind = AllocationKind.Heap; } + classA = new SimpleClassA(f1, f2); + + classWithGCField = new SimpleClassWithGCField(f1, f2, null); + + str0 = "str_zero"; + str1 = "str_one"; + str2 = "str_two"; + str3 = "str_three"; + str4 = "str_four"; + CallTestAndVerifyAllocation(AllocateSimpleClassAndAddFields, 12, expectedAllocationKind); CallTestAndVerifyAllocation(AllocateSimpleClassesAndEQCompareThem, 0, expectedAllocationKind); @@ -107,23 +126,25 @@ namespace ObjectStackAllocation CallTestAndVerifyAllocation(AllocateClassWithNestedStructAndAddFields, 24, expectedAllocationKind); + CallTestAndVerifyAllocation(AllocateSimpleClassAndCheckTypeNoHelper, 1, expectedAllocationKind); + + CallTestAndVerifyAllocation(AllocateSimpleClassWithGCFieldAndAddFields, 12, expectedAllocationKind); + + CallTestAndVerifyAllocation(AllocateSimpleClassAndAssignRefToAField, 12, expectedAllocationKind); + + CallTestAndVerifyAllocation(TestMixOfReportingAndWriteBarriers, 34, expectedAllocationKind); + // The remaining tests currently never allocate on the stack if (expectedAllocationKind == AllocationKind.Stack) { expectedAllocationKind = AllocationKind.Heap; } // This test calls CORINFO_HELP_ISINSTANCEOFCLASS - CallTestAndVerifyAllocation(AllocateSimpleClassAndCheckType, 1, expectedAllocationKind); + CallTestAndVerifyAllocation(AllocateSimpleClassAndCheckTypeHelper, 1, expectedAllocationKind); // This test calls CORINFO_HELP_CHKCASTCLASS_SPECIAL CallTestAndVerifyAllocation(AllocateSimpleClassAndCast, 7, expectedAllocationKind); - // Stack allocation of classes with GC fields is currently disabled - CallTestAndVerifyAllocation(AllocateSimpleClassWithGCFieldAndAddFields, 12, expectedAllocationKind); - - // Assigning class ref to a field of another object currently always disables stack allocation - CallTestAndVerifyAllocation(AllocateSimpleClassAndAssignRefToAField, 12, expectedAllocationKind); - // Stack allocation of boxed structs is currently disabled CallTestAndVerifyAllocation(BoxSimpleStructAndAddFields, 12, expectedAllocationKind); @@ -183,6 +204,7 @@ namespace ObjectStackAllocation { SimpleClassA a1 = new SimpleClassA(f1, f2); SimpleClassA a2 = (f1 == 0) ? a1 : new SimpleClassA(f2, f1); + GC.Collect(); return (a1 == a2) ? 1 : 0; } @@ -191,20 +213,31 @@ namespace ObjectStackAllocation { SimpleClassA a1 = new SimpleClassA(f1, f2); SimpleClassA a2 = (f1 == 0) ? a1 : new SimpleClassA(f2, f1); + GC.Collect(); return (a1 != a2) ? 1 : 0; } [MethodImpl(MethodImplOptions.NoInlining)] - static int AllocateSimpleClassAndCheckType() + static int AllocateSimpleClassAndCheckTypeNoHelper() + { + object o = (f1 == 0) ? (object)new SimpleClassB(f1, f2) : (object)new SimpleClassA(f1, f2); + GC.Collect(); + return (o is SimpleClassB) ? 0 : 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AllocateSimpleClassAndCheckTypeHelper() { object o = (f1 == 0) ? (object)new SimpleClassB(f1, f2) : (object)new SimpleClassA(f1, f2); - return (o is SimpleClassB) || !(o is SimpleClassA) ? 0 : 1; + GC.Collect(); + return !(o is SimpleClassA) ? 0 : 1; } [MethodImpl(MethodImplOptions.NoInlining)] static int AllocateSimpleClassAndCast() { object o = (f1 == 0) ? (object)new SimpleClassB(f1, f2) : (object)new SimpleClassA(f2, f1); + GC.Collect(); return ((SimpleClassA)o).f1; } @@ -212,6 +245,7 @@ namespace ObjectStackAllocation static int AllocateSimpleClassAndGetField() { SimpleClassA a = new SimpleClassA(f1, f2); + GC.Collect(); ref int f = ref a.f2; return f; } @@ -220,6 +254,7 @@ namespace ObjectStackAllocation static int AllocateClassWithNestedStructAndGetField() { ClassWithNestedStruct c = new ClassWithNestedStruct(f1, f2); + GC.Collect(); ref int f = ref c.ns.s.f1; return f; } @@ -228,6 +263,7 @@ namespace ObjectStackAllocation static int AllocateClassWithNestedStructAndAddFields() { ClassWithNestedStruct c = new ClassWithNestedStruct(f1, f2); + GC.Collect(); return c.ns.f1 + c.ns.f2 + c.ns.s.f1 + c.ns.s.f2; } @@ -235,14 +271,15 @@ namespace ObjectStackAllocation static int AllocateSimpleClassWithGCFieldAndAddFields() { SimpleClassWithGCField c = new SimpleClassWithGCField(f1, f2, null); + GC.Collect(); return c.f1 + c.f2; } static int AllocateSimpleClassAndAssignRefToAField() { SimpleClassWithGCField c = new SimpleClassWithGCField(f1, f2, null); - SimpleClassA a = new SimpleClassA(f1, f2); - c.o = a; + GC.Collect(); + c.o = classA; return c.f1 + c.f2; } @@ -253,7 +290,38 @@ namespace ObjectStackAllocation str.f1 = f1; str.f2 = f2; object boxedSimpleStruct = (object)str; + GC.Collect(); return ((SimpleStruct)boxedSimpleStruct).f1 + ((SimpleStruct)boxedSimpleStruct).f2; } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int TestMixOfReportingAndWriteBarriers() + { + // c1 doesn't escape and is allocated on the stack + SimpleClassWithGCField c1 = new SimpleClassWithGCField(f1, f2, str0); + + // c2 always points to a heap-allocated object + SimpleClassWithGCField c2 = classWithGCField; + + // c2 and c3 may point to a heap-allocated object or to a stack-allocated object + SimpleClassWithGCField c3 = (f1 == 0) ? c1 : c2; + SimpleClassWithGCField c4 = (f2 == 0) ? c2 : c1; + + // c1 doesn't have to be reported to GC (but can be conservatively reported as an interior pointer) + // c1.o should be reported to GC as a normal pointer (but can be conservatively reported as an interior pointer) + // c2 should be reported to GC as a normal pointer (but can be conservatively reported as an interior pointer) + // c3 and c4 must be reported as interior pointers + GC.Collect(); + + // This assignment doesn't need a write barrier but may conservatively use a checked barrier + c1.o = str1; + // This assignment should optimally use a normal write barrier but may conservatively use a checked barrier + c2.o = str2; + // These assignments require a checked write barrier + c3.o = str3; + c4.o = str4; + + return c1.o.ToString().Length + c2.o.ToString().Length + c3.o.ToString().Length + c4.o.ToString().Length; + } } } |