!standard B.3(1/3) 13-01-02 AI12-0028-1/03 !standard B.3(60.15/3) !standard B.3(75) !class binding interpretation 12-06-04 !status Amendment 202x 13-01-02 !status WG9 Approved 13-06-14 !status ARG Approved 9-0-2 12-12-08 !status work item 12-06-04 !status received 12-04-13 !priority Medium !difficulty Medium !subject Import of variadic C functions !summary A new convention, C_Variadic_*N* is introduced to handle C functions with variable numbers of parameters. !question B.3(75) contains the following statement: "10 A C function that takes a variable number of arguments can correspond to several Ada subprograms, taking various specific numbers and types of parameters.". This is only true for ABIs where the regular C calling convention is the same as the calling convention used for variadic C functions. On systems using the System V ABI AMD64 (used e.g. on 64bit Linux, see http://www.x86-64.org/documentation/abi.pdf) the calling conventions are different, which leads to unpredictable results when importing variadic C functions. Should this note be rewritten or deleted? (Yes.) Should Ada have some standard way of interfacing to variadic C functions? (Yes.) !recommendation (See summary.) !wording In B.3(1/3) replace ... and support for specifying the Convention aspect with *convention* identifiers C and C_Pass_By_Copy. with ... and support for specifying the Convention aspect with *convention* identifiers C, C_Pass_By_Copy, and any of the C_Variadic_*n* conventions described below. Append after B.3(60.15/3) The identifiers C_Variadic_0, C_Variadic_1, C_Variadic_2, and so on are convention_identifiers. These conventions are said to be *C_Variadic*. The convention C_Variadic_*n* is the calling convention for a variadic C function taking *n* fixed parameters and then a variable number of additional parameters. The C_Variadic_*n* convention shall only be specified as the convention aspect for a subprogram, or for an access-to-subprogram type, having at least *n* parameters. A type is compatible with a C_Variadic convention if and only if the type is C-compatible. AARM To Be Honest: It is implementation defined what the largest *n* in C_Variadic_*n* is supported. We don't say this because it complicates the wording and it is true for almost any convention_identifier (only Ada is required to be supported by the language, all others need to be documented in order for programmers to know that they are available). In B.3(75), replace "A C function that takes a variable number of arguments" with "A variadic C function" !discussion This AI is classified as a Binding Interpretation, applying to Ada 2012. Note that adding a language name is something that any implementation can do, so Ada 95 and Ada 2005 compilers also can implement the solution. As such the classification does not matter that much, but it seems better to encourage adoption in Ada 2005 compilers (else it is impossible to write portable interfaces without resorting to writing C code, which should never be required lest programmers wonder why they're using Ada at all). !corrigendum B.3(1/3) @drepl The facilities relevant to interfacing with the C language and the corresponding subset of the C++ language are the package Interfaces.C and its children, and support for specifying the Convention aspect with @i@fas C and C_Pass_By_Copy. @dby The facilities relevant to interfacing with the C language and the corresponding subset of the C++ language are the package Interfaces.C and its children, and support for specifying the Convention aspect with @i@fas C, C_Pass_By_Copy, and any of the C_Variadic_@i conventions described below. !corrigendum B.3(60.15/3) @dinsa If a type is C_Pass_By_Copy-compatible, then it is also C-compatible. @dinst The identifiers C_Variadic_0, C_Variadic_1, C_Variadic_2, and so on are @i@fas. These conventions are said to be @i. The convention C_Variadic_@i is the calling convention for a variadic C function taking @i fixed parameters and then a variable number of additional parameters. The C_Variadic_@i convention shall only be specified as the convention aspect for a subprogram, or for an access-to-subprogram type, having at least @i parameters. A type is compatible with a C_Variadic convention if and only if the type is C-compatible. !corrigendum B.3(75) @drepl @s9 @dby @s9 !ACATS test An ACATS C-Test could be created to interface to a variadic C function (provided by the test suite) using these identifiers. It's not clear that such a test would be of much value. !appendix From: Schoepfin, Markus Sent: Friday, April 13, 2012 3:41 AM !topic Problematic wording regarding the import of variadic C functions !reference Ada 2005 RM B.3(75) !from Markus Sch”pflin 12-04-13 !keywords varargs !discussion B.3(75) contains the following statement: "10 A C function that takes a variable number of arguments can correspond to several Ada subprograms, taking various specific numbers and types of parameters. ". Currently this is only true for ABIs where the regular C calling convention is the same as the calling convention used for variadic C functions. On systems using the System V ABI AMD64 (used e.g. on 64bit Linux, see http://www.x86-64.org/documentation/abi.pdf) the calling conventions are different, which leads to unpredictable results when importing variadic C functions. Possible solution 1: Amend B.3(75) with a warning that this only works on ABIs where the calling convention for variadic C functions is the same as for regular C functions. Possible solution 2: As an implementation advice in B.3, add a new import convention (e.g. `C_Varargs' or `C_Variadic') which allows to explicitly specify a different calling convention for variadic C functions. This new calling convention of course should map to `C' on ABIs where `C_Varargs' and `C' are identical. The discussion on comp.lang.ada which stipulated this mail can be found here: http://groups.google.com/group/comp.lang.ada/browse_thread/thread/235855e3822c83d1# **************************************************************** From: Tucker Taft Sent: Friday, April 13, 2012 2:51 PM Thanks for bringing this to our attention. I would agree that a special "convention" might be the best way to handle this. However, in some cases just using, for example, C_Varargs as the convention, might not provide enough information, as perhaps it is necessary to know where the varargs part starts. However, it sounds like the AMD64 ABI for varargs doesn't need anything more than a count provided in the %al register. Here is a quote from the ABI document: ... %al does not need to match exactly the number of vector registers, but must be an upper bound on the number of vector registers used and is in the range 0-8 inclusive. So setting it to 8 will always work, apparently, through presumably incur a heavier overhead, so even a simple-minded compiler could get something that worked, seeing only a calling convention of, say, C_Varargs. For now, the recommendation in the GNAT user manual seems like the correct approach. That is, create a wrapper function in C that takes a fixed number of arguments, and then have that call the variadic function. **************************************************************** From: Schoepfin, Markus Sent: Monday, April 16, 2012 4:42 AM > So setting it to 8 will always work, apparently, through presumably incur a > heavier overhead, so even a simple-minded compiler could get something that > worked, seeing only a calling convention of, say, C_Varargs. As long as the compiler doesn't use the vector registers (which I think is the case for GNAT), just setting %al to 0 also works without resulting in a possible performance degradation. But that wasn't really the point of my mail. I think it is problematic that the Ada standard seems to imply by B.3(75) that variadic C functions can be called by using various Ada overloads and a simple import pragma. And this should be fixed some way or another. Providing an official way to import variadic C function of course would be the best solution, but even replacing B.3(75) by a statement that explicitly warns that variadic C functions in general cannot be imported in this way (or perhaps use a wording similar to the one in the GNAT user guide) would be an improvement over the current wording, in my opinion. On the other hand, B.3(75) has been unmodified since the original Ada 83 standard, and perhaps it's more a problem with my reading of the standard than a problem with the standard itself. **************************************************************** From: Tucker Taft Sent: Monday, April 16, 2012 6:53 AM Actually, there was no B.3(75) before Ada 95. In any case, it is only relatively recently that this became a problem. In old versions of C, function prototypes were completely optional, so even the C compiler didn't know it was calling a variadic function. C++ always required prototypes, but the compilers still attempted to use calling conventions that didn't take advantage of this for many years (actually, for decades). Apparently when they designed the ABI for the X86-64 architecture, they finally decided to require prototypes, even for C. So I think this was correct as written until relatively recently, and unfortunately, no one pointed out this problem during the Ada 2012 revision process. We can only fix problems we know about! This is certainly a good candidate for a fix to the standard, but it will be a while before there is another "official" Ada standard, so hopefully we can get the word out that variadic functions may require special handling. **************************************************************** From: Schoepfin, Markus Sent: Monday, April 16, 2012 8:15 AM Yes, you are right of course, B.3(75) was added for Ada 95. Sorry for the thinko. Is there anything else I need to do that this issue stays on the radar of the ARG? **************************************************************** From: Tucker Taft Sent: Monday, April 16, 2012 7:50 PM No, Randy Brukardt is very diligent about creating official "AI"s if the issue is substantive, and this one seems to be so. Official AIs are tracked very carefully and pop up next time we have an ARG meeting. **************************************************************** From: Florian Weimer Sent: Tuesday, April 17, 2012 2:26 AM > In any case, it is only relatively recently that this became a > problem. In old versions of C, function prototypes were completely > optional, so even the C compiler didn't know it was calling a variadic > function. C++ always required prototypes, but the compilers still > attempted to use calling conventions that didn't take advantage of > this for many years (actually, for decades). C compilers use different calling conventions for functions with and without prototypes. Historically, a prototype-less declaration had to be matched with an old-style function definition. The default argument promotions (such as conversion from float to double) are automatically applied when you call a function without a prototype. For variadic functions with prototypes, default argument promotions are applied only for the optional arguments. This can be compensated for on the Ada by using promoted types there. But prototypes had an ABI impact right from the beginning. With the x86_64 psABI (and apparently, the Windows amd64 ABI), it is insufficient to apply the default argument promotions manually. This is indeed a difference. But a knowledge when to apply default argument promotions would still help Ada compilers to detect incorrect interface declarations. Knowing the number of fixed arguments is needed for that, too. **************************************************************** From: Tucker Taft Sent: Monday, July 23, 2012 9:33 AM > Append after B.3(60.15/3) > > For some implementation-dependent integer M greater than or equal to > 16, I find this a weird way of specifying the requirement that you support at least _0 up to _16. And where in the world did "16" come from? If we have such a requirement, I would put it in a separate sentence in the implementation requirements section. E.g.: Implementations shall support at least up to C_Variadic_16. > ... the identifiers C_Variadic_0, C_Variadic_1, C_Variadic_2, and on > up to C_Variadic_*M* are convention identifiers. If we drop "M" then this becomes simply: ..., C_Variadic_2, and so on, are convention identifiers. **************************************************************** From: Steve Baird Sent: Monday, July 23, 2012 10:58 AM > I find this a weird way of specifying the requirement that you support > at least _0 up to _16. And where in the world did "16" come from? I see two choices here: 1) Don't require any support for this feature. That means code that is intended to be portable can't use this feature. 2) Pick a number. I picked one - 16 just seemed like a reasonable value. No more justification than that. I agree that the wording changes you suggested are improvements. **************************************************************** From: Tucker Taft Sent: Monday, July 23, 2012 11:12 AM > I see two choices here: > 1) Don't require any support for this feature. That means code that is > intended to be portable can't use this feature. > 2) Pick a number. I picked one - 16 just seemed like a reasonable > value. No more justification than that. I don't see any need for a limit here at all. We don't specify elsewhere a separate requirement for number of parameters allowed in a subprogram. Implementations of course have some capacity limits, but pulling this number "16" out of a hat seems a bit bizarre at this stage. I would presume this is just a pattern match thing, where if the convention name matches "C_Variadic_" and is less than or equal to the number of formal parameters, it is acceptable. I suppose one could argue for a separate aspect, something like First_Varying => N+1, which would perhaps be easier to swallow. This would make it independent of the "language" specified by "Convention," which might be nice for interfacing to other languages that have similar capabilities. > I agree that the wording changes you suggested are improvements. Glad they were useful. **************************************************************** From: Steve Baird Sent: Monday, July 23, 2012 12:04 PM > I don't see any need for a limit here at all. We don't specify > elsewhere a separate requirement for number of parameters allowed in a > subprogram. Ok, you've convinced me. This felt like a different situation because it affects whether a given identifier is a recognized convention name, but I suppose this difference isn't really significant. **************************************************************** From: Robert Dewar Sent: Tuesday, July 24, 2012 6:09 PM > I would presume > this is just a pattern match thing, where if the convention name > matches "C_Variadic_" and is less than or equal to > the number of formal parameters, it is acceptable. Well that's a totally new mechanism, makes it MUCH harder to implement IMO. > I suppose one could argue for a separate aspect, something like > First_Varying => N+1, which would perhaps be easier to swallow. > This would make it independent of the "language" specified by > "Convention," which might be nice for interfacing to other languages > that have similar capabilities. Better **************************************************************** From: Steve Baird Sent: Tuesday, July 24, 2012 6:35 PM I think this would imply that the "calling convention" part of determining whether two profiles are subtype conformant (6.3.1(17/3)) would need to look at more than the Conventions of the two profiles. Consider: procedure Proc (X, Y, Z : Integer) with Convention => C, First_Varying => 2); type Ref is access procedure (Xx, Yy, Zz : Integer) with Convention => C; Ptr : Ref := Proc'Access; -- legal? This example must be illegal (right?) and yet the profiles for Ref and Proc have the same Convention (i.e., C). So just looking at the convention isn't enough. We could certainly do this, but it seems to run counter to the idea that an Ada source-level Convention for a profile corresponds to an implementation level calling convention. ****************************************************************