Conversation
asmcomp/selectgen.ml
Outdated
| Some(self#insert_op (Iconst_float (Int64.bits_of_float n)) [||] r) | ||
| | Cconst_symbol n -> | ||
| let r = self#regs_for typ_val in | ||
| let r = self#regs_for typ_int in |
There was a problem hiding this comment.
The reason why this works is quite intricate, this certainly need some comment to explain it (if you don't end up adding a new register type to represent that).
There was a problem hiding this comment.
FYI: I had a crash with 4.08 and no-naked-pointers that we narrowed down to another instance of this bug with the help of @mshinwell
|
By the way, this also suggest that there is some problem on the ordering of instructions: It is probably better in the situation you encountered to delay loading rather than spilling. |
|
Yes, I did wonder about that too. I can investigate that aspect further. |
|
The lub of a register of type "static data that cannot be scanned" and int should be "static data that cannot be scanned" I think, and not an error. But otherwise, this seems like a sensible addition to the register type lattice. I can imagine one situation where joining might occur, but not with the current compiler: If we add some cmm level unboxing of allocated blocs, (with some gadts to allow typing that, but lets assume we did that correctly): if it gets unboxed in some way, we might end up with the variable |
|
This makes me think that spending more time trying to optimise GC headers before function bodies may in fact be worthwhile. |
|
Optimise ? |
|
I believe the proposed change (Cconst_symbol of type Tint) is sound. Still, it is weird that a pointer to the code of a function ends up spilled. |
|
I decided to try to improve the register typing so that we can solve this problem in a more obviously correct manner. The results are now in this pull request. In the new world, there are two parts to a register type, as seen in the diff for
The second part is called the "GC action" and has the following possibilities.
At join points (and assignments) an operation is used, as at present, that takes the least upper bound of these actions. Here is the ordering: For example, a conditional with one arm returning "must be scanned" and another arm returning "can be scanned" will cause the result register to be eligible for root registration. A fatal error will be produced at such points if an attempt is made to join "must be scanned" with "cannot be scanned". The careful distinctions here enable the compiler code to be more explicit, easier to understand and robust. As an example, bigarray indexing calculations currently produce register type
As far as point 2 goes, this patch adds an assertion that This patch reduces the number of Cmm language constructs by coalescing the various "add" operations into a single operation equipped with its result type. A similar simplification has been made to memory chunks; and it is now explicit that any sub-word memory chunk is of type "cannot be scanned". I also took the liberty of renaming The patch annotates The patch clarifies that the Various other potentially dubious pieces of code have been fixed as a result of these changes:
I need to make another pass over |
| | Caddi | Csubi | Cmuli | Cmulhi | Cdivi | Cmodi | ||
| | Cadd of gc_action (* the [gc_action] gives the type of the result *) | ||
| | Csubi | Cmuli | Cmulhi | Cdivi | Cmodi | ||
| | Cand | Cor | Cxor | Clsl | Clsr | Casr |
There was a problem hiding this comment.
We've just realised that more (all?) of the arithmetic operations will need altering so that they specify the type of the thing (e.g. tagged integer, naked int32, etc...) that they are working on.
I came across a case today, when testing with "no naked pointers" enabled, where code pointers had been spilled onto the stack (prior to being used for building closures) and registered as GC roots. This is not permissible under the current no naked pointers rules.
I think it is probably unnecessary to register symbols as GC roots within functions in any case. The addresses of symbols are invariant under GC. As far as I know the only symbols whose contents need to be scanned are the Flambda
Initialize_symbolones---and those are all referenced from the global GC roots tables anyway.I suspect we can fix these problems by assigning
Cconst_symbolexpressions the "int" instead of the "val" register type. Does anyone know of any reason why this might not be safe?What happens during instruction selection needs careful reasoning about. If there are two branches and a join point then the code now takes the least upper bound of the register types from the branches. This seems necessary for correctness of this change. It may mean that a symbol gets registered as a GC root when not strictly required (for example if one branch of a conditional dynamically constructs a pair and the other branch returns a statically-allocated pair). Could it lead to a situation where a register is registered as a root and doesn't contain a valid value? I'm not completely certain but the only case for such invalid values might be that of code pointers. In that case I don't see how the situation could arise.
We could maybe enhance the register typing to at least enable us to catch these cases during compilation. For example we could have "static data that cannot be scanned" which would result in an error if it occurs at a join point with any other register type. We could add "static data that can be scanned" for completeness, perhaps.