-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
Document the behavior of the CLR when unsafe casts are used #6176
Comments
These unsafe behaviors are not guaranteed to be the same accross runtimes. My description applies to CLR/CoreCLR. GC does not care about the exact types, e.g. if type of local object reference variable is not compatible with what is actually stored in it, the GC will still track it fine. The lowest level constructs (load/store fields, call non-virtual non-generic method) work ok too - of course, you have to know what you are doing. Most of the higher level constructs (virtual methods, etc.) do depend on exact types and can get confused:
|
@jkotas Could you please confirm that the following example is "safe" in terms of GC stability, i.e. unsafe casting of an array In the code below,
|
It will work in CoreCLR today, but unsafe casting of KeyValuePair[] to byte[] is a bit questionable. There is no guarantee that KeyValuePair[] and byte[] will have same in-memory layout. Some runtimes may insert different amount of padding before the start of the first element based on the array element type. A bit more robust way would be to do the unsafe cast on the address of the first array element. It requires current preview version of C# compiler (https://github.com/dotnet/roslyn/) and the unsafe package (https://github.com/dotnet/corefx/tree/master/src/System.Runtime.CompilerServices.Unsafe).
|
There should be more formal documentation that can be used to clearly decide whether any given piece of code is safe or not. @jkotas your comments are helpful but not sufficient to decide on safety. For example, could the JIT optimize the following assuming that
If There needs to be more formal and complete documentation on what effects unsafe operations can cause. |
I do not believe that he JIT makes assumptions about no-aliasing today. There are number of variants of it, e.g.:
I agree it would be nice to have more precise specification for valid unveriable code - that includes unsafe casts. There is some specification for it in ECMA-335, but it is not as precise as the specification for verifiable code. Ideally it should be added to ECMA-335 spec. cc @dotnet/jit-contrib |
Actually, if I compile this code with the current JIT: class A { public int x; }
class B { public int y; }
...
static int DoIt(A a, B b) {
int x = a.x;
b.y = 2;
return x + a.x;
} then the codegen for
It looks like the assumption the JIT makes to acheive this is that different class fields (i.e. with different field handles) will not alias each other so long as As a more general rule, I'm used to trusting that fields/locals/args/elements whose type is a reference type point to either null or an instance of the class type indicated, but trusting basically nothing about byrefs or unmanaged pointers. But that's my mental model and is informed by other projects, I'm not sure to what extent the current JIT code base actually makes use of such an assumption (aside from the case mentioned above). |
Looks like even BCL-internal code is treading on glass using these unsafe primitives. And it would be a shame to lose optimizations based on aliasing. Also, even if a given optimization is not performed right now we probably don't want to commit ourselves to never perform it for compat reasons. Much stronger optimizers (e.g. LLVM/LLILC) are on the horizon. |
I think it would require some serious analysis of JIT behavior and the current specification, plus some language-lawyering, to come up with the right specification for how the JIT should behave with regard to these types of unsafe casts. However, I think that it is imperative that we retain the following invariants, from the JIT's perspective:
A short list, not at all exhaustive list of implications for developers when using unsafe casts:
|
@CarolEldt So I assume that unsafe downcasts of discriminated union types (after having already verified that the cast is safe) are okay? |
@DemiMarie - Yes, at least from the JIT's perspective. Any value types that have overlapping fields are treated conservatively by the JIT. |
@CarolEidt would it make sense to close this now? |
@danmosemsft - I'm not sure what is the correct way to handle this kind of issue. Ideally, it would be reassigned as a spec issue, and handled there, but we do not currently have an active standard working group, nor a mechanism for filing and tracking these kinds of issues. But, yes, I think this issue should be closed. I've made a note of it, for consideration in future spec work. |
In the
System.Runtime.CompilerServices.Unsafe
package there is a method that allows unsafe casts from anything to anything:This means that
string s = F(); s.GetType();
can beSomeClass
orstring s = F(); (string)s
can throw. In fact it might not throw because the C# compiler or the JIT might optimize that cast out.Please document the usage rules for unsafe casts in general and document what exact "unsafe" operations are safe to perform on the CLR.
Note, that
As<T>
can be implemented right now using an unsafe union type e.g.For that reason the question is relevant right now, even without the
System.Runtime.CompilerServices.Unsafe
package.It should be documented as well what the CLR thinks about such unsafe structs. In particular I wonder what the optimizing JIT and the GC do with object references that point to an unexpected type.
The text was updated successfully, but these errors were encountered: