Well, I have my own compiler, however, I don't as of yet target x86-64.
one of the main reasons:
the calling conventions used in Linux and friends are, scary...
Short Description:
arguments are passed in registers, meanwhile, others may be left on the
stack, and there is no particular relation between the registers and the
stack layout.
Beyond Just Putting A Big Damper On The Compiler Machinery, It Leaves Many
Ugly Questions:
how does one effectively deal with varargs? (of all things, the convention
still uses regs even for passing varargs...).
it seems almost like one will have to dump all of the registers into a kind
of a context, and keep note of particular counts (GPR pos, XMM pos, stack
pos).
how does one do things like hidden argument injection? (useful for many
tasks, such as closures).
.....
Or, in general, there are all sorts of ugly questions wrt the topic of
actually working with the args list in a first-class manner (beyond just the
task of having to get the compiler to work with all this to begin with).
I am left considering the idea of abandoning this convention for internal
usage, and instead using a different default calling convention. probably,
name mangling would be used to distinguish them, and interface stubs would
be generated to handle external calls.
sadly, this would lead to a big issue, which is that there would be a kind
of a "wall" between the two worlds. firstly, interface stubs would be
needed, and secondly, that function pointers could not be safely passed
across this wall (limiting the usage of callbacks, ...).
more-so, these stubs could potentially be limited in terms of what they can
pass along (functions passing raw structs or similar stack-disordering
features may not be wrapable).
so, such a wall could be very problematic (especially in a mixed-compiler
codebase).
now, what kind of convention would I be imagining:
well, basically, it would be a hybrid of the existing conventions, and the
good old 32-bit x86 convention.
in particular, arguments would be passed on the stack (in good old linear
right-to-left ordering).
now, beyond this wall, there is also a possible risk of a performance impact
(both with calls, and in general).
it is also possible I could just "bear with it", and possibly use
stack-flattening/unflattening hacks mostly as a means of dealing with edge
cases.
alternatively, I could use a variant of the MS convention by default (even
on Linux), which is basically about the same (it being fairly simple to dump
arguments to the shadow space, or retrieve them from there).
the task of defeating the "wall" would then be a task for later...
any suggestions?
[My suggestion is that if you want people to use the compiler, use the same
calling sequence as everyone else. The code to deal with varargs is ugly,
but I would be surprised if it were very long. -John]
|
|
0
|
|
|
|
Reply
|
cr88192355 (1754)
|
5/11/2008 11:44:42 PM |
|
"cr88192" <cr88192@hotmail.com> wrote in message
> Well, I have my own compiler, however, I don't as of yet target x86-64.
>
> one of the main reasons:
> the calling conventions used in Linux and friends are, scary...
>
>
> Short Description:
> arguments are passed in registers, meanwhile, others may be left on the
> stack, and there is no particular relation between the registers and the
> stack layout.
>
> Beyond Just Putting A Big Damper On The Compiler Machinery, It Leaves Many
> Ugly Questions:
> how does one effectively deal with varargs? (of all things, the convention
> still uses regs even for passing varargs...).
<snip>
> alternatively, I could use a variant of the MS convention by default
> (even on Linux), which is basically about the same (it being fairly
> simple to dump arguments to the shadow space, or retrieve them from
> there).
>
> the task of defeating the "wall" would then be a task for later...
>
> any suggestions?
> [My suggestion is that if you want people to use the compiler, use
> the same calling sequence as everyone else. The code to deal with
> varargs is ugly, but I would be surprised if it were very
> long. -John]
Yes, this is probably what I will have to do.
the convention is a little bit of a horror, but I guess it has to be lived
with...
after all, it is probably my due punishment for writing yet another C
compiler. it will also be the cost for trying to add an x86-64
target.
well, I do JIT and I focus on somewhat different goals than those of
LLVM and similar. probably a good enough justification.
all just the fiddling of some lowly hobbyist anyways...
general context:
the compiler exists as 2 major parts:
an upper compiler, which does parsing and a lot of the higher-level
transformations;
the lower compiler, which takes the RPN-based output of the upper compiler,
and compiles it into assembly code.
the JIT is simply an integrated assembler and linker (I use an
NASM-like syntax, and it can also dynamically load COFF, ELF, GNU-AR
libs, ... and can export COFF).
Win32 is my primary target, with Linux (x86 and x86-64) as secondary
targets. Win64 may also be a target at some point.
the primary use at present is as a scripting VM. technically, pretty damn
overkill, but this is the price I pay to not have to endlessly deal with
stupid FFI issues. now I just deal with code-generation issues...
later on, I may also incorporate a Java frontend as well (currentlly, I only
have a C frontend).
it is my eventual hope to use it for implementing something much closer to
the high-level scripting languages I have usually implemented in the past,
yet while hopefully still being solidly attached to C-land...
a nearer term goal is to add both lexical closures and eval as C extensions
(note: I already have garbage collection, dynamic types, geometric vectors
and quaternions, ... as compiler extensions).
note, on x86 maintaining the integrity of the calling convention and proper
threading has been an invariant goal (it not being acceptable for features
either to break the calling convention or violate threading).
my previous lower compiler was based on a stack machine, which later ended
up using register allocation and similar more as a means of optimization;
basically the lower compiler is an elaborate mass of fixed logic trees.
later on, I had started a rewrite (highly delayed due to getting backlogged
with homework and similar...).
I started building from the bottom up, targetting both x86 and x86-64 at the
same time, and reusing some of the codegen machinery from the previous lower
compiler.
what I have now is basically a bunch of register allocation machinery, code
for performing various operations on registers, and code for performing
low-level memory operations.
however, I started to get a little stalled as to which direction I would go.
I am thinking of adding an intermediate representation basically consisting
of a kind of value flow graph.
this would imply a new upper end of the lower compiler, basically converting
from RPN into the graph representation (and also handling most of the
typesystem machinery and similar), possibly doing some fiddling, and then
flattening it back out in the process of converting it into assembly code
(the big hassle here being that registers have to be allocated in effective
reverse order...).
all of this will be because I don't really think it is possible to target
x86-64 with a big mass of fixed-logic being driven by the RPN-based input
(this works for x86, but doesn't really look like it will work for x86-64, a
graph being necessary just to generate working code it seems...).
but, I don't know if there is much point in all this, or if I am headed in a
good direction.
general opinions?...
or such...
|
|
0
|
|
|
|
Reply
|
cr88192
|
5/12/2008 10:10:59 AM
|
|
On 12 May, 00:44, "cr88192" <cr88...@hotmail.com> wrote:
> Well, I have my own compiler, however, I don't as of yet target x86-64.
>
> one of the main reasons:
> the calling conventions used in Linux and friends are, scary...
I'm working on a language where the call conventions are to be filled-
in after compilation. Each function is to compile to also use virtual
registers rather than real ones. Then only when one function calls
another do we set up the registers for both functions, the calling
conventions to pass data between them, and decide which regs to either
spill or save.
I should say this is work in progress. I'm not sure how well it will
work but expect that for small called functions there may be useful
gains. Oh, yes, the decision as to whether to inline small functions
will happen at the same time (based on optimisation speed/space
goals).
Not sure if that's any help. If it works it would illustrate that
subroutine and OS calling conventions can be plugged in after and are
distinct from code generation. OS calls are just function calls where
the callee's needs have already been decided - stack for Windows, regs
for Linux etc.
--
James
[How do you plan to handle function pointers? -John]
|
|
0
|
|
|
|
Reply
|
James
|
5/12/2008 11:12:03 AM
|
|
On May 12, 12:44 am, "cr88192" <cr88...@hotmail.com> wrote:
> Well, I have my own compiler, however, I don't as of yet target x86-64.
>
> one of the main reasons:
> the calling conventions used in Linux and friends are, scary...
>
> Short Description:
> arguments are passed in registers, meanwhile, others may be left on the
> stack, and there is no particular relation between the registers and the
> stack layout.
Ouch. This is hideous. Since I didn't know what you were talking
about, but is in the process of writing a compiler in Ruby (currently
targeting 386, but I want to add x86_64 later) I did a search and came
up with this: http://www.x86-64.org/documentation/abi-0.99.pdf
I can see why you consider it scary. My compiler is taking a lot of
ideas from Ruby, and that includes being able to take all arguments as
an array, which is trivial on 32 bit x86, but a real pain here..
There are a number of callee saved registers, so you can do some forms
of extension of the calling convention without losing the ability for
your code to call out to C without interface stubs, but you'd lose the
ability for C to call back in, of course (I made that sacrifice in my
compiler for varargs, where I pass the count of varargs provided in a
register, in which case you'll need a wrapper to safely call it from
C, but if my code calls varargs C functions, the value of that
register will just be ignored).
Apart from that, I'd strongly suggest you stick with the calling
convention, even though it's painful. To handle higher order argument
lists you could always synthesize an anonymous conversion function if
needed, to turn it into an array or iterate over the arguments.
As for "hidden arguments", presumably they won't be hidden to the C
code anyway, or the values would be lost when crossing the barrier, so
there I'd consider just treating them as normal arguments with respect
to the calling convention and just hiding them to the users of your
language.
There's in practice nothing outright stopping you from defining your
function signatures in a way which will force all the arguments onto
the stack and still comply with this calling convention, either, as
far as I can see, but it'd result in function signatures that would be
extremely inconvenient to work with on the C side, and you'd still
have a problem calling C code unless your compiler explicitly knows
when it needs to resort to the C calling convention.
I'd bite the bullet...
Vidar
|
|
0
|
|
|
|
Reply
|
Vidar
|
5/12/2008 12:48:02 PM
|
|
On May 11, 5:44 pm, "cr88192" <cr88...@hotmail.com> wrote:
> now, what kind of convention would I be imagining:
> well, basically, it would be a hybrid of the existing conventions, and the
> good old 32-bit x86 convention.
>
> in particular, arguments would be passed on the stack (in good old linear
> right-to-left ordering).
That's kind of what I did in the Flaming Thunder compiler (which
supports the x86 and x86-64 versions of FreeBSD, Linux, Mac OS X and
Windows).
Some differences:
The calling convention is left-to-right (since I was making my own
calling convention, I figured I might as well fix the old right-to-
left holdover and use the stack the way the stack should be used ---
left-to-right). Left-to-right means the arguments are evaluated in
the order that users expect them to be. If the function is vararg,
then I push the argument count on the stack last.
When I need to access a system service, I translate my stack layout to
the Windows/Mac/Linux/FreeBSD/32/64 layout, then call the system.
|
|
0
|
|
|
|
Reply
|
Dave
|
5/13/2008 3:10:58 AM
|
|
"Dave Parker" <daveparker@flamingthunder.com> wrote in message
> On May 11, 5:44 pm, "cr88192" <cr88...@hotmail.com> wrote:
>> now, what kind of convention would I be imagining:
>> well, basically, it would be a hybrid of the existing conventions, and
>> the
>> good old 32-bit x86 convention.
>>
>> in particular, arguments would be passed on the stack (in good old linear
>> right-to-left ordering).
>
> That's kind of what I did in the Flaming Thunder compiler (which
> supports the x86 and x86-64 versions of FreeBSD, Linux, Mac OS X and
> Windows).
>
> Some differences:
>
> The calling convention is left-to-right (since I was making my own
> calling convention, I figured I might as well fix the old right-to-
> left holdover and use the stack the way the stack should be used ---
> left-to-right). Left-to-right means the arguments are evaluated in
> the order that users expect them to be. If the function is vararg,
> then I push the argument count on the stack last.
>
> When I need to access a system service, I translate my stack layout to
> the Windows/Mac/Linux/FreeBSD/32/64 layout, then call the system.
yes, though possible, there is a biggie issue with this: it would be
rather painful in the case of mixed-compiler codebases.
Also, in my case for the time being I am limited to right-to-left
ordering, since that is what I am getting out of the upper compiler
(for x86, I more or less layed out all of the logic in a fixed
ordering, namely that of execution on x86, however this ordering does
not work so well for x86-64...).
Recently, I had started beating together code for internally using
SSA, but this does not look like it will be so easy. SSA seems, rather
awkward... a big issue is that one ends up with a graph, and rather
than being driven either imperatively, or simply by unwinding a tree,
requires (as it was turning out in my case at least) identifying
top-level expressions and stepping along the graph doing register
allocation and such.
Just as of writing this, I have had an interesting idea: it could be
possible to abandon my current RPN based representation, and
reimplement a lot of the machinery from the RPNIL compiler into a
modified form of the existing upper compiler.
The result would be replacing the big mass of fixed-form code
(basically, a structure driven purely by a central switch statement
and a big mass of function calls), with likely a much bigger mass of
XML processing (the current upper compiler being structured around
processing DOM trees).
This would likely imply adding another XML-based stage (likely,
working out all of the type mechanics, ...), the output of which would
then be converted directly to SSA.
A slight modification of this idea would be to use the DOM trees to
directly drive the low-level code generation (XML could likely serve a
similar role to what I am imagining with SSA, albeit there is little
to stop SSA from being represented is XML as well). in this case,
register allocation and codegen would be driven directly by the
process of flattening out these trees.
Never Mind that all this would imply discarding and rewriting almost
everything located between the parser and the low level code
generation (even as such, it could be less work than SSA+reworking the
RPN compiler to target SSA).
Another possible advantage is that, for the implied notable loss of
abstraction, it could make the whole compiler core a lot more open and
flexible (thus easing the addition of new features and possible non-C
languages).
In all this, pretty much everything above low-level code generation,
would become a big mass of transforms applied to globs of XML (I will
just have to deal some with the awkwardness and verbosity of XML
processing... XSLT almost looks tempting...).
possible layout:
parser (intact);
expression reduction, inlining, ... (heavily modified);
expression type propagation and decomposition (new);
tree flattening, register allocation (new, directly-drives next down);
low-level code generation (intact);
assembler, ...
more in general:
I tend to use my compiler right along with gcc, using gcc more as a
static compiler, and mine more as a dynamic compiler.
reasons:
though some code is likely to be highly volatile, other code is likely to be
mostly static;
compiling C code, especially large amounts of it, also tends not to be a
super fast process (sadly, my compiler is not the fastest option WRT
compiling speed...);
likewise, though there are many cases where my compiler does well, at
present gcc generally does better (in terms of general performance and
non-buggy output).
Thus It Is Effective To Compile Parts Of The App Statically, And Other
Parts Dynamically. some parts are linked into the executable, and
other parts are static libraries (however, at present, the entire
library tends to be linked in, wheras later it may be better to
demand-link missing symbols).
Later plans may involve caching and demand compilation (we have the
source, and automatically recompile those parts that have changed).
or such...
|
|
0
|
|
|
|
Reply
|
cr88192
|
5/13/2008 7:51:07 AM
|
|
"Vidar Hokstad" <vidar.hokstad@gmail.com> wrote in message
> On May 12, 12:44 am, "cr88192" <cr88...@hotmail.com> wrote:
>> Well, I have my own compiler, however, I don't as of yet target x86-64.
>>
>> one of the main reasons:
>> the calling conventions used in Linux and friends are, scary...
>>
>> Short Description:
>> arguments are passed in registers, meanwhile, others may be left on the
>> stack, and there is no particular relation between the registers and the
>> stack layout.
>
> Ouch. This is hideous. Since I didn't know what you were talking
> about, but is in the process of writing a compiler in Ruby (currently
> targeting 386, but I want to add x86_64 later) I did a search and came
> up with this: http://www.x86-64.org/documentation/abi-0.99.pdf
>
> I can see why you consider it scary. My compiler is taking a lot of
> ideas from Ruby, and that includes being able to take all arguments as
> an array, which is trivial on 32 bit x86, but a real pain here..
yeah.
> There are a number of callee saved registers, so you can do some forms
> of extension of the calling convention without losing the ability for
> your code to call out to C without interface stubs, but you'd lose the
> ability for C to call back in, of course (I made that sacrifice in my
> compiler for varargs, where I pass the count of varargs provided in a
> register, in which case you'll need a wrapper to safely call it from
> C, but if my code calls varargs C functions, the value of that
> register will just be ignored).
>
my compiler is for C...
the ability to make calls need to be bidirectional...
> Apart from that, I'd strongly suggest you stick with the calling
> convention, even though it's painful. To handle higher order argument
> lists you could always synthesize an anonymous conversion function if
> needed, to turn it into an array or iterate over the arguments.
>
> As for "hidden arguments", presumably they won't be hidden to the C
> code anyway, or the values would be lost when crossing the barrier, so
> there I'd consider just treating them as normal arguments with respect
> to the calling convention and just hiding them to the users of your
> language.
my language is C...
Actually, the hidden arguments are more within the compiler-generated
function body. it itself is wrapped by another automatically generated
wrapper (which has the same interface as the original function).
The added argument would basically be a context representing the
closures' environment...
> There's in practice nothing outright stopping you from defining your
> function signatures in a way which will force all the arguments onto
> the stack and still comply with this calling convention, either, as
> far as I can see, but it'd result in function signatures that would be
> extremely inconvenient to work with on the C side, and you'd still
> have a problem calling C code unless your compiler explicitly knows
> when it needs to resort to the C calling convention.
well, this wont work in my case.
> I'd bite the bullet...
will probably have to.
|
|
0
|
|
|
|
Reply
|
cr88192
|
5/13/2008 8:19:29 AM
|
|
Dave Parker wrote:
(snip)
> The calling convention is left-to-right (since I was making my own
> calling convention, I figured I might as well fix the old right-to-
> left holdover and use the stack the way the stack should be used ---
> left-to-right). Left-to-right means the arguments are evaluated in
> the order that users expect them to be. If the function is vararg,
> then I push the argument count on the stack last.
That doesn't sound very convenient. I have known for a while now that
ANSI C requires varargs routines to be declared as such so that a
different calling convention could be used.
So far, I haven't known any that actually did that, though.
If you always push the length, one could use it to find the address of
the first argument, and then subsequent arguments.
As far as passing arguments in registers, there is at least one
convention (possibly SPARC, but I am not sure now) where some are
passed in registers, but stack space is still reserved for them. The
called routine can then store them into the stack. As some number of
words are kept in the registers, in some cases a double variable will
be half in a register and half on the stack. Storing to the stack is
a convenient way to get the two back together again.
-- glen
|
|
0
|
|
|
|
Reply
|
glen
|
5/13/2008 9:28:57 AM
|
|
On 12 May, 12:12, James Harris <james.harri...@googlemail.com> wrote:
> I'm working on a language where the call conventions are to be filled-
> in after compilation. Each function is to compile to also use virtual
> registers rather than real ones. Then only when one function calls
> another do we set up the registers for both functions, the calling
> conventions to pass data between them, and decide which regs to either
> spill or save. ...
> [How do you plan to handle function pointers? -John]
You mean how to call routines which are dynamically chosen? I'm not
sure I'll have function pointers as such but maybe first class
functions having the same issue. If the caller does not know which
function it will call at the time of compilation perhaps the caller
could fall back to saving all in-use registers and using a generic
method to pass parameters
1. on the stack,
2. in registers,
3. in a block of memory.
Of course, any function which could potentially be called dynamically
would need to use the matching mechanism to retrieve its parameter
values. Things get more interesting if a function could be called
either statically or dynamically. In that case it may need to support
more than one method of parameter passing. Options here? 1. Header and
footer glue code - possibly the more complex of the two glue code
sequences calls the simpler or both types of glue code call the
function proper, 2. Duplicate functions - especially if the function
is short its code could be duplicated between suitable headers and
footers.
Does that sound like it would work? Maybe you meant something entirely
different!
By the way, the idea of duplicating code is intended to be quite
widespread in order to achieve speed. Two cases where that may apply:
First, when the number of iterations is small some iterable constructs
may be implementable more efficiently than in a loop. As an example,
the IBM 360 instruction set includes an insert characters under mask
instruction which can load bytes into a register. It may be the
fastest (and shortest) way to pick up unaligned bytes from memory
where the number of bytes is three or fewer. For higher numbers a loop
may be more appropriate.
Second, where parameters have default values execution may be faster
to execute if there is a copy of the code which uses the defaults.
After all, constants provide ways to optimise that are not possible
when variables are used. There would also need to be a piece of code
emitted for the more general case.
--
James
[Are you considering cache behavior? If you duplicate a lot of code,
it becomes less likely that it'll fit in the cache. -John]
|
|
0
|
|
|
|
Reply
|
James
|
5/14/2008 10:38:42 AM
|
|
On May 13, 4:10 am, Dave Parker <davepar...@flamingthunder.com> wrote:
> Left-to-right means the arguments are evaluated in
> the order that users expect them to be. If the function is vararg,
> then I push the argument count on the stack last.
The evaluation order and the calling convention don't need to be tied
together, though. Gcc on i386 for example subtracts the required
number of words from %esp and then copy the arguments into place via
indexed indirect mov's. No idea if that is slower or faster than
push'ing stuff onto the stack instead, but the version of gcc I've
used "gcc -S" on will use the mov approach even if all arguments are
constants.
Vidar
|
|
0
|
|
|
|
Reply
|
Vidar
|
5/14/2008 3:20:27 PM
|
|
On 14 May, 11:38, James Harris <james.harri...@googlemail.com> wrote:
....
> By the way, the idea of duplicating code is intended to be quite
> widespread in order to achieve speed.
> [Are you considering cache behavior? If you duplicate a lot of code,
> it becomes less likely that it'll fit in the cache. -John]
Well, I'm aware of the issue but I don't have a general formula as yet
to know when to generate alternate copies of code and when not to do
so. Things I can say:
1. This is a code generation issue. The viability of using alternate
copies depends in large part on the target CPU. As such the IR is to
have only the simple loop code.
2. As long as the same alternative of the function code is called
repeatedly in an inner loop the benefits of code cacheing should still
apply. The natural extension of calling the same variant each time is
inlined code which will be appropriate in some cases.
Is it worth it? Although I cannot quantify the gains yet it is easy to
demonstrate cases where loops with few iterations are faster encoded
with non-loop instruction sequences. This applies even including a
test and branch (which is not needed if the iteration count is
constant or predictable). If the test and branch is needed viability
depends on correct branch prediction. In terms of ease of use I want
the programmer who uses the language to be able to code simple loops
without having to think about special per-CPU cases to gain speed, and
to be able to leave efficient code generation to the compiler.
The main intended benefit is the source code can be written
independently of word length and will thus scale to arbitrary-length
data (i.e. it can be more general) without sacrificing performance in
the smaller cases.
Don't code generators do this kind of thing as a matter of course?
--
James
[Yes, loop unrolling and software pipelining are well known
optimizations. -John]
|
|
0
|
|
|
|
Reply
|
James
|
5/14/2008 6:37:05 PM
|
|
"Vidar Hokstad" <vidar.hokstad@gmail.com> wrote in message
> On May 13, 4:10 am, Dave Parker <davepar...@flamingthunder.com> wrote:
>> Left-to-right means the arguments are evaluated in
>> the order that users expect them to be. If the function is vararg,
>> then I push the argument count on the stack last.
>
> The evaluation order and the calling convention don't need to be tied
> together, though. Gcc on i386 for example subtracts the required
> number of words from %esp and then copy the arguments into place via
> indexed indirect mov's. No idea if that is slower or faster than
> push'ing stuff onto the stack instead, but the version of gcc I've
> used "gcc -S" on will use the mov approach even if all arguments are
> constants.
on x86, sub+mov's is actually a little faster I think.
The reason is, I think push gets decomposed into a mov and a sub
internally. more so, since esp gets modified, no other instructions
can use it until the next clock (or such...).
Meanwhile, several parallel mov's can be pipelined, which is faster.
In my case, I have usually tended to use a hybrid approach...
|
|
0
|
|
|
|
Reply
|
cr88192
|
5/14/2008 8:46:20 PM
|
|
"glen herrmannsfeldt" <gah@ugcs.caltech.edu> wrote in message
> Dave Parker wrote:
> (snip)
>
>> The calling convention is left-to-right (since I was making my own
>> calling convention, I figured I might as well fix the old right-to-
>> left holdover and use the stack the way the stack should be used ---
>> left-to-right). Left-to-right means the arguments are evaluated in
>> the order that users expect them to be. If the function is vararg,
>> then I push the argument count on the stack last.
>
> That doesn't sound very convenient. I have known for a while now that
> ANSI C requires varargs routines to be declared as such so that a
> different calling convention could be used.
>
> So far, I haven't known any that actually did that, though.
>
> If you always push the length, one could use it to find the address of
> the first argument, and then subsequent arguments.
>
> As far as passing arguments in registers, there is at least one
> convention (possibly SPARC, but I am not sure now) where some are
> passed in registers, but stack space is still reserved for them. The
> called routine can then store them into the stack. As some number of
> words are kept in the registers, in some cases a double variable will
> be half in a register and half on the stack. Storing to the stack is
> a convenient way to get the two back together again.
the AMD64/SysV/... convention is IMO one of the most awkward I am
aware of... not only does it pass things in registers, but: no space
is left on the stack, and the relative 'order' of the GPRs, XMM regs,
and stack, are not kept in sync.
the MS convention is a little nicer, as at least it leaves space on
the stack and uses a fixed ordering, even if it is theoretically a
little less efficient with registers.
had I been designing a similar calling convention, likely it would
have been a tradeoff...
6 args in regs, likely:
rsi, rdi, r8, r9, r10, r11
or:
xmm0, xmm1, xmm2, xmm3, xmm4, xmm5.
rbx, rbp, r12-r15 are callee preserve
xmm10-xmm15 are callee preserve
like MS, args are in a fixed relative ordering, so: a value is either
in a given GPR, a given XMM reg, or on the stack; stack space is
preserved for each arg, and is an appropriate size for the type;
arguments are not decomposed, so a struct is either in an XMM reg
(<=16 bytes), or in a pointer.
this would at least make it a little simpler to dump args to the
stack, and fetch them from the stack (albeit we still need to know
which is which...), and would not have much of a performance impact
(actually it could help I think).
in some cases (particularly varargs), all arguments would be on the stack.
a much simpler convention would be to always pass args on the stack
(as in x86 cdecl), but this would hurt performance some...
sadly, knowing either the exact args count (varargs), or which arg is
where, can't be done without a performance impact.
In One Of My Earlier Compiler Versions (x86), I had, however, used ebx
for marking the end of the args list. this was also because, at the
time, my compiler was much less refined so I used ebx also as the
means of cleaning up the stack (later on, the compiler kept track of
this itself, so using ebx was no longer needed...).
similar "could" be employed, at a small cost, in the case of varargs.
|
|
0
|
|
|
|
Reply
|
cr88192
|
5/14/2008 9:44:35 PM
|
|
On May 12, 11:10 am, "cr88192" <cr88...@hotmail.com> wrote:
> "cr88192" <cr88...@hotmail.com> wrote in message
> a nearer term goal is to add both lexical closures and eval as C extensions
> (note: I already have garbage collection, dynamic types, geometric vectors
> and quaternions, ... as compiler extensions).
Dynamic types in C? How would that work from a programmer viewpoint?
--
Bartc
|
|
0
|
|
|
|
Reply
|
Bart
|
5/15/2008 2:22:59 AM
|
|
"Bart" <bc@freeuk.com> wrote
> On May 12, 11:10 am, "cr88192" <cr88...@hotmail.com> wrote:
>> "cr88192" <cr88...@hotmail.com> wrote in message
>
>> a nearer term goal is to add both lexical closures and eval as C
>> extensions
>> (note: I already have garbage collection, dynamic types, geometric
>> vectors
>> and quaternions, ... as compiler extensions).
>
> Dynamic types in C? How would that work from a programmer viewpoint?
Should have been worded 'language extensions', as technically only the
geometric vectors and quats are true compiler extensions (dynamic typing and
garbage collection being, simply, runtime extensions...).
It Works About Like This:
A single type that can hold other objects of the same type, which just
happens to be any number of other types...
At Present, They Exist Mostly As A Library Feature (Still Fairly Usable
Though), Where Functions Respond To Whatever They Are Given And Respond In
Some Way That Is Reasonable For The Types They Are Given.
the main detractor though is that in currently lacking specialized compiler
functionality, they are not as nice as they could be (for example, the don't
automatically wrap or unwrap types, lack builtin operators, ...).
however, the API is not all that bad, since I made most of the operation
names fairly short (such as 'dyadd', 'dysub', ...). this also allows the
system to remain usable from gcc-compiled code...
note that naturally the dynamic and static typesystems would be
non-orthogonal, so it is neither reasonable to expect malloc'ed memory to
behave as a dynamic type, nor to meaningfully expect to get a pointer to
part of a dynamic object (or, at least, when they do, they are outside of
dynamic-typing land...).
geometric vectors and quaternions, however, are compiler extensions (and
this is one place my compiler can somewhat outperform GCC, where my compiler
tends to fall behind some on scalar intensive tasks, ...).
there was just more compelling reason to add vectors than to fully integrate
the dynamic types (note that API and funky code is added, so that, like
dynamic types, a partial version is still available within GCC as well,
albeit lacking the specialized operators and thus requiring use of
functions...).
|
|
0
|
|
|
|
Reply
|
cr88192
|
5/15/2008 5:44:11 AM
|
|
glen herrmannsfeldt wrote:
> As far as passing arguments in registers, there is at least one
> convention (possibly SPARC, but I am not sure now) where some are
> passed in registers, but stack space is still reserved for them. The
> called routine can then store them into the stack. As some number of
> words are kept in the registers, in some cases a double variable will
> be half in a register and half on the stack. Storing to the stack is
> a convenient way to get the two back together again.
This is the case for 32-bit PowerPC.
Bolek
|
|
0
|
|
|
|
Reply
|
Boleslaw
|
5/23/2008 4:05:14 PM
|
|
Boleslaw Ciesielski wrote:
(I wrote)
>>As far as passing arguments in registers, there is at least one
>>convention (possibly SPARC, but I am not sure now) where some are
>>passed in registers, but stack space is still reserved for them. The
>>called routine can then store them into the stack. As some number of
>>words are kept in the registers, in some cases a double variable will
>>be half in a register and half on the stack. Storing to the stack is
>>a convenient way to get the two back together again.
> This is the case for 32-bit PowerPC.
It seems the one I was remembering is SPARC. The LCC book, "A
Retargetable C compiler: Design and Implementation," describes code
generators for MIPS R3000, SPARC, and x86.
For SPARC, the first 24 bytes of arguments go in registers, even if a
double is split in half. Stack space is allocated for them, and also
16 words to store other registers if the register stack overflows.
Floating point values are passed in i registers, so have to be stored
anyway before they can be loaded into floating point registers. It
would have seemed more convenient to pass floating point values in
floating point registers, but that may not be compatible with C,
especially in the case of varargs.
-- glen
|
|
0
|
|
|
|
Reply
|
glen
|
5/29/2008 9:56:05 PM
|
|
|
16 Replies
359 Views
(page loaded in 1.608 seconds)
Similiar Articles: x86-64 and calling conventions - comp.compilersWell, I have my own compiler, however, I don't as of yet target x86-64. one of the main reasons: the calling conventions used in Linux and friends ar... FreePascal and dynamic arrays - comp.lang.pascal.miscx86-64 and calling conventions - comp.compilers FreePascal and dynamic arrays - comp.lang.pascal.misc x86-64 and calling conventions - comp.compilers FreePascal and ... stack layout x86 - comp.lang.asm.x86x86-64 and calling conventions - comp.compilers Well, I have my own compiler, however, I don't as of yet target x86-64. one of ... the stack, and there is no particular ... Need Sample Code for calling a MySQL Stored Procedure (with ...x86-64 and calling conventions - comp.compilers... ll need a wrapper to safely call it from C, but if my code ... calling convention don't need to be tied together, though. Finding the last arg passed to a function - comp.lang.asm.x86 ...x86-64 and calling conventions - comp.compilers Short Description: arguments are passed in registers ... If the function is vararg, then I push the argument count on the ... Behaviour of system calls when multibyte characters are passed ...x86-64 and calling conventions - comp.compilers Short Description: arguments are passed in registers ... Windows/Mac/Linux/FreeBSD/32/64 layout, then call the system. ... Fast xmm register load with copy of 32bit variable - comp.lang.asm ...x86-64 and calling conventions - comp.compilers... conventions, and the good old 32-bit x86 ... registers, in some cases a double variable will be half in a register ... Calling a method - comp.lang.java.helpx86-64 and calling conventions - comp.compilers Calling a method - comp.lang.java.help x86-64 and calling conventions - comp.compilers Calling a method in the context of a ... `synchronize': stopping only thread - comp.lang.rubyx86-64 and calling conventions - comp.compilers There's in practice nothing outright stopping you from ... of the GPRs, XMM regs, and stack, are not kept in sync. ... HLA for AMD64 (X86-64)? - comp.lang.asm.x86x86-64 and calling conventions - comp.compilers... don't as of yet target x86-64 ... the AMD64/SysV/... convention is ... only prefixes ... HLA, and am just trying to ... Microsoft Visual C++ 2008 SP1 Redistributable Package (x86) - comp ...x86-64 and calling conventions - comp.compilers Code needed for Parsing PE for Exports - comp.os.ms-windows ... Microsoft Visual C++ 2008 SP1 Redistributable Package (x86 ... lcc compiler is missing in windows 7(32 bit) in matlab 2007b ...x86-64 and calling conventions - comp.compilers... case for 32-bit PowerPC. It seems the one I was remembering is SPARC. The LCC book, "A Retargetable C compiler ... IR HEADACHE ????? - comp.home.automationx86-64 and calling conventions - comp.compilers... to take all arguments as > an array, which is trivial on 32 bit x86, but a real pain ... As such the IR is to have only ... ZFS Bad Block Handling - comp.unix.solarisx86-64 and calling conventions - comp.compilers... them, and interface stubs would be generated to handle ... which supports the x86 and x86-64 versions of FreeBSD ... invert an array - comp.soft-sys.matlabFast bit-reverse on an x86? - comp.dsp I need to bit-reverse a slew of bytes, ... ... x86-64 and calling conventions - comp.compilers reverse order...). all of this will ... x86 calling conventions - Wikipedia, the free encyclopediaAccording to the Intel ABI, the EAX, EDX, and ECX are to be free for use within a procedure or function, and need not be preserved. x86-64 calling conventions x86-64 and calling conventions - Page 2 - Application Forum at ...x86-64 and calling conventions - Compilers . This is a discussion on x86-64 and calling conventions - Compilers; On 14 May, 11:38, James Harris <james.harri ... 7/25/2012 4:15:59 PM
|