T.R | Title | User | Personal Name | Date | Lines |
---|
304.1 | Ok to return address, I think | MOIRA::FAIMAN | light upon the figured leaf | Wed Mar 13 1991 22:08 | 21 |
| Without digging out the standard, it would appear to me that it
would be valid to return an *address* (even the address of a string
or descriptor) in R0, and that the cases where the extra parameter
is required are those where the string *value* is returned. Then,
the calling routine must allocate a buffer for the data and pass the
buffer address to the called routine, which must copy the data into
the buffer.
However, if a called routine is going to return an address in R0,
then it must be the address of permanently valid data. For example,
it might be the address of read-only descriptor that described a
read-only string, but not the address of a descriptor that described
a string in global storage, since the returned data might then
become invalid when that global data was modified.
In general, I believe that the normal way of returning string data
is "by descriptor, write only", where the argument might be an
explicit argument, or might be a "hidden first argument" function
return value.
-Neil
|
304.2 | VAXC, VCPS, etc. | LENO::GRIER | mjg's holistic computing agency | Thu Mar 14 1991 16:15 | 26 |
| VAX C doesn't really make much effort at all to follow the letter or
the spirit of the VCPS, but if you're careful enough you can do it. It
follows the way the original C implementations were done, for better or
worse.
It's not "normal" in callable routines to return the address of a
descriptor. If the descriptor is dynamically allocated (the descriptor
itself, not the data!) how do you deallocate it? You'd have to provide
a function to deallocate the descriptor when the caller is done with it,
or clearly document how to call LIB$FREE_VM or something to deallocate it.
Either way, it's messy.
You'll find that generated code for languages like Pascal and Ada
will use 4(ap) to store the address of large structured return values.
The "correct" thing to do - which is what people expect in most cases,
is that you pass in the address of a string descriptor (allocated by the
caller, not the callee) into which the data is copied. If you're feeling
low-level, you should specify that it should be a fixed length string
descriptor, in which case you just look at the first longword as the
length of the buffer and the second as the address. If you're feeling
high-level and friendly, you should use LIB$SCOPY_DX or another LIB$ string
function to move the data into the user's buffer so that the correct
semantics around truncation, allocation and format are followed.
-mjg
|
304.3 | | JAMMER::JACK | Marty Jack | Thu Mar 14 1991 16:50 | 2 |
| >descriptor, in which case you just look at the first longword as the
Important note: That should be word not longword.
|
304.4 | Function Versus Procedure? | DSM::SCHWARTZ | DSM Engineering | Thu Mar 14 1991 17:51 | 17 |
| The technique VAX DSM currently uses for passing strings as "output"
parameters, is to pass an empty dynamic string descriptor, and have
the user call LIB$SCOPY_DXDX (etc.) to fill it in. VAX DSM copies the
string to internal storage and calls LIB$SFREE1_DD.
This method is fine for external procedure calls. However, .0 was
concerned with external function calls, where the function returns
a value (as opposed to a VMS status code as the "procedure value").
Re .2: Under what conditions will the Pascal compiler use 4(AP) to
return a function value? I was unable to cause this to happen.
Sounds like its OK to violate the VPCS as long as you are calling
external routines written in C.
David
|
304.5 | Ok... | LENO::GRIER | mjg's holistic computing agency | Fri Mar 15 1991 00:01 | 41 |
| Re: Pascal:
I believe if you just have it return a structure larger than 4 or 8
bytes it will pass it as a fake first parameter.
Let's see...
MODULE X;
TYPE
BigString = VARYING [65535] OF CHAR;
[GLOBAL] FUNCTION ReturnABigString (A : INTEGER) : BigString;
VAR
Indx : INTEGER;
t : BigString VALUE ZERO;
BEGIN
FOR Indx := 1 TO A DO
t := t + 'X';
ReturnABigString := t;
END;
END.
That passes back the result in 4(AP). (When you read the machine code,
remember that R12 = AP)
Re: reading the word:
Is that correct, for utilities which expect a fixed length string
descriptor? Maybe learning by example is wrong, but I seem to recall
seeing code examples where the first longword is assumed to be the entire
length, rather than just the first word. (Of course, you're right, you
should just use the first word so that you're compatible with RTL string
descriptors where the address points to the real start of data. I can't
think of where I've seen specific examples of this.)
-mjg
|
304.6 | | TLE::BRETT | | Fri Mar 15 1991 07:34 | 7 |
| You may have seen it in internal code produced by the Ada compiler. We
use normal CLASS S/CLASS SB descriptors for imported or exported
intervals, but internally when passing long strings we use a modified
style of the descriptor without class or dtype field, with the width
spread right across the first longword.
/Bevin
|
304.7 | | OZROCK::MCGINTY | Truffle prefers vi | Mon Mar 18 1991 16:57 | 12 |
|
The first longword in a string descriptor contains the length, in a
word, followed by two bytes containing the class and type of the string.
-------------------------------
| class | type | length |
|-------------------------------|
| address |
-------------------------------
The definitions of the fields and constants are contained in $dscdef
in sys$library:starlet.mlb.
|
304.8 | In certain places... | LENO::GRIER | mjg's holistic computing agency | Tue Mar 19 1991 21:30 | 19 |
| Re: .6:
Yup, that's the structure, but any code which doesn't layer on the
RTL (i.e. system services, pretty much any inner-mode code,) just treats
string descriptors ala..
DESC: .LONG 5
.ADDRESS Y
Y: .ASCII /Hello/
Without any cares in the world about classes or types or whatever.
User-mode code should *always* use the LIB$/STR$ stuff to work with
descriptors to be friendly, but there's a large body of code which most VMS
folks have to interact with which doesn't have any more complex notion
of descriptors than the MACRO-32 I outlined above.
-mjg
|
304.9 | The original question | DSM::SCHWARTZ | DSM Engineering | Wed Mar 20 1991 11:37 | 6 |
| We seem to have forgotten my original question which asks how the
various language compilers return strings as external function values,
or receive strings as external function values.
Is C the only language that "violates" the calling standard in this respect?
|
304.10 | | TLE::BRETT | | Wed Mar 20 1991 13:20 | 19 |
| This is a very tricky area of the calling std.
It is NOT correct to describe VAX C as "violating" it.
The VAX Calling Std basically allows you to return string results in
one way - by reference via 4(ap). There is NO standard way of
returning a string result whose size is not already known to the
caller.
Ada and PL/I return such strings by having the caller pass in some form
of descriptor, and by either heap allocating the result or fiddling
around so that the sp is not reset by the return, and have the called
routine fill in the descriptor saying where the result is.
C never returns strings, it returns "char *"'s POINTERS TO STRINGS [and
to the disgust of programmers interested in re-entrancy these are often
pointers to OWN storage].
/Bevin
|
304.11 | Well, in C there is... | LENO::GRIER | mjg's holistic computing agency | Wed Mar 20 1991 19:20 | 26 |
| Re: no standard way to return arbitrary lengthed strings:
In C, part of the implied semantics are that the strings are null-
terminated, so returning the address of the first character of the string
is completely reasonable. In a C-only environment.
The VPCS is truly minimal, and there's a degree of following it "to the
letter" and "in spirit". With the exception of requiring certain descriptor
formats and how the parameters *are* passed (i.e. not in registers for CALLS
and CALLG linkages,) the "to the letter" interpretation of the VPCS says
very little.
The utility is in following it in spirit. C isn't very friendly towards
following the VCPS in spirit, even if the flexibility of the language does
permit following it to the letter. Other VMS languages tend to generate
code which does a fair job of producing entry points which follow both the
spirit, as well as the letter, of the law. (Ada, Pascal, COBOL and BASIC
to name a couple. All the C comments apply equally to BLISS, because they
have roughly equivalent views of the world in terms of calling in the
CALLS/CALLG sense...)
My personal rule-of-thumb is that if SDL can quickly and easily generate
an accurate description of the interface which is usable without trickery
from those languages, then you're following the VPCS in spirit.
-mjg
|
304.12 | don't forget dynamic descriptors | SAUTER::SAUTER | John Sauter | Thu Mar 21 1991 11:20 | 7 |
| re: .10
I believe the calling standard allows string results to be returned
by descriptor, also using 4(ap). If the caller doesn't know what
the length of the string will be then he can pass a dynamic
descriptor.
John Sauter
|
304.13 | Aside: CLASS=0, DTYPE=0 *is* defined. | SKYLRK::WHEELERLL | Lloyd Wheeler | Thu Mar 21 1991 12:38 | 20 |
| Re .6, .7, .8: (and a couple others)
To close on the implied "hack" of using the first *long*word of a
descriptor as the length:
I remember reading in the VPCCHS that DTYPE and CLASS fields of zero
*do* have a defined meaning. If these fields are zero, the called
routine is supposed to assume that the argument data is of the
"correct" type. (See the description of DSC$K_DTYPE_Z.)
In practice, at least for the RTL routines (and, in a funny, NUL-padded
way, for the System Services) this means that the argument will be
treated as a static string.
Of course, using the *entire* longword as a length field would be a
violation of the VPCCHS. (And, of course, this makes no difference if
a single product controls the code used in both the caller and the
called routines.)
Lloyd
|
304.14 | Thanks | DSM::SCHWARTZ | DSM Engineering | Mon Mar 25 1991 14:45 | 73 |
| RE: .11
> My personal rule-of-thumb is that if SDL can quickly and easily generate
> an accurate description of the interface which is usable without trickery
> from those languages, then you're following the VPCS in spirit.
I tried some experiments with SDL before entering the base note.
DESCRIPTOR and RTL_STR_DESC are not supported as RETURN keywords:
MODULE test;
ENTRY test1 RETURNS DESCRIPTOR;
ENTRY test2 RETURNS RTL_STR_DESC;
END_MODULE;
$ sdl/lang=pascal TEST1
%SDL-E-UNDEFSYM, Undefined symbol DESCRIPTOR [Line 3]
Error on line 5 column 32: Replaced reserved-word "rtl_str_desc" with
identifier
%SDL-E-INVNAME, Item name is invalid
%PLI-F-ERROR, PL/I ERROR condition.
-SYSTEM-F-ACCVIO, access violation, reason mask=00, virtual address=00000004, PC
=0009A06A, PSL=03C000A4
$
However, SDL was perfectly happy with the following:
MODULE test;
ENTRY test1 RETURNS CHARACTER;
ENTRY test2 RETURNS CHARACTER VARYING;
ENTRY test3 RETURNS ADDRESS;
ENTRY test4 RETURNS OCTAWORD;
END_MODULE;
$ sdl/lang=pascal TEST2
MODULE TEST2 ;
[HIDDEN] TYPE (**** Pre-declared data types ****)
$OCTA = [OCTA,UNSAFE] RECORD
L0,L1,L2:UNSIGNED; L3:INTEGER; END;
$DEFTYP = [UNSAFE] INTEGER;
$DEFPTR = [UNSAFE] ^$DEFTYP;
(*** MODULE test ***)
[HIDDEN] TYPE (**** SDL-Generated type names ****)
test$$typ1 = VARYING [1] OF CHAR;
[ASYNCHRONOUS] FUNCTION test1 : CHAR; EXTERNAL;
[ASYNCHRONOUS] FUNCTION test2 : test$$typ1; EXTERNAL;
[ASYNCHRONOUS] FUNCTION test3 : $DEFPTR; EXTERNAL;
[ASYNCHRONOUS] FUNCTION test4 : $OCTA; EXTERNAL;
END.
Thanks for all the feedback. The bottom line appears to be that if a
language (compiler) doesn't enforce the VPCS on external calls, and our
customers want to call external functions that don't adhere to the VPCS,
then VAX DSM should not prevent them from doing so!
David
|