Browse Source

* ARM: Fixed issue where some offsets of ADR assembly instructions
were incorrectly encoded by the internal assembler

J. Gareth "Curious Kit" Moreton 1 year ago
parent
commit
c4061e49e6
1 changed files with 43 additions and 7 deletions
  1. 43 7
      compiler/arm/aasmcpu.pas

+ 43 - 7
compiler/arm/aasmcpu.pas

@@ -2814,6 +2814,8 @@ implementation
         refoper : poper;
         refoper : poper;
         msb : longint;
         msb : longint;
         r: byte;
         r: byte;
+        imm : dword;
+        count : integer;
         singlerec : tcompsinglerec;
         singlerec : tcompsinglerec;
         doublerec : tcompdoublerec;
         doublerec : tcompdoublerec;
 
 
@@ -3870,17 +3872,51 @@ implementation
                   if assigned(currsym) then
                   if assigned(currsym) then
                     offset:=currsym.offset-insoffset-8;
                     offset:=currsym.offset-insoffset-8;
                   offset:=offset+oper[1]^.ref^.offset;
                   offset:=offset+oper[1]^.ref^.offset;
-                  if offset>=0 then
+                  if opcode = A_ADR then
                     begin
                     begin
-                      { set U flag }
-                      bytes:=bytes or (1 shl 23);
-                      bytes:=bytes or offset
+                      { The encoding for an ADR instruction is that of an ADD instruction,
+                        so the offset has to abide by immediate shifter rules, otherwise
+                        it can't be encoded }
+                      if is_shifter_const(offset,r) then
+                        begin
+                          bytes:=bytes or (1 shl 23);
+                        end
+                      else
+                        begin
+                          bytes:=bytes or (1 shl 22);
+                          offset:=-offset;
+                        end;
+
+                      { calc rotate and adjust imm }
+                      count:=0;
+                      r:=0;
+                      imm:=dword(offset);
+                      repeat
+                        imm:=RolDWord(imm, 2);
+                        inc(r);
+                        inc(count);
+                        if count > 32 then
+                          begin
+                            message1(asmw_e_invalid_opcode_and_operands, 'invalid shifter imm (offset)');
+                            exit;
+                          end;
+                      until (imm and $ff)=imm;
+                      bytes:=bytes or (r shl 8) or imm;
                     end
                     end
                   else
                   else
                     begin
                     begin
-                      bytes:=bytes or (1 shl 22);
-                      offset:=-offset;
-                      bytes:=bytes or offset
+                      if offset>=0 then
+                        begin
+                          { set U flag }
+                          bytes:=bytes or (1 shl 23);
+                          bytes:=bytes or offset
+                        end
+                      else
+                        begin
+                          bytes:=bytes or (1 shl 22);
+                          offset:=-offset;
+                          bytes:=bytes or offset
+                        end;
                     end;
                     end;
                 end
                 end
               else
               else