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

[OCaml][NPM] Add OCaml bindings to new pass manager #66421

Merged
merged 1 commit into from
Sep 16, 2023

Conversation

alan-j-hu
Copy link
Contributor

I'm still torn on how to translate the LLVM API's C error handling to OCaml. Should I use the result type or exceptions?

@alan-j-hu
Copy link
Contributor Author

If I do use the result type, I want to share a single allocation of Ok () across all calls. What is the best way for me to do this?

LLVMRunPasses(Module_val(M), String_val(Passes), TargetMachine_val(TM),
PassBuilderOptions_val(Options));
if (Err == LLVMErrorSuccess) {
value result = caml_alloc(1, 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

Regarding the comment about sharing a single allocation, do you mean this call to caml_alloc? If so, do not worry about it, this is vanishingly cheap (usually just a pointer increment and test) compared to what LLVMRunPasses does.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this is what I meant by sharing the allocation.

Copy link
Contributor

@jberdine jberdine left a comment

Choose a reason for hiding this comment

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

This LGTM. I made a few minor comments, but nothing that needs another round of review.

Regarding the question of how to expose the LLVM error reporting mechanics, there are 3 reasonable options I know of:

  1. use result just as you have
  2. use option where None means "no error" and Some msg means an error described by msg occurred
  3. use exceptions

If you want, you could post on the ocaml discuss with the function signature and a brief description of what underlying functionality is being exposed. I would not be surprised to see different people offer support for all of the options. Overall though, there is a move in the ocaml community away from using exceptions toward either option or result, so I expect choice 3 to be the least supported. Choosing between 1 and 2 is harder. If LLVMRunPasses was very light and expected to be fast, using option 2 would avoid an allocation in the common case, which might be worth it. But in this case that won't matter, and it is probably (my opinion) better to have the no-error case be reported as Ok and the error case be reported as Error rather than None and Some error which is opposite to usual usage where None represents an unspecified error.

Comment on lines 11 to 14
(** [run_passes m passes tm opts] runs a set of passes over a module. The
format of the string [passes] is the same as opt's -passes argument for
the new pass manager. Individual passes may be specified, separated by
commas. Full pipelines may also be invoked. *)
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be helpful to add a crossref to LLVMRunPasses, or whatever the corresponding C++ function in the doxygen is?

I think it would be good to explicitly state what the two cases of the result mean in LLVM terms, in particular to state that the returned string is obtained from LLVMGetErrorMessage, if only for users who are familiar with LLVM-C.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the definition of LLVMRunPasses, which seems to exist for the C API:

LLVMErrorRef LLVMRunPasses(LLVMModuleRef M, const char *Passes,

This seems to be the corresponding C++ function: https://llvm.org/doxygen/classllvm_1_1PassManager.html#aef5d9142acafceffd14c76b8ddd0fd4e where ModulePassManager is an alias for PassManager<Module>: https://llvm.org/doxygen/namespacellvm.html#a083c8f57e0e3a1011bf2ea8673700558

Should I just put down the C function LLVMRunPasses? Or should I put down ModulePassManager::run? What would be easiest for readers to understand and look up? I think that the C API function has enough additional functionality that the wrapped C++ function doesn't have that I should refer to the C function if I were to put a crossref. What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, agree that referring to LLVMRunPasses makes sense.

-> llpassbuilder_options
-> (unit, string) result

(** Creates a new set of options for a PassBuilder. *)
Copy link
Contributor

Choose a reason for hiding this comment

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

crossref?

(** Creates a new set of options for a PassBuilder. *)
val create_passbuilder_options : unit -> llpassbuilder_options

(** Toggles adding the VerifierPass for the PassBuilder. *)
Copy link
Contributor

Choose a reason for hiding this comment

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

crossref?

val passbuilder_options_set_verify_each
: llpassbuilder_options -> bool -> unit

(** Toggles debug logging. *)
Copy link
Contributor

Choose a reason for hiding this comment

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

crossref?

: llpassbuilder_options -> int -> unit

(** Tuning option to disable promotion to scalars in LICM with MemorySSA, if
the number of access is too large. See
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
the number of access is too large. See
the number of accesses is too large. See

val passbuilder_options_set_inliner_threshold
: llpassbuilder_options -> int -> unit

(** Disposes of the options. *)
Copy link
Contributor

Choose a reason for hiding this comment

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

crossref?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This has no crossref to a C++ function. The C API wrapper function merely calls delete.


let context = Llvm.global_context ()

let m = Llvm.create_module context "mymodule"
Copy link
Contributor

Choose a reason for hiding this comment

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

It would perhaps be a more effective test if this loaded something like llvm/test/Other/new-pass-manager.ll rather than just using an empty module.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I couldn't figure out how to manipulate the OCaml test infrastructure to read llvm/test/Other/new-pass-manager.ll , so I recreated it via the OCaml API. After all, the actual LLVM IR in that file is just a small function, and the rest of it is test scripts irrelevant to testing the OCaml bindings.

@alan-j-hu alan-j-hu merged commit 49610ed into llvm:main Sep 16, 2023
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants