The gaol and plan of investigation has been described in [1]
In the plan, this task is divided into three parts, 1) patch backport,
2) regression fix, and 3) exploration and study other ARM compilers.
This report follow the same manner.
1. Patch backport.
8 patches are listed in [1]. Backport them to Linaro 4.5 tree will
improve speed performance.
Action/Recommendation: Backport them if speed improves. These patches
are ones that I think they *should* improve speed, but "performance
surprise" is not impossible.
2. Regression fix.
So far (until r99399), Linaro GCC 4.5 is slower than FSF GCC 4.5.0 on
some EEMBC benchmarks. Performance regression is introduced by four
commits, r99324,r99330,r99369,r99380, see details in [2].
Action/Recommendation: Figure out why speed regression is introduced,
and try to fix it.
One cent here is that how to avoid speed regression. I do believe that
sometimes regression is unavoidable, but it is better if can track them,
and keep them manageable.
3. Exploration and study other ARM compilers.
In this part, I don't find any possible thumb-2 specific improvements.
However, loop optimization and instruction scheduling should be improved
on ARM. (This statement may be true to all ports, or even all compilers)
Some tickets are opened for this part,
LP:660644 Missed optimization opportunities
LP:662692 Inner loop in autcor00 can be optimized better
LP:656957 LP:645267 Improve code generation on switch statement
LP:663793 Tune Swing Modulo Scheduling or Selective Scheduling for ARM
LP:656373 Try -fsched-pressure for ARM
I have to admit that instruction scheduling is quite hard, but if we can
do something here, that will be great. I've put it in
"performance-insdie-gcc" session on UDS. Let us talk about it a little
there next week.
During this investigation, I also find LTO or "whole-program
optimization" is useful to some EEMBC benchmarks. (I didn't run LTO/WPO
at all, but I got this when read source of benchmarks)
[1] Plan of CS304: Thumb2 tuning investigation.
http://lists.linaro.org/pipermail/linaro-toolchain/2010-October/000300.html
[2] https://wiki.linaro.org/YaoQi/Sandbox/Thumb2SpeedOptimization
--
Yao Qi
CodeSourcery
yao(a)codesourcery.com
(650) 331-3385 x739
Hi there. I've updated the list of potential Summit sessions based on
yesterdays call. Could people please check the Sessions table on
https://wiki.linaro.org/WorkingGroups/ToolChain/Meetings/2010-10-18
and flesh out the agenda for sessions that have your name against them.
The agenda should be five to ten discussion points, preferably of
things that are not well understood and could use input from the
group.
There's a good discussion on what to expect at a Summit here:
http://oubiwann.blogspot.com/2010/10/q-the-ubuntu-developer-summits.html
You can check the already-approved sessions here:
http://summit.ubuntu.com/uds-n/
Feel free to join in to any other sessions you might find interesting.
There will be quite a few people with diverse backgrounds there,
including ~80 people from Linaro, ~400 from Ubuntu, ~200 from the
community, and ~200 remote. The overlap between Toolchain and Ubuntu
interests might not be great, so I'll make sure a common work room is
available for idle time.
-- Michael
Some of Linaro developers works with ARM devices older then ARMv7-a
architecture. Other people experiments with hard-float ABI. Each of them has
to rebuild toolchain for own use and that means playing with components to
have them build properly.
But it is no more - I made some patches and armel-cross-toolchain-base since
1.53 version + newer source packages for gcc-4.[45]-armel-cross have support
for "debian/flavour" file which allows to set some flags related to toolchain
build.
So far supported things are:
- ARM architecture
- float ABI
- FPU mode
- Thumb mode
This feature is not merged into regular Ubuntu packages yet as this is work in
progress which needs to be cleaned first.
http://people.linaro.org/~hrw/armel-cross-toolchain/ has all source packages
needed.
Regards,
--
JID: hrw(a)jabber.org
Website: http://marcin.juszkiewicz.com.pl/
LinkedIn: http://www.linkedin.com/in/marcinjuszkiewicz
Some of Linaro developers works with ARM devices older then ARMv7-a
architecture. Other people experiments with hard-float ABI. Each of them has
to rebuild toolchain for own use and that means playing with components to
have them build properly.
But it is no more - I made some patches and armel-cross-toolchain-base since
1.53 version + newer source packages for gcc-4.[45]-armel-cross have support
for "debian/flavour" file which allows to set some flags related to toolchain
build.
So far supported things are:
- ARM architecture
- float ABI
- FPU mode
- Thumb mode
This feature is not merged into regular Ubuntu packages yet as this is work in
progress which needs to be cleaned first.
http://people.linaro.org/~hrw/armel-cross-toolchain/ has all source packages
needed.
Regards,
--
JID: hrw(a)jabber.org
Website: http://marcin.juszkiewicz.com.pl/
LinkedIn: http://www.linkedin.com/in/marcinjuszkiewicz
I meant to send this to the "external" Linaro toolchain mailing list,
not the internal CS one. Apologies to those who receive it twice!
In a follow-up message, Joseph Myers pointed out a post he'd written
previously on the same subject:
http://gcc.gnu.org/ml/gcc-patches/2010-06/msg00409.html
In further followups (at the risk of misrepresenting Joseph & Paul
Brook's opinions!), there seemed to be general agreement that a scheme
something like that outlined below, with "permuting" loads/stores and
some way of handling multiple in-register layouts for vectors seems
like it will be a necessary addition to the vectorizer, going forward.
Julian
Begin forwarded message:
Date: Thu, 7 Oct 2010 16:45:17 +0100
From: Julian Brown <julian(a)codesourcery.com>
To: Ira Rosen <IRAR(a)il.ibm.com>
Cc: Tejas Belagod <Tejas.Belagod(a)arm.com>, Linaro List
<gnu-linaro-tools(a)codesourcery.com> Subject: [gnu-linaro-tools] NEON
vectorization: use of specialized load/store instructions
Hi,
We're having some system issues, so I thought I'd take the chance to
write down some things I've been thinking about re: utilising the NEON
load/store instructions more effectively. I've also attempted to
summarize the problems with big-endian mode. All unverified as of yet,
so please take with a pinch of salt :-). Comments appreciated. It's
been a while since I last thought about some of this stuff...
Cheers,
Julian
Use of specialized load instructions
====================================
To provide good support for NEON's element and structure load/store
instructions, GCC lacks support for a couple of key features:
1. A good way of representing a set of two, three or four vector
registers (either D- or Q-sized), possibly with non-unit stride.
2. A generalised mapping between memory locations and lane numbers.
To start with point 1: currently the element and structure load/store
instructions are only supported via intrinsics. These are specified to
load and store as if going via an array embedded in a union, i.e.:
typedef struct int8x8x2_t
{
int8x8_t val[2];
} int8x8x2_t;
__extension__ static __inline int8x8x2_t __attribute__
((__always_inline__)) vld2_s8 (const int8_t * __a)
{
union { int8x8x2_t __i; __builtin_neon_ti __o; } __rv;
__rv.__o = __builtin_neon_vld2v8qi ((const __builtin_neon_qi *) __a);
return __rv.__i;
}
Even for a trivial test program, e.g.:
#include <arm_neon.h>
int foo (int8_t *x)
{
int8x8x2_t result = vld2_s8 (x);
return vget_lane_s8 (result.val[0], 1);
}
We will generate code like so:
sub sp, sp, #32
vld2.8 {d16-d17}, [r0]
mov r3, sp
vstmia sp, {d16-d17}
add ip, sp, #16
ldmia r3, {r0, r1, r2, r3}
stmia ip, {r0, r1, r2, r3}
fldd d16, [sp, #16]
vmov.s8 r0, d16[1]
add sp, sp, #32
bx lr
I.e., rather than being used directly, the registers loaded by vld2
will always be spilled to the stack then reloaded. This obviously
reduces the usefulness of these intrinsics by a large factor. With some
planning, it'd be good to find a powerful enough solution to this
problem so that the same representation for multiple registers can be
used by the autovectorizer as well as the intrinsic-handling code.
(One difficulty is that the "foo.val[X]" interface should still be
available to user code. There's probably no need for "val" to literally
be an array, though other representations would require front-end
changes).
Assuming it's hard for the register allocator to deal with
highly-constrained situations like requiring four consecutive
registers, one (ugly) possibility might be to run a pass before
register allocation, looking for "big" multi-register vectors and
pre-allocating them to hard registers. Even using a fixed allocation of
a single set of registers (e.g. make it so that all multi-reg
loads/stores larger than a Q register must use d0-d7, or whatever)
would probably give better code than what we produce at present, in
most cases.
Now, point 2. To start with, an aside: AIUI, there is currently an
assumption in the vectoriser code that increasing element numbers in
vector registers correspond to increasing addresses when those
registers are loaded from and stored to memory (as if the vector was a
short array, or alternatively as if a union of the vector register and
an array of element-types had the same numberings for lanes and array
indices corresponding to the same elements). Unfortunately that is only
true for NEON in little-endian mode: in big-endian mode, the story is
more complicated, for reasons I will try to explain.
To remain compliant with the soft-float variant of the ARM EABI, we
must pass vector register arguments in ARM registers (or the stack),
not vector registers. This means that we must be very careful with the
ordering of elements for values passed to functions. Consider the
trivial function:
int __attribute__((noinline)) qux (int16x8_t x)
{
x = vaddq_s16 (x, x);
return vgetq_lane_s16 (x, 1);
}
This is compiled by GCC to the following (slightly unimpressively):
vmov d18, r1, r0 @ v8hi
vmov d19, r3, r2
vmov d20, r1, r0 @ v8hi
vmov d21, r3, r2
vadd.i16 q8, q9, q10
vmov.s16 r0, d16[1]
bx lr
Which may then be called like, e.g.:
ldmia sp, {r0-r3}
blx qux
So: notice that we're careful that when vector values are transferred
from NEON registers to core registers, the same result will be
transferred to/from memory when we use ldm/stm (core registers) or
vldm/vstm (vector registers) -- i.e. we might use "vldm rX, {d18-d19}",
storing d18 and d19 in consecutive increasing addresses, or "ldmia rX,
{r0-r3}", again with consecutive registers in increasing memory
locations, and we get the same outcome. The fact that we can use the
multiple-register loads/stores is also important for spilling/reloading
between vector and core registers, which inevitably happens
occasionally.
Notice also that when we call the above function like so:
typedef union {
int16x8_t quadvec;
int16_t half[8];
} u;
int foo (int8_t *x)
{
u bar;
int i;
for (i = 0; i < 8; i++)
bar.half[i] = i;
qux (bar.quadvec);
}
The value returned from "qux" is NOT 2 (1+1), as it would be if we were
accessing the value at index 1 in the superimposed array in the union
"u". The vgetq_lane_s16 call still interprets the array as if it had
been loaded in little-endian element order. But we don't get the result
we would have if the vector had been interpreted in purely big-endian
order either (i.e. 12, 6+6)! In fact from the perspective of the
element numbering used by vgetq_lane_s16, the vector elements we see
for each of the (equal) operands of the "vadd" instruction in the qux
function are:
equiv. core register
lane number (at function entry) value
----------- -------------------- -----
[0] high part of r1 3
[1] low part of r1 2
[2] high part of r0 1
[3] low part of r0 0
[4] high part of r3 7
[5] low part of r3 6
[6] high part of r2 5
[7] low part of r2 4
So the value returned will be 2+2, 4.
Now, coming back to the vectorizer. Current practice means that
increasing element numbers should correspond to increasing memory
locations: i.e., that "array ordering" is in effect, just as in the
call to vgetq_lane_s16 in the above example. This leads to an anomaly:
it means that when the vectorizer asks for a particular element, it
will generally get a different one. Most of the time we get away with
this, since the vectorizer mostly deals with "opaque" vectors which are
operated on element-wise: i.e. we only deal with data at the
granularity of whole vectors, so it doesn't matter which order the
elements are in. The ARM implementations of reduction operations
fortuitously calculate the results across all elements simultaneously,
so when one of those elements is extracted, we still get the right
answer.
One notable exception to this though is the movmisalign<mode> patterns:
these are implemented using the vld1 and vst1 instructions, which load
elements in "array" order (increasing elements from increasing memory
locations), even in big-endian mode. Since vectors loaded using those
instructions are "incompatible" with the above scheme, such misaligned
accesses are simply disabled in big-endian mode.
Of course, generally, sticking with the current non-solution in
big-endian mode is not sustainable (and is probably already broken in
various cases). So it might be worth thinking about whether supporting
big-endian mode properly, as well as handling the more complex load and
store element/structure instructions, can be done using some
generalised solution.
I'm thinking (without having much idea about how feasible such an idea
is) of something along the lines of a function (in the mathematical
sense) attached to each vector value manipulated by the vectorizer, to
map that value's element numberings to and from memory offsets. So then
the quad-word vector of 16-bit elements discussed above would look
like, in big-endian mode:
foo, {6, 4, 2, 0, 14, 12, 10, 8}
Whereas in little-endian mode (or in big-endian mode, for vectors
loaded using vld1), it would look like:
foo, {0, 2, 4, 6, 8, 10, 12, 14}
And then, perhaps more interestingly, a vector loaded using e.g. a
"multiple 3-element structures" load,
vld3.16 {d1, d2, d3}, [rN]
Might look like (in either endianness, assuming we can represent a
vector of such size in our hypothetical scheme):
foo, {0, 6, 12, 18, 2, 8, 14, 20, 4, 10, 16, 22}
Though it's not clear that such a scheme would be powerful enough to
represent the whole range of element/structure loads/stores available
(you'd probably need to be able to specify skipped or don't-care
elements to do that, at least).
First of all, the goal of this work is about investigation on speed
improvement on linaro gcc 4.5. Finally, the output/result of this work
is to list all possible recommendations/actions to improve speed on
linaro 4.5. Comments to this plan are welcome.
So far, we can improve speed in three ways,
1. Backport patches from FSF GCC 4.6. Note that we don't want to
backport the whole 4.6.
2. Benchmark with FSF GCC 4.5.0. Fix performance regressions if there
are on linaro gcc 4.5. Output is the reason of performance regression,
or even further, give recommendations on how to fix it.
3. Study the code generated by other ARM compilers, and give
recommendations on how to improve GCC to do better job.
I'll describe these three ways in details in the following sections,
- Backport patches from FSF GCC 4.6
I went through gcc-patches archive, and select several patches that are
helpful to code improvements.
1 ifcvt optimization. Target independent.
http://gcc.gnu.org/ml/gcc-patches/2010-04/msg00832.html
2 redundant register move for sign extending. Thumb2.
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43137
3. PR 45335 Use ldrd and strd to access two consecutive words.
Not yet approved.
http://gcc.gnu.org/ml/gcc-patches/2010-09/msg00059.html
4. Fix an if statement in arm_rtx_costs_1.
http://gcc.gnu.org/ml/gcc-patches/2010-07/msg02096.html
5. Reduce code duplication for Thumb2 move patterns
http://gcc.gnu.org/ml/gcc-patches/2010-07/msg00624.html
6. ARM ldm/stm peepholes
http://gcc.gnu.org/ml/gcc-patches/2010-07/msg00512.html
7. PR44999 Replace "and r0, r0, #255" with uxtb in thumb2
http://gcc.gnu.org/ml/gcc-patches/2010-07/msg01700.html
8. Improve optimization to transform TST into LSLS
http://gcc.gnu.org/ml/gcc-patches/2010-06/msg02518.html
9. Fix bswap patterns for ARM / Thumb and Thumb2.
http://gcc.gnu.org/ml/gcc-patches/2010-01/msg01238.html
- Fix speed regression
I found speed regression on EEMBC on linaro 4.5, compared with FSF GCC
4.5.0, and I'll investigate why speed regression happens on these cases.
Here is a table below about speed regression compared between FSF GCC
4.5.0 and Linaro GCC 4.5 (revno:99398)
O2 O3
puwmod01, -5.5 -3.5
bitmnp01, -7.9 -0.7
routelookup, -6.4 -8.2
conven00data_1, -7.2 -5.8
conven00data_2, -8.1 -7.3
conven00data_3, -6.6 -5.5
viterb00data_1, -1.7 +5.9
viterb00data_2, -4.3 +2.6
viterb00data_3, -2.3 +1.8
viterb00data_4, -5.3 -0.3
- Study the code generated by other ARM compilers.
In this part, I'll study the binary generated by other ARM compilers,
and try to teach GCC smart enough to do the same thing. This piece of
work is quite open, and hard to estimate how much output we could get.
--
Yao Qi
CodeSourcery
yao(a)codesourcery.com
(650) 331-3385 x739
People here might want to have a look at this bug:
http://gcc.gnu.org/bugzilla/show_bg.cgi?id=45979
Note that the heap randomization feature added to the kernel was part of
a Linaro security blueprint.
Nicolas