Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JIT: Support object stack allocation #20253

Open
erozenfeld opened this issue Oct 3, 2018 · 13 comments

Comments

@erozenfeld
Copy link
Member

commented Oct 3, 2018

This issue will track work on supporting object stack allocation in the jit. See #20251 for the document describing the work.

The initial goal is to be able to remove heap allocation in a simple example like this:

class Foo
{
    public int f1;
    public int f2;
    public Foo(int f1, int f2)
    {
        this.f1 = f1;
        this.f2 = f2;
    }
}

class Test
{
    static int f1;
    static int f2;
    public static int Main()
    {
         Foo foo = new Foo(f1, f2);
         return foo.f1 + foo.f2;
    }
}

and then in a similar example where the class has gc fields.

Proposed initial steps are:

  • add getHeapClassSize jit interface method and its implementations #20283
  • add canAllocateOnStack jit interface method and its implementations #20283
  • modify getClassGCLayout jit interface method to work on reference types #20814
  • add COMPlus_JitObjectStackAllocation environment variable to control this optimization (off by default) #20814
  • move ObjectAllocator phase to be closer to inlining #20377
  • modify lvaSetStruct to allow creating locals corresponding to stack-allocated classes #20814
  • update types of references in methods with stack allocated objects to TYP_REF (when always pointing to the heap) or TYP_I_IMPL (when always pointing to the stack) or TYP_BYREF (when pointing to the heap or to the stack) #21950
  • modify gc reporting to properly report gc fields of stack-allocated objects
  • modify gc writebarrier logic to apply appropriate barriers when assigning to fields of (possibly) stack-allocated objects #21950
  • add simple conservative escape analysis sufficient for the example above #20814
  • make the analysis more sophisticated to handle increasingly more complex examples
  • special case calls to helpers where arguments don't escape (this will require ensuring that the helpers report gc arguments as interior to gc)
  • enable promotion of fields of stack-allocated objects
  • enable the optimization for x86 #21950
  • enable stack allocation of boxed structs
  • enable stack allocation of constant-sized arrays
  • enable stack allocation of strings
  • enable object stack allocation in R2R mode #21533
  • make sure object stack allocation doesn't block fast tail call optimization unnecessarily (currently fast tail call optimization is disabled if there are any locals with lvAddrExposed set)

I will be modifying and extending this list as the work progresses.

cc @dotnet/jit-contrib

@erozenfeld erozenfeld self-assigned this Oct 3, 2018

@jkotas

This comment has been minimized.

Copy link
Member

commented Oct 4, 2018

add getObjHeaderSize jit interface method and its implementations

Why is this needed?

@erozenfeld

This comment has been minimized.

Copy link
Member Author

commented Oct 4, 2018

We'll have to allocate space for ObjHeader before the stack-allocated object unless we can prove that it won't be needed for synchronization or hash code storage.

@jkotas

This comment has been minimized.

Copy link
Member

commented Oct 4, 2018

needed for synchronization or hash code storage

I think using sychronization, etc. should prohibit stack allocation, for the initial iteration at least. Otherwise, you would also need a helper call to clear these objects from the synchronization tables and teach the synchronization tables to allow references to objects that do not live on the GC heap.

@mikedn

This comment has been minimized.

Copy link
Contributor

commented Oct 4, 2018

modify lvaSetStruct to allow creating locals corresponding to stack-allocated classes
modify gc writebarrier logic to apply appropriate barriers when assigning to fields of (possibly) stack-allocated objects

How would stack allocated object fields would be accessed? Would existing FIELD/IND nodes be replaced with LCL_FLD/LCL_VAR nodes? Are writer barriers still needed if the object is allocated on stack?

@Suchiman

This comment has been minimized.

Copy link
Collaborator

commented Oct 4, 2018

What would be the point of trying to synchronize on a stackallocated object? If it's considered for stack allocation then it means it doesn't escape which means no other thread can attempt to synchronize on it so any attempts to do so should be a no-op. The hash code could be derived from it's stack address as the object won't move until its deallocated.

@erozenfeld

This comment has been minimized.

Copy link
Member Author

commented Oct 4, 2018

We'll need to detect calls to RuntimeHelpers.GetHashCode on the stackallocated object. They will be problematic if we don't allocate ObjHeader before the object.

@erozenfeld

This comment has been minimized.

Copy link
Member Author

commented Oct 4, 2018

modify lvaSetStruct to allow creating locals corresponding to stack-allocated classes
modify gc writebarrier logic to apply appropriate barriers when assigning to fields of (possibly) stack-allocated objects

How would stack allocated object fields would be accessed? Would existing FIELD/IND nodes be replaced with LCL_FLD/LCL_VAR nodes? Are writer barriers still needed if the object is allocated on stack?

Stack allocated object will be treated like a struct and its fields may be promoted. No write barriers are needed if we are assigning to a field of a stack-allocated object.

@erozenfeld

This comment has been minimized.

Copy link
Member Author

commented Oct 4, 2018

We will have cases where we are assigning to a field of an object that may live on the stack or on the heap (e.g., at joins or when passing a stack allocated object to another method). We'll have to detect these cases and use checked write barriers.

@erozenfeld

This comment has been minimized.

Copy link
Member Author

commented Oct 4, 2018

I suppose calls to RuntimeHelpers.GetHashCode will look like calls to native code so we will consider the object escaping and won't stack-allocate, so we don't need ObjHeader for that.

@erozenfeld

This comment has been minimized.

Copy link
Member Author

commented Nov 21, 2018

#20814 implemented an initial version of object stack allocation. I updated the items in the description of this issue to mark what's done and added more items to the road map.

@erozenfeld

This comment has been minimized.

Copy link
Member Author

commented Jan 15, 2019

#21950 added several improvements:
objects with gc fields can be stack allocated;
optimization is enabled on x86;
gc pointer reporting is less conservative when the method has stack-allocated objects.

@benaadams

This comment has been minimized.

Copy link
Collaborator

commented Feb 27, 2019

What would be the point of trying to synchronize on a stackallocated object? If it's considered for stack allocation then it means it doesn't escape which means no other thread can attempt to synchronize on it so any attempts to do so should be a no-op.

Call it a "lock elision" optimization and highlight it as a feature 😉 I think that's what Java does, unless I misunderstand what they do.

Equally could drop interlocked in objects that don't escape (where they operate on the object)?

@mikedn

This comment has been minimized.

Copy link
Contributor

commented Feb 27, 2019

Call it a "lock elision" optimization and highlight it as a feature 😉 I think that's what Java does, unless I misunderstand what they do.

AFAIR the first Java collection classes were synchronized. Of course, they found out that this isn't such a great idea and their implementation of escape analysis was also trying to help by removing synchronization when the collection object wasn't escaping the stack.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants
You can’t perform that action at this time.