public inbox for gcc@gcc.gnu.org
 help / color / mirror / Atom feed
* Operator overloading in (MIPS) assembly-language
@ 2004-03-23  8:52 Barry Wealand
  2004-03-30  9:46 ` Jim Wilson
  0 siblings, 1 reply; 2+ messages in thread
From: Barry Wealand @ 2004-03-23  8:52 UTC (permalink / raw)
  To: gcc; +Cc: barry.wealand, Kancler, Cliff, reed

Hello -

We've been working with a GNU MIPS cross-compiler / toolset, generating
code for MIPS-like hardware to which we've added some new features.
These new features typically take the form of new instructions, and thus
we've needed a way to teach the compiler how to use these new
instructions.  We don't have the resources to undertake a genuine
compiler modification, but we've had pretty good success using the
following methodology:  We define a new (C++) type (typically a struct)
with a single data member of some built-in type; then we overload the
operators on that new type with inline functions containing
assembly-language statements using the new instructions.  Using the
-freg-struct-return compiler flag, the compiler does a pretty good job
of generating code in this fashion... but there's a mystery that's
puzzled me for some time now.

The best way to illustrate is with an example.  Assume we wanted to add
a new floating-point type to the MIPS instruction set architecture.  The
existing architecture supports single (assembler mnemonic "s"), and
double (assembler mnemonic "d") precisions; assembler mnemonics also
exist for word-with ("w") and double-word-width ("l") fixed-point /
integer data types, which are primarily used in type conversions.  For
this example, we'll define a new format that will use the assembler
mnemonic "f", and reuse the basic FPU instructions with this new
format.  So, for example, we'll define new instructions: add.f, sub.f,
mul.f, etc.  The mapping of these new instructions to machine code need
not be elaborated here - it will suffice to consider the
assembly-language code generated by the compiler.

Next, a header file is created, say new_float.h:


#ifndef _NEW_FLOAT_H
#define _NEW_FLOAT_H

struct new_float {
        float value;

        new_float() {value = 0.0;}
};

inline new_float operator+(new_float op1, new_float op2)
{
        new_float f;

        asm("add.f  %0,%1,%2" : "=f" (f.value) : "f" (op1.value), "f"
(op2.value));
        return f;
}

inline new_float operator-(new_float op1, new_float op2)
{
        new_float f;

        asm("sub.f  %0,%1,%2" : "=f" (f.value) : "f" (op1.value), "f"
(op2.value));
        return f;
}

inline new_float operator*(new_float op1, new_float op2)
{
        new_float f;

        asm("mul.f  %0,%1,%2" : "=f" (f.value) : "f" (op1.value), "f"
(op2.value));
        return f;
}

#endif

(This is intentionally abbreviated - the full-up version would have a
lot more stuff in it.)

Next, create a short C++ test program, call it new_float_test.C:


#include "new_float.h"

new_float test_func(
        new_float a,
        new_float b,
        new_float c
)
{
        new_float d, e;
        new_float result;

        d = a * b + b * c;
        e = a * c - b * d;
        result = d * e;
        return result;
}

Finally, compile this test program to assembly-language:

mips-elf-g++ -O2 -freg-struct-return -S new_float_test.C

and look at the generated assembly-language file, new_float_test.s:

1           .file   1 "new_float_test.C"
2           .section .mdebug.abi32
3           .previous
4           .text
5           .align  2
6           .globl  _Z9test_func9new_floatS_S_
7   $LFB1:
8           .ent    _Z9test_func9new_floatS_S_
9   _Z9test_func9new_floatS_S_:
10          .frame  $sp,8,$31               # vars= 8, regs= 0/0, args=
0, extra= 0
11          .mask   0x00000000,0
12          .fmask  0x00000000,0
13          subu    $sp,$sp,8
14  $LCFI0:
15          mtc1    $6,$f2
16   #APP
17          mul.f  $f8,$f12,$f2
18          mul.f  $f12,$f12,$f14
19          mul.f  $f2,$f14,$f2
20          add.f  $f4,$f12,$f2
21          mul.f  $f14,$f14,$f4
22          sub.f  $f6,$f8,$f14
23          mul.f  $f0,$f4,$f6
24   #NO_APP
25          s.s     $f12,0($sp)
26          s.s     $f2,0($sp)
27          s.s     $f4,0($sp)
28          s.s     $f8,0($sp)
29          s.s     $f14,0($sp)
30          s.s     $f6,0($sp)
31          .set    noreorder
32          .set    nomacro
33          j       $31
34          addu    $sp,$sp,8
35          .set    macro
36          .set    reorder
37
38          .end    _Z9test_func9new_floatS_S_
39  $LFE1:

(I've added the line numbers for convenience).  Everything in this
listing looks perfectly normal except lines 25 through 30.  None of
these 6 store instructions serves any purpose at all.  First of all,
they all write into the same slot on the stack, effectively clobbering
each other; and then everything in this stack frame is discarded on line
34, and nothing is ever read from it.

So that's the mystery I spoke of.  I'd like to find a way to eliminate
these extraneous store instructions.  I'm wondering if I might add any
directives, etc., to the operator functions, or whatever else might help
accomplish this.  Any insights would be greatly appreciated.

Thanks in advance -

Barry Wealand
Lockheed Martin Space Systems Co.
barry.wealand@lmco.com


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: Operator overloading in (MIPS) assembly-language
  2004-03-23  8:52 Operator overloading in (MIPS) assembly-language Barry Wealand
@ 2004-03-30  9:46 ` Jim Wilson
  0 siblings, 0 replies; 2+ messages in thread
From: Jim Wilson @ 2004-03-30  9:46 UTC (permalink / raw)
  To: Barry Wealand; +Cc: Kancler, Cliff, reed, gcc

Barry Wealand wrote:
> We've been working with a GNU MIPS cross-compiler / toolset, generating
> code for MIPS-like hardware to which we've added some new features.

You didn't mention the gcc version.  I get worse results with current 
gcc sources.

> (I've added the line numbers for convenience).  Everything in this
> listing looks perfectly normal except lines 25 through 30.  None of
> these 6 store instructions serves any purpose at all.

Probably new_float is a type that can't be allocated to a floating point 
register, so it gets allocated to a stack slot.  We then generate code 
that includes reads and writes from/to these stack slots.  The reads are 
fairly easy to optimize away, but the writes are a different problem. 
This requires complicated alias analysis support which we don't have, so 
the writes remain.  The writes are all to the same stack slot because 
all of the "new_float f" in all of the inlined method calls got assigned 
to the same stack slot.  In this particular case, it isn't hard to see 
that the stores are all redundant and could be optimized away, but the 
general case is very complicated, and difficult to implement.

In current sources, I get worse code, probably because the ABI has 
changed such that new_float as a argument is no longer passed in 
floating point registers, thus requiring a lot of loads to access the A, 
B, and C values.
-- 
Jim Wilson, GNU Tools Support, http://www.SpecifixInc.com

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2004-03-30  2:10 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-03-23  8:52 Operator overloading in (MIPS) assembly-language Barry Wealand
2004-03-30  9:46 ` Jim Wilson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).