Chapel: Should the compiler generate multiple wrappers for export functions?

Created on 25 Jul 2020  路  10Comments  路  Source: chapel-lang/chapel

This issue asks a question related to #15917 (and #15944 #15901).

If I understand the current situation correctly, the compiler always generates 0 or 1 wrapper functions for an export proc. Some details of the wrapper function will depend on --library flag provided. However just because (say) a Python library is being created, it is not the case that C code will never call an export proc in the resulting application. Additionally, the export proc might be called from Chapel code.

For example, which intents are supported for a type are different currently for multilocale, Python, or single-locale C. See e.g. validateFormalIntent which checks fMultiLocaleInterop and fLibraryPython.

This issue proposes that the compiler create, in general, multiple wrappers for each export proc. There would be a different wrapper for each situation requiring different behavior (e.g. Python interop; multi-locale interop, single-locale C interop). In the generated code, these different wrappers will have different names.

In a case where intents used for a specific language are not supported, we could avoid generating the wrapper (or generate something causing the other language to generate an error if that function is used).

  • export proc called only from Chapel code - has normal Chapel semantics and full range of intents
  • export proc FnName to be called from C code - the compiler would always generate a wrapper. Perhaps we would always name the C wrapper with the FnName. But, I think it would be better if the compiler would generate, along with the .h file for the library, static inline functions that call the appropriate wrapper functions.
  • export proc FnName to be called from Python code - since the Python interop will generate Python level shims anyway, in the generated code we can name the wrapper e.g. FnName_python. (However if the Python interop can in some cases use the C wrappers, that is just fine as well).
  • export proc FnName to be called from multilocale C code - the compiler would generate a different wrapper, e.g. FnName_multilocale.

It's also possible to use this approach to enable a Chapel export proc accepting an array to operate on both chpl_external_array and chpl_opaque_array arguments. In particular, the compiler could generate a different export proc wrapper for each and give each of these a different name. (This does not help with mixing chpl_external_array and chpl_opaque_array within the same function but it does start to address a general problem we have now). In particular, default-rectangular arrays can be constructed from both chpl_external_array and chpl_opaque_array.

This approach would (I think) also help with export proc functions accepting a chpl_opaque_array using a primitive (at least they do in the test system) e.g.

https://github.com/chapel-lang/chapel/blob/aeb77df3db3ca8d224567208ee90d7d0040ac712/test/interop/C/exportArray/wrapArrayAsOpaque_global.chpl#L26-L32

This function could be written as

// recall that, above, in the file, we have
// const D = {1..5} dmapped Block({1..5});
export proc printBlock(x: [D] int) {
  // operate on Block array as normal
}

and then the compiler could generate the wrapper that converts a chpl_opaque_array into a Block array with the right type.

Compiler Language Design

Most helpful comment

Do we really envision a scenario where a Chapel library is compiled both for multilocale and for single locale execution in a single binary?

When I'm thinking about this case, I'm thinking about a multi-locale Chapel execution in which:

a) I might have a C program running on the front-end calling into the Chapel launcher, similar to what we support for Python today, where the launcher has to feed the request out to the compute nodes
b) a C routine on the compute node (as part of a library that was linked into Chapel, say), calling into the Chapel program on that same compute node.

So I think we'd potentially need to support calls into both the launcher and the compute binary, but I wouldn't call this single- vs. multi-locale interop in a single binary so much as login vs. compute interop within the same binary.

All 10 comments

Hi, I have a few questions:

  • It sounds like that with this proposal the effect of --library will be to generate wrappers for all supported interop languages at once?
  • For your array example, I think you are suggesting that as long as we're already going to generate multiple wrappers (e.g. one for each language or mode), we can also think about generating multiple wrappers for other cases (e.g. opaque/external arrays) as well?
  • I am not sure why a user would ever want a call to an exported function to resolve to the wrapper instead of the unwrapped Chapel function. Is this for extern blocks (I see some discussion about this in #15944)?

It sounds like that with this proposal the effect of --library will be to generate wrappers for all supported interop languages at once?

I'd expect it only to generate the wrappers for the interop languages selected (e.g. with --library-fortran etc). But the goal is to enable cases where multiple languages are selected without messing each other up.

For your array example, I think you are suggesting that as long as we're already going to generate multiple wrappers (e.g. one for each language or mode), we can also think about generating multiple wrappers for other cases (e.g. opaque/external arrays) as well?

Yes.

I am not sure why a user would ever want a call to an exported function to resolve to the wrapper instead of the unwrapped Chapel function. Is this for extern blocks (I see some discussion about this in #15944)?

It depends on where the call is comping from. If the call to say export proc f() is done from within Chapel code, then we would resolve to the original function (not a wrapper). But if the call is from C or Python or whatever then we would arrange for it to call the appropriate wrapper.

The discussion in #15944 is where I started to identify what seems to be a problem in my mind - that the current implementation seems to assume that an export proc will only ever be called from the current interop language selected (e.g. --library-python) when I don't think that's something we can rely on.

This proposal sounds intriguing-to-appealing to me. If it were problematic, I could imagine other approaches like requiring the invocation of the compiler to specify which languages the exports should support by default and/or decorations on the exports themselves to say what languages they should support.

What is the difference between bullets 2 and 4 in your list? What's an example of when a C program would care about the context in which it's calling an exported Chapel routine?

What is the difference between bullets 2 and 4 in your list? What's an example of when a C program would care about the context in which it's calling an exported Chapel routine?

For example, in multilocale C interop we can only do in intent for strings/arrays because we are copying the data in the process of calling a function in a different process. Generally speaking, that is not a limitation of single-locale C interop, where passing around pointers to things in the shared memory space is not a problem. (Of course, one way to solve this issue is to have the multilocale interop use pointers to a shared memory space that includes both processes...)

Oh, OK, so by "multilocale C code/interop" you mean "a multilocale Chapel program interoperating with C" rather than "a multilocale C program interoperating with Chapel"?

I thought the main places we required in intents for interop with multilocale Chapel programs at present were (a) for Python interop where we expect the Python program to be running on the login node and not on the compute nodes and (b) for cases where we were already inserting copies and hadn't bothered to implement other intents yet (but I could easily be misremembering).

Maybe the distinction we really ought to make is "interoperate with something running on one of the Chapel locales" vs. "interoperate with something running on the interactive/login node"? In which case we could use the same routine name in both settings since we run a different binary for each role (the launcher vs. the _real binary). (of course for interoperating with the login node, the compute nodes are also involved, but that's through generated code, so we could name their versions of that RPC-like routine whatever we want).

Oh, OK, so by "multilocale C code/interop" you mean "a multilocale Chapel program interoperating with C" rather than "a multilocale C program interoperating with Chapel"?

Yes

I thought the main places we required in intents for interop with multilocale Chapel programs at present were (a) for Python interop where we expect the Python program to be running on the login node and not on the compute nodes and (b) for cases where we were already inserting copies and hadn't bothered to implement other intents yet (but I could easily be misremembering).

Yes

Maybe the distinction we really ought to make is "interoperate with something running on one of the Chapel locales" vs. "interoperate with something running on the interactive/login node"? In which case we could use the same routine name in both settings since we run a different binary for each role (the launcher vs. the _real binary).

That is true. However this issue is about giving us the flexibility to allow export routines to work with different languages (potentially at the same time). It is true that we don't necessarily need that flexibility for multi-locale vs single-locale Chapel programs inter-operating with C as it stands now. Even so, I think the flexibility would be good for future-proofing that case as well as supporting the other languages.

Even so, I think the flexibility would be good for future-proofing that case as well as supporting the other languages.

I wasn't disagreeing, but was trying to point out that your bullets 2 and 4 might not need to use different procedure names (even if we did support calls from C to Chapel on the login node or compute node for a multi-locale execution) since each routine would be defined in a distinct binary鈥攕o they could use the same name rather than distinct ones. So I was thinking that the client language would always refer to a Chapel function foo as foo where we'd use different names for things like the C-based Python shim and the C-based compute node call that supports calls from the login node, but could still use foo itself for the direct-call-from-C case on either node (and in Python due to the Cython shim).

But the goal is to enable cases where multiple languages are selected without messing each other up.

I can get behind this, at least for cases where the argument intents are supported in multiple languages.

Do we really envision a scenario where a Chapel library is compiled both for multilocale and for single locale execution in a single binary? We don't do that for executables today, I'd maybe expect the user to link to different versions of the library rather than have it in the same one, considering how different the setup stage is

Do we really envision a scenario where a Chapel library is compiled both for multilocale and for single locale execution in a single binary?

I think it's nice for it to be a possibility but I agree with what @bradcray was saying here - https://github.com/chapel-lang/chapel/issues/16134#issuecomment-665383464 - which means that we don't have to worry about that as much as the other elements here.

Do we really envision a scenario where a Chapel library is compiled both for multilocale and for single locale execution in a single binary?

When I'm thinking about this case, I'm thinking about a multi-locale Chapel execution in which:

a) I might have a C program running on the front-end calling into the Chapel launcher, similar to what we support for Python today, where the launcher has to feed the request out to the compute nodes
b) a C routine on the compute node (as part of a library that was linked into Chapel, say), calling into the Chapel program on that same compute node.

So I think we'd potentially need to support calls into both the launcher and the compute binary, but I wouldn't call this single- vs. multi-locale interop in a single binary so much as login vs. compute interop within the same binary.

Was this page helpful?
0 / 5 - 0 ratings