This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Operator overloading in (MIPS) assembly-language


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



Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]