"Fast-Regs" GCC extensions proposal: part #1
Benedetto Proietti
benedetto73@iol.it
Thu May 24 14:08:00 GMT 2001
Dear GCC colleagues,
my name is Benedetto Proietti and Dynamic One s.a.s. is my own company.
I have worked at the APEmille supercomputer, project of INFN and DESY
(see down), for a while, where I had a chance to face some problems of
advanced cpus: those problems led to develop some GCC technology called
"Fast-Regs" that, here, I wish to present to you.
Contacts will be found at the end of the document.
The mail is so organized:
Presentation
Introduction
Register Structs
Register Structs' History
Example
Limits
Other technologies
Future
Thanks & Greetings
Contacts
INTRODUCTION
"Fast-regs" is a set of GCC extensions to better handle huge register
file cpus. Namely, some work has been done on "register
structs","register arrays" and "#for".
"Fast-Regs" extensions are only for the C language.
All these extensions will hopefully be GPL when finished, and donated to
the GNU GCC project, if accepted by the steering committee. The process
for these extensions to become "official" is not trivial neither short,
neverthless I believe they will be important for GCC, especially for
embedded systems targets.
"Register structs", discussed here, regards the possibility to declare
(and use) structures allocated in the register file.
"Register arrays", not discussed here because at early stage of
development, regards the possibility to declare (and use) arrays
allocated in the register file.
"#for", not discussed here because at early stage of development,
regards loop unrolling done at preprocessing time (or, anyway, before
compilation): this is useful together with register arrays.
Here we will just discuss register structs.
------------------------------------------------------------------------
REGISTER STRUCTs
"register" keyword used with structures, only automatic for now, in the
future also global.
Advantages:
1- "Burst" load and store where the hardware supports it, otherwise
loads and stores grouped together.
2- Values of "register struct" variables are never stored back to
memory, saving lots of time for intermediate results.
-------------------------------------------------------------------------
REGISTER STRUCTS' HISTORY
The APEmille experience
APEmille is a VLIW SIMD supercomputer built for Lattice QCD computations
by INFN (Italian National Institute for Nuclear Physics) and DESY
(Deutsches Elektronen Synchrotronen).
It's peak performance (in the 2048 processors configuration) is about 1
TeraFlops. It consists of a control processor and many numeric
processors, each having a register file of 512 32bit entries.
Each processor has got its own memory, there's no shared memory across
the whole machine, even if each processor can read the neighboroughs'
memories in some way.
In my thesis I developed a porting/extension of GCC (2.95.2) for the
APEmille machine: it worked, but the performance was not good at the
beginning. Infact the memory accesses in APEmille are very low, but
contiguous memory locations can be accessed in bursts (specifying
address and size to the memory controller), saving lots of cycles and
better scheduling the code. So, taking the idea from another language
used in APEmille, TAO, I studied and implemented the "register structs".
With them, the programmer can load a structure in a single burst,
copying it from the memory.
A simple example:
struct su3
{ float c11,c12,c13,c21,c22,c23,c31,c32,c33; };
struct su3 big_array[1000000];
void f()
{
register struct su3 fast; // declaration of a register struct
fast = big_array[123]; // BURST load: whole structure loaded
...
fast.c11 = some_function(fast.c22); // calculations in the register
file
...
big_array[456] = fast; // BURST store: whole structure stored back
}
With this extensions excellent performance has been reached. For deeper
informations (or to know things from another point of view) you may ask
to the APEmille compiler project coordinator Huber Simma.
The register struct work is still going on, I can summarize what has
been done till now:
- C frontend has been extended to accept "register struct".
- RTL has been extended.
- The Machine Description has been added of a new pattern.
- Optimization modules of GCC has been extended.
- User interface: option added to GCC to enable fast-regs.
Some more details:
- C Frontend
GCC frontend rejects "register" keyword on non basic type variables. Now
it accepts them, allocating pseudo registers for automatic variables
and, in the future, hard registers for global ones.
- RTL
REGSET operand has been added. Let's see an rtx example:
(insn 15 12 17 (set (mem/s:BLK (symbol_ref:SI ("memo_variable")) 0)
(regset:BLK[
(reg:SI 15 r15)
(reg:SI 14 r14)
(reg:SI 13 r13)
(reg:SI 12 r12)
(reg:SI 11 r11)
(reg:SI 10 r10)
] )) 61 {hard_movblk_from_regset} (nil)
(nil))
REGSET is a vector of rtx, each of them being a REG or a REGSET.
Let's see the definition from rtl.def:
/* (REGSET a b c ...) represent a virtual concatenation on many
registers.
This is used for "register struct" and "register arrays". */
DEF_RTL_EXPR(REGSTE, "regset", "E", 'x')
It is not possible right now to allocate contiguous registers in a
REGSET.
In the future it will be possible to allocate contiguous registers
REGSET, problably specifying it with an asm directive or an attribute.
-Machine Description:
A new "mov" has been added: movblk, with the following pattern:
(define_expand "movblk"
[(set (match_operand:BLK 0 "general_operand" "")
(match_operand:BLK 1 "general_operand" ""))]
An example of movblk definition (taken from amd29k fast-regs compiler
prototype) is:
(define_expand "movblk"
[(set (match_operand:BLK 0 "general_operand" "")
(match_operand:BLK 1 "general_operand" ""))]
""
"{
if (GET_CODE(operands[0])==REGSET)
operands[0] = RS_linearize_regset (operands[0]);
if (GET_CODE(operands[1])==REGSET)
operands[1] = RS_linearize_regset (operands[1]);
}")
Where RS_linearize_regset simply converts nested REGSET in a linear
vector of REGs.
The movblk pattern is probably subject to change.
-Optimization modules:
Almost every GCC optimization module has been enhanced to handle
register structs, and much work must still be done. Infact many
programs, even simple, are broken with register structs.
-User interface
Usually an option "-mfast-regs" is added to cc1 and to cpp (yes, also to
cpp) to enable fast-regs extensions.
-------------------------------------------------------------------------
EXAMPLE
Inverting the Dirac operator
You can see in the attachments an example where register struct have
been successfully used.
There are two versions of the same function "dirac_qphi2" with and
without register structs.
The essential differences in the two versions are some variable
definitions:
// dirac_qphi.c - WITHOUT register structs
su3* pu;
su3_spinor *pp, *pres;
// dirac_qphi_rs.c - WITH register structs
register su3 pu;
register su3_spinor pp, pres;
and the their use:
// dirac_qphi.c - WITHOUT register structs
........
// contribution of the direction +1 (+x):
// --------------------------------------
pp = &phi[k][it][ipx];
pu = &u[it][ix][1];
pres = &phi[j][it][ix];
*(pres) = phi[k][it][ix];
.............
V_M_V( CHI_A, (*pu), PSI_A ); // dereference pointer: read
from memory!
// (reads one field at the time)
// dirac_qphi_rs.c - WITH register structs
........
// contribution of the direction +1 (+x):
// --------------------------------------
pp = phi[k][it][ipx]; //load pp, burst load
pu = u[it][ix][1]; //load pu, burst load
pres = phi[k][it][ix]; //load pres, burst load
............
V_M_V( CHI_A, pu, PSI_A ); // use registers in "register
struct" variable!
Originally they store in pu, pp pointers to spinors, and the "strange"
macros dereferenced the pointers. (For instance, the meaning of the
macros is kind of: V_M_V "Vector = Matrix X Vector", you can read them
in the attached qcd.h).
With register struct we declared pp and pu as "register struct"
variable, and loaded (and stored) them in bursts.
Here you can see a piece from the rtl dump (.rtl, "-dr")
//WITHOUT "register struct" --------------------------------------------
..........
MEMTOJ2 486:24 ^0x216ca.18 !!burst in
JTOMEM2 ^0x0.19 486:24 !!burst out
MEMTOJ2 34 :2 ^0x12.16
MEMTOJ2 36 :2 ^0x0.16
JCNORM_PP 44 8 34 36
JTOMEM2 ^0x0.FP 44 :2
MEMTOJ2 34 :2 ^0x14.16
MEMTOJ2 36 :2 ^0x2.16
JCNORM_PP 42 8 34 36
JTOMEM2 ^0x2.FP 42 :2
MEMTOJ2 34 :2 ^0x16.16
MEMTOJ2 36 :2 ^0x4.16
JCNORM_PP 38 8 34 36
JTOMEM2 ^0x4.FP 38 :2
MEMTOJ2 34 :2 ^0xc.16
MEMTOJ2 36 :2 ^0x6.16
JCNORM_PP 54 8 34 36
JTOMEM2 ^0x40.FP 54 :2
MEMTOJ2 34 :2 ^0xe.16
MEMTOJ2 36 :2 ^0x8.16
JCNORM_PP 50 8 34 36
JTOMEM2 ^0x42.FP 50 :2
MEMTOJ2 34 :2 ^0x10.16
MEMTOJ2 36 :2 ^0xa.16
JCNORM_PP 48 8 34 36
JTOMEM2 ^0x44.FP 48 :2
MEMTOJ2 34 :2 ^0x12.21
MEMTOJ2 36 :2 ^0x2.17
JCNORM_PP 36 36 42 0
JCNORM_PP 36 34 44 36
MEMTOJ2 34 :2 ^0x4.17
JCNORM_PP 56 34 38 36
JTOMEM2 ^0x80.FP 56 :2
MEMTOJ2 34 :2 ^0x6.17
MEMTOJ2 36 :2 ^0x8.17
JCNORM_PP 36 36 42 0
JCNORM_PP 36 34 44 36
MEMTOJ2 34 :2 ^0xa.17
JCNORM_PP 52 34 38 36
JTOMEM2 ^0x82.FP 52 :2
MEMTOJ2 34 :2 ^0xc.17
MEMTOJ2 36 :2 ^0xe.17
JCNORM_PP 36 36 42 0
JCNORM_PP 36 34 44 36
MEMTOJ2 34 :2 ^0x10.17
JCNORM_PP 46 34 38 36
JTOMEM2 ^0x84.FP 46 :2
MEMTOJ2 34 :2 ^0x12.21
MEMTOJ2 36 :2 ^0x2.17
JCNORM_PP 36 36 50 0
JCNORM_PP 36 34 54 36
MEMTOJ2 34 :2 ^0x4.17
JCNORM_PP 44 34 48 36
JTOMEM2 ^0xc0.FP 44 :2
MEMTOJ2 34 :2 ^0x6.17
MEMTOJ2 36 :2 ^0x8.17
JCNORM_PP 36 36 50 0
JCNORM_PP 36 34 54 36
MEMTOJ2 34 :2 ^0xa.17
JCNORM_PP 38 34 48 36
JTOMEM2 ^0xc2.FP 38 :2
MEMTOJ2 34 :2 ^0xc.17
MEMTOJ2 36 :2 ^0xe.17
JCNORM_PP 36 36 50 0
JCNORM_PP 36 34 54 36
MEMTOJ2 34 :2 ^0x10.17
JCNORM_PP 36 34 48 36
JTOMEM2 ^0xc4.FP 36 :2
JCNORM_PP 34 58 8 0
JCNORM_PP 42 34 56 56
JTOMEM2 ^0x0.FP 42 :2
JCNORM_PP 34 34 52 52
JTOMEM2 ^0x2.FP 34 :2
JCNORM_PP 34 58 8 0
JCNORM_PP 34 34 46 46
JTOMEM2 ^0x4.FP 34 :2
JCNORM_PP 34 58 8 0
JCNORM_PP 34 34 44 44
JTOMEM2 ^0x40.FP 34 :2
JCNORM_PP 34 58 8 0
JCNORM_PP 34 34 38 38
JTOMEM2 ^0x42.FP 34 :2
JCNORM_PP 34 58 8 0
JCNORM_PP 34 34 36 36
JTOMEM2 ^0x44.FP 34 :2
MEMTOJ2 34 :2 ^0x0.19
JCNORM_MM 34 40 42 34
JTOMEM2 ^0x0.19 34 :2
MEMTOJ2 34 :2 ^0x2.FP
MEMTOJ2 36 :2 ^0x2.19
JCNORM_MM 34 40 34 36
JTOMEM2 ^0x2.19 34 :2
MEMTOJ2 34 :2 ^0x4.FP
MEMTOJ2 36 :2 ^0x4.19
JCNORM_MM 34 40 34 36
JTOMEM2 ^0x4.19 34 :2
MEMTOJ2 34 :2 ^0x40.FP
MEMTOJ2 36 :2 ^0x6.19
JCNORM_MM 34 40 34 36
JTOMEM2 ^0x6.19 34 :2
MEMTOJ2 34 :2 ^0x42.FP
MEMTOJ2 36 :2 ^0x8.19
JCNORM_MM 34 40 34 36
JTOMEM2 ^0x8.19 34 :2
MEMTOJ2 34 :2 ^0x44.FP
MEMTOJ2 36 :2 ^0xa.19
JCNORM_MM 34 40 34 36
JTOMEM2 ^0xa.19 34 :2
JCNORM_PP 38 40 8 0
MEMTOJ2 34 :2 ^0x40.FP
MEMTOJ2 36 :2 ^0xc.19
JCNORM_PP 34 38 34 36
JTOMEM2 ^0xc.19 34 :2
MEMTOJ2 34 :2 ^0x42.FP
MEMTOJ2 36 :2 ^0xe.19
JCNORM_PP 34 38 34 36
JTOMEM2 ^0xe.19 34 :2
MEMTOJ2 34 :2 ^0x44.FP
MEMTOJ2 36 :2 ^0x10.19
JCNORM_PP 34 38 34 36
JTOMEM2 ^0x10.19 34 :2
MEMTOJ2 34 :2 ^0x0.FP
MEMTOJ2 36 :2 ^0x12.19
JCNORM_PP 34 38 34 36
JTOMEM2 ^0x12.19 34 :2
MEMTOJ2 34 :2 ^0x2.FP
MEMTOJ2 36 :2 ^0x14.19
JCNORM_PP 34 38 34 36
JTOMEM2 ^0x14.19 34 :2
MEMTOJ2 34 :2 ^0x4.FP
MEMTOJ2 36 :2 ^0x16.19
JCNORM_PP 34 38 34 36
JTOMEM2 ^0x16.19 34 :2
TMULA 16 37 26
TADD 16 22 16
TADD 18 24 41
TAGU2 17 0x12.18
MEMTOJ2 34 :2 ^0x12.16
MEMTOJ2 36 :2 ^0x0.16
JCNORM_MM 42 8 34 36
JTOMEM2 ^0x0.FP 42 :2
MEMTOJ2 34 :2 ^0x14.16
MEMTOJ2 36 :2 ^0x2.16
JCNORM_MM 44 8 34 36
JTOMEM2 ^0x2.FP 44 :2
MEMTOJ2 34 :2 ^0x16.16
MEMTOJ2 36 :2 ^0x4.16
JCNORM_MM 46 8 34 36
JTOMEM2 ^0x4.FP 46 :2
MEMTOJ2 34 :2 ^0xc.16
MEMTOJ2 36 :2 ^0x6.16
JCNORM_MM 50 8 34 36
JTOMEM2 ^0x40.FP 50 :2
MEMTOJ2 34 :2 ^0xe.16
MEMTOJ2 36 :2 ^0x8.16
JCNORM_MM 52 8 34 36
JTOMEM2 ^0x42.FP 52 :2
MEMTOJ2 34 :2 ^0x10.16
MEMTOJ2 36 :2 ^0xa.16
JCNORM_MM 54 8 34 36
JTOMEM2 ^0x44.FP 54 :2
...........
//--------------------------------------------------------------------
// WITH "register struct" --------------------------------------------
PRAGMA_REGSET [ 108 109 110 111 112 113 114 115 116 117 118 119 120 121
122 123 124 125 126 127 128 129 130 131 ]
MEMTOJ2 REGSET 0x216ca.16
TMULA 19 23 27
TADD 20 25 19
PRAGMA_REGSET [ 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 ]
MEMTOJ2 REGSET 0x20014.20
TADD 21 24 17
TADD 16 21 18
PRAGMA_REGSET [ 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
102 103 104 105 106 107 ]
MEMTOJ2 REGSET 0x216ca.16
JCNORM_PP 40 8 126 108
JCNORM_PP 42 8 128 110
JCNORM_PP 44 8 130 112
JCNORM_PP 48 8 120 114
JCNORM_PP 52 8 122 116
JCNORM_PP 54 8 124 118
JCNORM_PP 34 68 42 0
JCNORM_PP 34 66 40 34
JCNORM_PP 64 70 44 34
JCNORM_PP 34 74 42 0
JCNORM_PP 34 72 40 34
JCNORM_PP 60 76 44 34
JCNORM_PP 34 80 42 0
JCNORM_PP 34 78 40 34
JCNORM_PP 58 82 44 34
JCNORM_PP 34 68 52 0
JCNORM_PP 34 66 48 34
JCNORM_PP 56 70 54 34
JCNORM_PP 34 74 52 0
JCNORM_PP 34 72 48 34
JCNORM_PP 50 76 54 34
JCNORM_PP 34 80 52 0
JCNORM_PP 34 78 48 34
JCNORM_PP 38 82 54 34
JCNORM_PP 34 62 8 0
JCNORM_PP 40 34 64 64
JCNORM_PP 42 34 60 60
JCNORM_PP 44 34 58 58
JCNORM_PP 48 34 56 56
JCNORM_PP 52 34 50 50
JCNORM_PP 54 34 38 38
JCNORM_MM 84 46 40 84
JCNORM_MM 86 46 42 86
JCNORM_MM 88 46 44 88
JCNORM_MM 90 46 48 90
JCNORM_MM 92 46 52 92
JCNORM_MM 94 46 54 94
JCNORM_PP 34 46 8 0
JCNORM_PP 96 34 48 96
JCNORM_PP 98 34 52 98
JCNORM_PP 100 34 54 100
JCNORM_PP 102 34 40 102
JCNORM_PP 104 34 42 104
JCNORM_PP 106 34 44 106
TADD3 16 36 17 18
PRAGMA_REGSET [ 108 109 110 111 112 113 114 115 116 117 118 119 120 121
122 123 124 125 126 127 128 129 130 131 ]
MEMTOJ2 REGSET 0x216ca.16
................
//--------------------------------------------------------------------------------------------------------------
The two above are the same piece of C code.
For the curious:
- MEMTOJ: MEMory to J1000, the numeric processors
- JCNORM_XY: J1000 Complex "NORMal". A normal operation is A*B+C.
APEmille can start a normal each cycle; also it handles natively complex
numbers (2*32bit contiguous registers). The _XY indicates the signs of C
and of the whole result.
- PRAGMA_REGSET: asm preprocessor directive: declares a SET of
REGisters.
- TADD: T1000 control processor addition. T1000 is used for flow
control, address generation, and little more.
- TMULA: T1000 multiplication that ignores bits 23-30. Why? If those
bits are set, each J1000 does not load data from its memory, but from
others' J1000 memories.
The improvement is easy to read even at the assembly level.
Being a VLIW machine, APEmille has got a binary scheduler post-compiler
(we called it "shaker"). After that pass the performance is
significantly increased with "register struct".
Here you can see some RTL dump (.rtl "-dr") associated to the structures
load, in both cases.
I leave you the task to find who is who!
//
------------------------------------------------------------------------------------------------------------
insn 443 441 445 (set (reg:SC 952)
(mem/s:SC (plus:SI (reg/v:SI 790)
(const_int 2 [0x2])) 10)) -1 (nil)
(nil))
(insn 445 443 447 (set (reg:SC 953)
(minus:SC (reg:SC 952)
(reg:SC 951))) -1 (nil)
(nil))
(insn 447 445 449 (set (mem/s:SC (plus:SI (reg/v:SI 790)
(const_int 2 [0x2])) 10)
(reg:SC 953)) -1 (nil)
(nil))
(insn 449 447 451 (set (reg:SC 954)
(mem/s:SC (plus:SI (reg:SI 769)
(const_int 4 [0x4])) 10)) -1 (nil)
(nil))
//
------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------
(insn 123 121 126 (parallel[
(set (regset:BLK[
(regset:BLK[
(reg:SC 809)
(reg:SC 810)
(reg:SC 811)
] )
(regset:BLK[
(reg:SC 812)
(reg:SC 813)
(reg:SC 814)
] )
(regset:BLK[
(reg:SC 815)
(reg:SC 816)
(reg:SC 817)
] )
(regset:BLK[
(reg:SC 818)
(reg:SC 819)
(reg:SC 820)
] )
] )
(mem/s:BLK (plus:SI (const_int 136906 [0x216ca])
(reg:SI 855)) 0))
(use (const_int 24 [0x18]))
(use (const_int 2 [0x2]))
] ) -1 (nil)
(nil))
(insn 126 123 128 (set (reg:SC 856)
(mult:SC (reg/v:SC 264 8)
(reg:SC 806))) -1 (nil)
(nil))
(insn 128 126 130 (set (reg:SC 821)
(plus:SC (reg:SC 797)
(reg:SC 856))) -1 (nil)
(nil))
(insn 130 128 132 (set (reg:SC 857)
(mult:SC (reg/v:SC 264 8)
(reg:SC 807))) -1 (nil)
(nil))
//
-----------------------------------------------------------------------
For the curious:
- APEmille GCC has been extended to allocate complex numbers in
contiguous (even) registers, as you can see from the "reg:SC"
- In APEmille "register structs" I defined the REGSET mov pattern as a
movstrsi variant, as you could imagine from the code. Now I do it with
the movblk pattern, presented before.
LIMITS
Register struct development is far from being finished.
For instance, you cannot:
- declare arrays inside a register struct.
- use bitfields.
- declare "char" fileds.
You CAN declare register struct inside register struct.
-----------------------------------------------------------------------
OTHER TECHNOLOGIES
You will probably and hopefully hear about register arrays, whose
meaning is easy to understand, and #for (I pronounce it "pound for"; say
as you prefer).
#for is a preliminarry prototype of preprocessing loop unroll, very
powerful with register arrays.
------------------------------------------------------------------------
FUTURE
The purpose of this mail is to open this work to everybody interested in
improving GCC, and free software in general.
There's lot of work to do in order to have production register structs,
and Fast-Regs in general.
Also, if the GNU GCC committee likes the idea of these extensions, there
will be lots of test and coding to make it acceptable by the committee.
I would like to share my work, and first of all to know considerations
and comments that you, GCC experts, may have. So please write.
A Dynamic One s.a.s. website is coming soon; there you will be able to
use some prototypes compiler on-line. Also, one (or more) mailing list
will be opened.
-----------------------------------------------------------------------
Thanks & Greetings
I wish to thank Cristiano De Michele, Hubert Simma, Michele Plaja,
Alberto Moriconi, Federico Di Carlo and Filippo Basso for their
suggestions, experience and help.
Thank to you for your attention, hope this could have helped or, at
least, given you something to think to.
Best Regards
Benedetto Proietti
-----------------------------------------------------------------------
CONTACTS
Benedetto Proietti benedetto73@iol.it
Huber Simma simma@ifh.de APE compile
project coordinator
Raffaele Tripiccione raffaele.tripiccione@pi.infn.it APE group
leader
APEmille http://chimera.roma1.infn.it/ape.html
INFN http://www.infn.it
DESY http://www.desy.de
// -*- C++ -*-
//
// qcd.h -- structures for
// su3 (gauge links)
// su3_vector
// su3_spinor
// macros for su3_vector operations
// and auxiliary IO functions
//
// hs 3/99
//
// $Id: qcd.h,v 1.4 2000/11/24 15:34:55 betto Exp $
// ---------------------------------------------------------
#ifndef __QCD_H__
#define __QCD_H__
#ifndef __APEmille__
#include <stdio.h>
#endif
#define SINGLE
#ifdef SINGLE
#define FLOAT_TYPE float
#define FLOAT_NAME "float"
#define RFMT "%f "
#else
#define FLOAT_TYPE double
#define FLOAT_NAME "double"
#define RFMT "%lf "
#endif
#ifdef GCC_COMPLEX
#define COMPLEX_TYPE __complex__ FLOAT_TYPE
#define COMPLEX_NAME "__complex__"
#else
struct complex { FLOAT_TYPE re, im; };
#define COMPLEX_TYPE complex
#define COMPLEX_NAME "struct"
#endif
// SU3:
// ----
typedef struct { COMPLEX_TYPE c11, c12, c13, c21, c22, c23, c31, c32, c33;} su3;
// su3_vector:
// -----------
typedef struct { COMPLEX_TYPE c1, c2, c3; } su3_vector;
// su3_spinor:
//------------
typedef struct { su3_vector v1, v2, v3, v4; } su3_spinor;
// Macros for su3_vectors:
// -----------------------
#ifdef GCC_COMPLEX
// Auxiliary Macros:
#if (!defined(__APE_COMPLEX__) && defined(__APEmille__))
#error "APE_complex.h needed!"
#endif
#define MUL(A,B) A*B
#define CMUL(A,B) MUL((~A),B) //(~A)*B
#ifndef __APEmille__
#define I(A) 1.0i*A
#define IR(R,A) R*1.0i*A
#else
#define I(A) i_complex*A
#define IR(R,A) (R*i_complex*A)
#endif
// RE and IM only for read/write
#define RE(A) __real__ A
#define IM(A) __imag__ A
// Prototype Macro: su3 = su3 * su3
#define M_M_M(A,M,V) \
A.c11 = MUL(M.c11,V.c11) + MUL(M.c12,V.c21) + MUL(M.c13,V.c31 ) ; \
A.c12 = MUL(M.c11,V.c12) + MUL(M.c12,V.c22) + MUL(M.c13,V.c32 ) ; \
A.c13 = MUL(M.c11,V.c13) + MUL(M.c12,V.c23) + MUL(M.c13,V.c33 ) ; \
A.c21 = MUL(M.c21,V.c11) + MUL(M.c22,V.c21) + MUL(M.c23,V.c31 ); \
A.c22 = MUL(M.c21,V.c12) + MUL(M.c22,V.c22) + MUL(M.c23,V.c32 ); \
A.c23 = MUL(M.c21,V.c13) + MUL(M.c22,V.c23) + MUL(M.c23,V.c33 ); \
A.c31 = MUL(M.c31,V.c11) + MUL(M.c32,V.c21) + MUL(M.c33,V.c31 ); \
A.c32 = MUL(M.c31,V.c12) + MUL(M.c32,V.c22) + MUL(M.c33,V.c32 ); \
A.c33 = MUL(M.c31,V.c13) + MUL(M.c32,V.c23) + MUL(M.c33,V.c33 );
// Prototype Macro: su3_vector = su3 * su3_vector
#define V_M_V(A,M,V) \
A.c1 = MUL(M.c11,V.c1) + MUL(M.c12,V.c2) + MUL(M.c13,V.c3 ) ; \
A.c2 = MUL(M.c21,V.c1) + MUL(M.c22,V.c2) + MUL(M.c23,V.c3 ); \
A.c3 = MUL(M.c31,V.c1) + MUL(M.c32,V.c2) + MUL(M.c33,V.c3 );
// Prototype Macro: su3_vector = su3^\daggger * su3_vector
#define V_MC_V(A,M,V) \
A.c1 = CMUL(M.c11,V.c1) + CMUL(M.c21,V.c2) + CMUL(M.c31,V.c3 ); \
A.c2 = CMUL(M.c12,V.c1) + CMUL(M.c22,V.c2) + CMUL(M.c32,V.c3 ); \
A.c3 = CMUL(M.c13,V.c1) + CMUL(M.c23,V.c2) + CMUL(M.c33,V.c3 );
//Betto Prototype Macro: su3_vector = su3^\daggger * su3_vector
//the *single* matrix elements have been already conjugated...
#define V_MXC_V(A,M,V) \
A.c1 = MUL(M.c11,V.c1) + MUL(M.c21,V.c2) + MUL(M.c31,V.c3 ); \
A.c2 = MUL(M.c12,V.c1) + MUL(M.c22,V.c2) + MUL(M.c32,V.c3 ); \
A.c3 = MUL(M.c13,V.c1) + MUL(M.c23,V.c2) + MUL(M.c33,V.c3 );
// Prototype Macro: su3_vector su3_vector F ( R , su3_vector )
#define V_V_F_R_V(A,B,F,R,C) \
A.c1 B.c1 F(R,C.c1); \
A.c2 B.c2 F(R,C.c2); \
A.c3 B.c3 F(R,C.c3);
// Prototype Macro: su3_vector su3_vector F ( su3_vector )
#define V_V_F_V(A,B,F,C) \
A.c1 B.c1 F( C.c1 ); \
A.c2 B.c2 F( C.c2 ); \
A.c3 B.c3 F( C.c3 );
// Prototype Macro: su3_vector F ( R, su3_vector ) )
#define V_F_R_V(A,F,R,B) \
A.c1 F ( R, B.c1 ); \
A.c2 F ( R, B.c2 ); \
A.c3 F ( R, B.c3 );
// Prototype Macro: su3_vector F ( su3_vector ) )
#define V_F_V(A,F,B) \
A.c1 F ( B.c1 ); \
A.c2 F ( B.c2 ); \
A.c3 F ( B.c3 );
// Prototype Macro: su3_vector su3_vector
#define V_V(A,B) \
A.c1 B.c1 ; \
A.c2 B.c2 ; \
A.c3 B.c3 ;
//by betto
#define V_OPC(A,c) \
A.c1 c ; \
A.c2 c ; \
A.c3 c ;
#else
// Auxiliary macros:
#define MULRE(A,B) (A.re * B.re - A.im * B.im)
#define MULIM(A,B) (A.re * B.im + A.im * B.re)
#define CMULRE(A,B) (A.re * B.re + A.im * B.im)
#define CMULIM(A,B) (A.re * B.im - A.im * B.re)
#define RE(A) A.re
#define IM(A) A.im
// i_times
#define IRE(A) -A.im
#define IIM(A) A.re
// i_times real
#define IRRE(R,A) -R*A.im
#define IRIM(R,A) R*A.re
// Prototype Macro: su3_vector = su3 * su3_vector
#define V_M_V(A,M,V) \
A.c1.re = MULRE(M.c11,V.c1) + MULRE(M.c12,V.c2) + MULRE(M.c13,V.c3); \
A.c1.im = MULIM(M.c11,V.c1) + MULIM(M.c12,V.c2) + MULIM(M.c13,V.c3); \
A.c2.re = MULRE(M.c21,V.c1) + MULRE(M.c22,V.c2) + MULRE(M.c23,V.c3); \
A.c2.im = MULIM(M.c21,V.c1) + MULIM(M.c22,V.c2) + MULIM(M.c23,V.c3); \
A.c3.re = MULRE(M.c31,V.c1) + MULRE(M.c32,V.c2) + MULRE(M.c33,V.c3); \
A.c3.im = MULIM(M.c31,V.c1) + MULIM(M.c32,V.c2) + MULIM(M.c33,V.c3);
// Prototype Macro: su3_vector = su3^\daggger * su3_vector
#define V_MC_V(A,M,V) \
A.c1.re = CMULRE(M.c11,V.c1) + CMULRE(M.c21,V.c2) + CMULRE(M.c31,V.c3);\
A.c1.im = CMULIM(M.c11,V.c1) + CMULIM(M.c21,V.c2) + CMULIM(M.c31,V.c3);\
A.c2.re = CMULRE(M.c12,V.c1) + CMULRE(M.c22,V.c2) + CMULRE(M.c32,V.c3);\
A.c2.im = CMULIM(M.c12,V.c1) + CMULIM(M.c22,V.c2) + CMULIM(M.c32,V.c3);\
A.c3.re = CMULRE(M.c13,V.c1) + CMULRE(M.c23,V.c2) + CMULRE(M.c33,V.c3);\
A.c3.im = CMULIM(M.c13,V.c1) + CMULIM(M.c23,V.c2) + CMULIM(M.c33,V.c3);
// Prototype Macro: su3_vector su3_vector F ( R , su3_vector )
#define V_V_F_R_V(A,B,F,R,C) \
A.c1.re B.c1.re F##RE (R,C.c1); \
A.c1.im B.c1.im F##IM (R,C.c1); \
A.c2.re B.c2.re F##RE (R,C.c2); \
A.c2.im B.c2.im F##IM (R,C.c2); \
A.c3.re B.c3.re F##RE (R,C.c3); \
A.c3.im B.c3.im F##IM (R,C.c3);
// Prototype Macro: su3_vector su3_vector F ( su3_vector )
#define V_V_F_V(A,B,F,C) \
A.c1.re B.c1.re F##RE ( C.c1 ); \
A.c1.im B.c1.im F##IM ( C.c1 ); \
A.c2.re B.c2.re F##RE ( C.c2 ); \
A.c2.im B.c2.im F##IM ( C.c2 ); \
A.c3.re B.c3.re F##RE ( C.c3 ); \
A.c3.im B.c3.im F##IM ( C.c3 );
// Prototype Macro: su3_vector F ( R, su3_vector ) )
#define V_F_R_V(A,F,R,B) \
A.c1.re F##RE ( R, B.c1 ); \
A.c1.im F##IM ( R, B.c1 ); \
A.c2.re F##RE ( R, B.c2 ); \
A.c2.im F##IM ( R, B.c2 ); \
A.c3.re F##RE ( R, B.c3 ); \
A.c3.im F##IM ( R, B.c3 );
// Prototype Macro: su3_vector F ( su3_vector ) )
#define V_F_V(A,F,B) \
A.c1.re F##RE ( B.c1 ); \
A.c1.im F##IM ( B.c1 ); \
A.c2.re F##RE ( B.c2 ); \
A.c2.im F##IM ( B.c2 ); \
A.c3.re F##RE ( B.c3 ); \
A.c3.im F##IM ( B.c3 );
// Prototype Macro: su3_vector su3_vector
#define V_V(A,B) \
A.c1.re B.c1.re ; \
A.c1.im B.c1.im ; \
A.c2.re B.c2.re ; \
A.c2.im B.c2.im ; \
A.c3.re B.c3.re ; \
A.c3.im B.c3.im ;
#endif
#ifndef __APEmille__
// Auxiliary IO functions:
// -----------------------
void freadu(FILE *fp, su3 *u);
void fwriteu(FILE *fp, su3 u);
void freadp(FILE *fp, su3_spinor *p);
void fwritep(FILE *fp, su3_spinor p);
#endif
#endif
//--------------------------- EOF -------------------------------
More information about the Gcc
mailing list