Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Register availability analysis#856

Merged
mshinwell merged 13 commits intoocaml:trunkfrommshinwell:reg_availability
Sep 15, 2017

Conversation

mshinwell
Copy link
Contributor

This pull request adds a forwards dataflow pass on Mach code to calculate which registers are available immediately prior to each instruction (that is to say, when the program counter is exactly at the start of that instruction. There is a subtlety to do with call instructions which will be explained in a later pull request---it isn't relevant for the correctness of this pass).

Another way of thinking of this is:

  • The liveness pass calculates the minimal ranges over which registers must be live;
  • The availability pass calculates the maximal ranges over which registers happen to be available.

The output of this pass is annotations on individual Mach instructions. These are collected together into contiguous ranges spanning multiple instructions by a subsequent pass, to be presented. These ranges correspond to statements such as "the value of the variableFoo.bar is to be found in registerrax between labelsL1234 andL5678". Ranges such as these may be transcribed into DWARF location lists that may be evaluated by the debugger at runtime against a specific value of the program counter. This then determines which variables are available to have their values inspected at that particular point in the program.

This analysis should be textbook, but is full of subtleties.

The availability analysis propagates naming information using a special operationIname_for_debugger which is analogous to the method used in CompCert (thanks to@xavierleroy for describing this, which helped to simplify the code from the previous version---we no longer have to modify the existingReg module). Changes toSelectgen, which are not presented here, insert these new operations when it becomes clear what the name to be associated to a particular register will be. The availability analysis then propagates that forwards. Care needs to be taken withCassign---if the value being assigned to is mutable and is associated to the variablefoo, say, then we need to forget about any other ascriptions of the namefoo to (other) registers (since the value offoo has now changed, and those registers might contain copies of its old value). Only in theCassign case will theis_assignment member insideIname_for_debugger betrue.

There is an assertion in this pass that checks the relation between liveness and availability, namely that any live register must also be available. This requires GPR#852 to work properly.

The type ofprovenance will be changed in due course. Those of you following along with the Flambda pull request#855 should be starting to see how this lines up with the provenance information there.

I will probably make some efficiency improvements and tidy-ups to this code, but it's pretty much in a final form now, and review should commence as it's tricky.@chambart has already looked at the core algorithm before (Pierre, your comments have been marked as XCR where they have been dealt with; please make a pull request against this one to delete them or re-open them as appropriate).

@mshinwell
Copy link
ContributorAuthor

This has been updated for current trunk.@chambart please review again.

@chambart
Copy link
Contributor

It looks like a few things could be simplified and clearer if you separated the notion of 'available before' and 'available across'. Of course in general available across wouldn't be observable, but for call and instructions implemented as multiple opcodes this can matter.

Also, it seems like there might be a problem with Ialloc: a register which was not clobbered might still be unavailable due to a collection moving the value. This seems to be handled correctly by functions, but there is the same problem here. If you want to represent values that are probably available, you should add another notion of 'available if not moved or collected'. Most of the time those values would still be available. I don't know if there is a good way to expose that in dwarf.

Otherwise it might not matter, but just for the record, there are two potential performance problems:

  • Finding all available register with in a given location can lead to quadratic situations the 'register with debug information set' type could contain a map of all the register currently living in a given location
  • The fixpoint on Icatch can follow the handlers in the wrong order. It could be mitigated by running availability for an handler only when the input changed.
    This might not be noticeable, but in case this pass is too slow, these might be good culpits.

I'm quite confident that except the Ialloc case everything else is fine.

in
let results =
assert (Array.length instr.arg = Array.length instr.res);
Array.mapi (fun index result_reg ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This is Array.map2

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Fixed

(RD.Set.of_array results))
| Iop op ->
if M.operation_can_raise op then begin
augment_availability_at_raise (Ok avail_before)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

If the instruction can destroy some register, this should be available across rather than available before.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This should be fixed

Printmach.instr ({ instr with M. next = M.end_instr (); })
end;
match instr.desc with
| Iop (Ialloc _) ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Across an allocation, only live and spilled values are guaranteed to be available.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This should be fixed now

(* Second: the case of OCaml to OCaml function calls. In this case,
registers always become unavailable unless:
(a) they are "live across" the call; and/or
(b) they hold immediates and are assigned to the stack. *)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This comment might need to explain why this is the case: the gc can move anything else.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Fixed

assert (nfail = nfail');
(* This [available_regs] call may cause [avail_at_exit] to be
updated. *)
available_regs handler ~avail_before:avail_at_top_of_handler
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

To avoid useless traversal (with a potential linear speedup if the handlers are in the wrong order), this could check if the argument changed. i.e. if avail_at_top_of_handler is equal to handler.available_before.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This check could be done for every instruction to avoid too much recomputing in case of nested fixpoints. But that would require an efficient equality test.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Hmm, except you need to get the "available_after" set somehow. I think for the moment I'm going to leave this, I have added a comment in the code referencing these suggestions.

Array.mapi (fun index result_reg ->
let arg_reg = instr.arg.(index) in
match RD.Set.find_reg_exn avail_before arg_reg with
| exception Not_found ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This case should never happen. It can be an assertion.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I've changed this to useArray.map2.

| Unreachable -> Format.fprintf ppf "<unreachable>"
| Ok availability ->
Format.fprintf ppf "{%a}"
(Format.pp_print_list (Reg_with_debug_info.print ~print_reg))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

You should add~pp_sep:(fun ppf () -> Format.fprintf ppf ",@ ")

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Fixed

@mshinwell
Copy link
ContributorAuthor

I think I've addressed everything (although deferring the performance concerns). I also fixed a small mistake: names should not be propagated fromraise points through to the parameters of exception handlers.

I have added two flags:-davail to dump register availability information when liveness information is requested; and-drunavail to actually cause this pass to be run. The latter requires-g to be in effect too. As discussed with@damiendoligez this will enable us to start exercising this pass on 4.06 compilers pending merging the remainder of the debugging-related patches.

Copy link
Contributor

@chambartchambart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Looks good for merge to me now. Thanks

@mshinwellmshinwell merged commitb650966 intoocaml:trunkSep 15, 2017
xavierleroy added a commit to xavierleroy/ocaml that referenced this pull requestApr 18, 2021
The availability analysis was introduced in 2017 as part of GPRocaml#856,as a first step towards generating DWARF debugging information.This project (generating DWARF debug info) was stopped, but the analysisis still here, even though its results are not used and it is not normally run(except when the experimental -drunavail flag is given).This is essentially dead code.  This PR removes it.
mshinwell pushed a commit that referenced this pull requestApr 23, 2021
The availability analysis was introduced in 2017 as part of GPR#856,as a first step towards generating DWARF debugging information.This project (generating DWARF debug info) was stopped, but the analysisis still here, even though its results are not used and it is not normally run(except when the experimental -drunavail flag is given).This is essentially dead code.  This PR removes it.
stedolan pushed a commit to stedolan/ocaml that referenced this pull requestOct 25, 2022
EmileTrotignon pushed a commit to EmileTrotignon/ocaml that referenced this pull requestJan 12, 2024
Co-authored-by: Cuihtlauac ALVARADO <cuihtmlauac@tarides.com>
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@chambartchambartchambart approved these changes

Assignees
No one assigned
Projects
None yet
Milestone
4.06.0
Development

Successfully merging this pull request may close these issues.

3 participants
@mshinwell@chambart@damiendoligez

[8]ページ先頭

©2009-2025 Movatter.jp