Skip to content

Allow specifying hardware support level for crossgen2 #226

@cshung

Description

@cshung

Goals

To make ahead of time compiled binary widely applicable, it cannot make assumptions on the execution environment. For example, it cannot assume the processor used to run the program support AVX instructions.

This is unfortunate because ahead of time compilation is meant for performance, but it cannot be as performant as it could be, just because of the lack of information.

The change proposed in this issue is to remedy it - the problem is the lack of information - so we ask the user to supply it.

The abstract, high-level functional requirement:

  • Users specify the hardware support level during publishing.
  • Crossgen2 will make the assumption that the underlying hardware will have the support, and it generates code as such.
  • Runtime, upon load of the assembly, will verify that the current hardware does support the assumed level. Ready to run code will be used only when the validation succeeded.

The key challenges:

  • What exactly do we mean by the hardware support level?
    This is tricky, different processors support different subsets of instructions. These subsets are not totally-ordered. The only way to specify the exact hardware support is to specify the subset.

  • What if the hardware is more capable than the assumed level?
    Ideally, the hardware support is exactly as assumed. If it is less capable, we can bail out and refuse to load the assembly. But it is more capable, then refusing to load the assembly seems harsh. If we do use the ready-to-run code, there could be problems as follow:

  1. Disagreeing on support level:
    Suppose we jit this, and ready-to-run compile ready_to_run_use_x assuming x is not supported. ready_to_run_use_x would throw PlatformNotSupportedException at runtime, not what we wanted.
void JittedCode()
{
  if (x is supported)
  {
    ready_to_run_use_x();
  }
}
  1. Disagreeing on Vector<T> size:
    Suppose we run this:
void JittedCode()
{
   Vector<T> x;
   ready_to_run_with_x(x);
}

If they do not agree on Vector<T> sizes, the call will not work.

  1. Disagreeing on calling convention:
    This is a general problem - if we change calling convention - then the call will not work. In general, calling convention is something we should just never change, but it appears to me that we will - due to this:
    https://github.com/dotnet/coreclr/issues/15943

AVX is likely to be much less useful if we cannot use Vector<T>.

Design

The solution to (1) is TBD, it will be a verbose and extensible format that describes the instruction subset.
The solution to (2) is that we specify a fixed Vector<T> size and also enforce the same size at runtime when JIT asks for it.
The solution to (3) is TBD - ideally, it is fixed so we can use Vector<T> in crossgen2.

If we zoom out a little bit - we notice the general problem is disagreement. The past approach for ready-to-run is to solve the disagreement by shutting up (i.e. not compiling). Here I am proposing something different, I am saying we should solve the disagreement by letting the JIT follows the assumption (i.e Vector<T> size).

Currently, Vector<T> size is the only thing I want to enforce the JIT to follow. In particular, suppose we implemented AVX512, I don't want to stop the JIT from using it, meaning if there is any code that used AVX512, crossgen2 will refuse to compile it, just like it was.

Audience

The key customer of this feature are:

  • People who cared about performance, and
  • People who have control over where their code runs.

This is likely to be rare in terms of the number of people. But if we could make it on the cloud, that could potentially benefit many people automatically.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions