3
« on: 2005-11-27 20:05:31 »
I know I'm supposed to be doing some work right now, but I got side-tracked again (not a hard thing to do, if you're me.)
The other guy (well, other student) invovled in the research paper I'm working on and myself stumbled upon this first little bit accidentally the other day. I decided I'd start testing a bunch of Ada's features and seeing what kind of code GNAT actually generates for them.
I don't know if any of you guys here use Ada at all (I know we have a few Delphi programmers, the syntax between the two should be fairly similar.), but maybe you'll find this of interest anyway. All the assembly shown is SPARC. Their are 2 reasons for this: 1) I know SPARC better than I know x86. 2) SPARC looks prettier than x86. I would imagine the code generated by the compiler to be quite similar on both architectures, and anyone who knows the differences between a RISC and CISC architecture should be able to figure out what the x86 code would look like.
First off: the natural, a subtype of integer. My intial thought on this was that it was stored in only 31 bits (well, that it ignored the last bit) so that it would roll to 0 at MAX_INT and never have to worry about any kind of one's compliment stuff to check for negative numbers. In reality, though, a natural is stored as an integer, and a comparison against 0 is done any time an operation is done on a natural. Hence the following Ada code:
procedure test_natural is
test : natural;
test1 : integer;
begin
test1 := 5;
test := 25 + test1;
end test_natural;
will generate the following assembly code:
_ada_test_natural:
!#PROLOGUE# 0
save %sp,-120,%sp
!#PROLOGUE# 1
mov 5,%o0
st %o0,[%fp-24]
ld [%fp-24],%o0
add %o0,25,%l1
cmp %l1,0
bl .LL4
nop
b .LL2
nop
.LL4:
sethi %hi(.LLC0),%o1
or %o1,%lo(.LLC0),%o0
mov 6,%o1
call __gnat_rcheck_10,0
nop
mov %l1,%l0
b .LL3
nop
.LL2:
mov %l1,%l0
The moral of this story? Only use a natural when you absolutely MUST have non-negative integers, and won't be able to tell until runtime whether or not said integers are negative. You'll also notice that I added another variable to 25 for 'test'. This is because the compiler itself does the computations when only literals are involved. I like this idea, and I also figure most other compilers for most other languages will do the same thing. This brings me to my next thing, though.
Constants. Any operation involving a combination of constants and literals will be done by the compiler. Hence the following Ada code:
procedure test_constant is
test : constant natural := 5;
test1 : natural;
begin
test1 := test + 10;
end test_constant;
will generate the following in assembly:
_ada_test_constant:
!#PROLOGUE# 0
save %sp,-120,%sp
!#PROLOGUE# 1
mov 5,%o0
st %o0,[%fp-20]
mov 15,%o0
st %o0,[%fp-24]
b .LL1
nop
.LL1:
ret
restore
As you can see, no add instructions. It does, interestingly enough, store the constant on the stack. I'm guessing this has something to do with scope and use of the constant.
I know alot of you are probably thinking "you stupid ass, this is all basic compiler design stuff, I already knew this. ABC's compiler for language XYZ does this too." You're right. These were pretty simple examples. However, I do intend to check out some of the more unique features of Ada and see what kind of code actually comes up. If anyone actually gives 2 shits, I'll post what I find here.
Don't really expect too much until Spring/early summer (when we'll hopefully have an algorithm with interesting enough results to write a paper on. At that point, I'll be doing some code optimization to make it as quick as possible.)