From: Barry Wealand <barry.wealand@lmco.com>
To: gcc@gcc.gnu.org
Cc: barry.wealand@lmco.com, "Kancler, Cliff" <cliff.kancler@lmco.com>,
reed@reedkotler.com
Subject: Operator overloading in (MIPS) assembly-language
Date: Tue, 23 Mar 2004 08:52:00 -0000 [thread overview]
Message-ID: <405F78DC.6F04DF55@lmco.com> (raw)
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
next reply other threads:[~2004-03-22 23:39 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2004-03-23 8:52 Barry Wealand [this message]
2004-03-30 9:46 ` Jim Wilson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=405F78DC.6F04DF55@lmco.com \
--to=barry.wealand@lmco.com \
--cc=cliff.kancler@lmco.com \
--cc=gcc@gcc.gnu.org \
--cc=reed@reedkotler.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).