Skip to content

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 after q1 := 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.

Possible fixes

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information