AVR: Incorrect 64 bit shift code generated at -O3 or higher
Summary
Under certain conditions the compiler generates incorrect code for 64 bit values shifted by integer multiples of 8. Other shift values seems to work fine.
The example works when compiled with FPC 3.2.2 or 3.2-fixes.
System Information
Main branch, latest commit is 6345a253 (March 13).
Steps to reproduce
Compile example program for avrsim and test using e.g. fp-avrsim:
$ ~/fpc/installs/lib/fpc/3.3.1/ppcrossavr -O3 -Wpavrsim testshift.pp
$ ~/LazProjs/fp-avrsim-cc/avrsim ./testshift.bin
Testing SHR64:
1 - OK
2 - OK
3 - OK
4 - OK
5 - OK
6 - OK
7 - OK
8 - $1234567890ABCDEF shr 8 = $00123456789078CD, expected $001234567890ABCD
Note that the error only appear under some circumstances. Referring to the testshift example:
- Commenting
q := data[0];
avoids the error (q is also initialized in main). - Commenting the code after
q1 := q shr 8; status( 8);
also does not result in an error. It seems as if some shift code afterq1 := q shr 8; status( 8);
is required to trigger the error. - Moving the code out of the test procedure (data array is global const, status is a normal procedure, shift instructions sit in main) also results in no errors.
- Simplifying the code, such as removing the const array data and passing the expected value as parameter to the status procedure, also works without error.
- Optimization levels below -O3 works correctly.
As a guess it seems as if the error is associated with high register pressure.
Example Project
program testshift;
var
q, q1: qword;
procedure test;
const
data: array of qword = (
$1234567890ABCDEF, $091A2B3C4855E6F7, $048D159E242AF37B, $02468ACF121579BD,
$01234567890ABCDE, $0091A2B3C4855E6F, $0048D159E242AF37, $002468ACF121579B,
$001234567890ABCD, $00091A2B3C4855E6, $00048D159E242AF3, $0002468ACF121579,
$0001234567890ABC, $000091A2B3C4855E, $000048D159E242AF, $00002468ACF12157,
$00001234567890AB, $0000091A2B3C4855, $0000048D159E242A, $000002468ACF1215,
$000001234567890A, $00000091A2B3C485, $00000048D159E242, $0000002468ACF121,
$0000001234567890, $000000091A2B3C48, $000000048D159E24, $00000002468ACF12,
$0000000123456789, $0000000091A2B3C4, $0000000048D159E2, $000000002468ACF1,
$0000000012345678, $00000000091A2B3C, $00000000048D159E, $0000000002468ACF,
$0000000001234567, $000000000091A2B3, $000000000048D159, $00000000002468AC,
$0000000000123456, $0000000000091A2B, $0000000000048D15, $000000000002468A,
$0000000000012345, $00000000000091A2, $00000000000048D1, $0000000000002468,
$0000000000001234, $000000000000091A, $000000000000048D, $0000000000000246,
$0000000000000123, $0000000000000091, $0000000000000048, $0000000000000024,
$0000000000000012, $0000000000000009, $0000000000000004, $0000000000000002,
$0000000000000001, $0000000000000000, $0000000000000000, $0000000000000000);
procedure status(const i: byte);
begin
write(i:2, ' - ');
if q1 <> data[i] then
begin
WriteLn('$', HexStr(data[0], 16), ' shr ', i, ' = $', HexStr(q1, 16), ', expected $', HexStr(data[i], 16));
Halt(i);
end
else
WriteLn('OK');
end;
begin
q := data[0];
writeln('Testing SHR64:');
q1 := q shr 1; status( 1);
q1 := q shr 2; status( 2);
q1 := q shr 3; status( 3);
q1 := q shr 4; status( 4);
q1 := q shr 5; status( 5);
q1 := q shr 6; status( 6);
q1 := q shr 7; status( 7);
q1 := q shr 8; status( 8);
q1 := q shr 9; status( 9);
q1 := q shr 10; status(10);
q1 := q shr 11; status(11);
end;
begin
q := $1234567890ABCDEF;
test;
Halt(0);
end.
What is the current bug behavior?
The correct code generated for q1 := q shr 8;
is:
# [46] q1 := q shr 8; status( 8);
lds r18,(U_sPsTESTSHIFT_ss_Q)
lds r24,(U_sPsTESTSHIFT_ss_Q+1)
lds r18,(U_sPsTESTSHIFT_ss_Q+2)
lds r19,(U_sPsTESTSHIFT_ss_Q+3)
lds r20,(U_sPsTESTSHIFT_ss_Q+4)
lds r21,(U_sPsTESTSHIFT_ss_Q+5)
lds r22,(U_sPsTESTSHIFT_ss_Q+6)
lds r23,(U_sPsTESTSHIFT_ss_Q+7)
sts (U_sPsTESTSHIFT_ss_Q1),r24
sts (U_sPsTESTSHIFT_ss_Q1+1),r18
sts (U_sPsTESTSHIFT_ss_Q1+2),r19
sts (U_sPsTESTSHIFT_ss_Q1+3),r20
sts (U_sPsTESTSHIFT_ss_Q1+4),r21
sts (U_sPsTESTSHIFT_ss_Q1+5),r22
sts (U_sPsTESTSHIFT_ss_Q1+6),r23
sts (U_sPsTESTSHIFT_ss_Q1+7),r1
When the error is triggered, the following code gets generated:
# [46] q1 := q shr 8; status( 8);
lds r18,(U_sPsTESTSHIFT_ss_Q)
lds r18,(U_sPsTESTSHIFT_ss_Q+1)
lds r19,(U_sPsTESTSHIFT_ss_Q+2)
lds r20,(U_sPsTESTSHIFT_ss_Q+3)
lds r21,(U_sPsTESTSHIFT_ss_Q+4)
lds r22,(U_sPsTESTSHIFT_ss_Q+5)
lds r23,(U_sPsTESTSHIFT_ss_Q+6)
lds r2,(U_sPsTESTSHIFT_ss_Q+7)
mov r25,r18
movw r18,r20
movw r20,r22
sts (U_sPsTESTSHIFT_ss_Q1),r25
sts (U_sPsTESTSHIFT_ss_Q1+1),r19
sts (U_sPsTESTSHIFT_ss_Q1+2),r18
sts (U_sPsTESTSHIFT_ss_Q1+3),r19
sts (U_sPsTESTSHIFT_ss_Q1+4),r20
sts (U_sPsTESTSHIFT_ss_Q1+5),r21
sts (U_sPsTESTSHIFT_ss_Q1+6),r2
sts (U_sPsTESTSHIFT_ss_Q1+7),r1
Note the wrong register (r19 instead of r24) is loaded into the 2nd byte of variable q1. There are also unnecessary register moves after the variable q is loaded.