Skip to content

Invalid code generation for intel mode asm block with constants usage in reference expression

Summary

When using constants inside of asm block (in intel syntax) reference expression ([BASE + INDEX*SIZE + OFFSET], where SIZE and OFFSET are used as constants) the compiler produces invalid code.

I am worried about if it is a real bug or my misunderstanding on how it should work

System Information

  • Operating system: Linux (Kubuntu 24.04)
  • Processor architecture: x86-64
  • Compiler version: Free Pascal Compiler version 3.3.1-18107-g994ebf55ec-dirty [2025/07/06] for x86_64
  • Device: Compiler

Steps to reproduce

  1. Compile the example code
  2. Disassemble the binary
  3. Examine produced assembler

Example Project

program test_asm_bug;

{$Mode ObjFPC}
{$AsmMode Intel}

const
    SizeConst = 4;

procedure Test; assembler;
asm
    LEA RAX, [RCX + RDX*4 - 4]
    LEA RAX, [RCX + RDX*SizeConst - SizeConst]

    LEA RAX, [RCX + RDX*4 + 4]
    LEA RAX, [RCX + RDX*SizeConst + SizeConst]

    MOV RAX, [RCX + RDX*4 - 4]
    MOV RAX, [RCX + RDX*SizeConst - SizeConst]
end; 

begin
end.

What is the current bug behavior?

Produced assembler (dumped by objdump):

0000000000401090 <P$TEST_ASM_BUG_$$_TEST>:

const
    SizeConst = 4;

procedure Test; assembler;
asm
  401090:	50                   	push   %rax
    LEA RAX, [RCX + RDX*4 - 4]
  401091:	48 8d 44 91 fc       	lea    -0x4(%rcx,%rdx,4),%rax
    LEA RAX, [RCX + RDX*SizeConst - SizeConst]
  401096:	48 8d 04 11          	lea    (%rcx,%rdx,1),%rax

    LEA RAX, [RCX + RDX*4 + 4]
  40109a:	48 8d 44 91 04       	lea    0x4(%rcx,%rdx,4),%rax
    LEA RAX, [RCX + RDX*SizeConst + SizeConst]
  40109f:	48 8d 04 d1          	lea    (%rcx,%rdx,8),%rax

    MOV RAX, [RCX + RDX*4 - 4]
  4010a3:	48 8b 44 91 fc       	mov    -0x4(%rcx,%rdx,4),%rax
    MOV RAX, [RCX + RDX*SizeConst - SizeConst]
  4010a8:	48 8b 04 11          	mov    (%rcx,%rdx,1),%rax
end;
  4010ac:	59                   	pop    %rcx
  4010ad:	c3                   	ret

First, second and third pairs of instructions should provide the same code, because they are equal, yet compiler produces different instructions (and for expressions with constant it is wrong)

EDIT: When using LEA RAX,[RCX + SizeConst*RDX - SizeConst] the compiler emits valid code

What is the expected (correct) behavior?

Corresponding pairs of LEA/MOV with and without constants usage should provide the same code

Relevant logs and/or screenshots

The bug reproduces even on the FPC 3.2.2 (tested on godbolt), this snippet:

unit output;

{$Mode ObjFPC}
{$AsmMode Intel}

interface

implementation

// Type your code here, or load an example.

const
    SizeConst = 4;

procedure Test; assembler;
asm
    LEA RAX, [RCX + RDX*4 - 4]
    LEA RAX, [RCX + RDX*SizeConst - SizeConst]

    LEA RAX, [RCX + RDX*4 + 4]
    LEA RAX, [RCX + RDX*SizeConst + SizeConst]

    MOV RAX, [RCX + RDX*4 - 4]
    MOV RAX, [RCX + RDX*SizeConst - SizeConst]
end; 

end.

provides

test():
        leaq    -8(%rsp),%rsp
        leaq    -4(%rcx,%rdx,4),%rax
        leaq    (%rcx,%rdx,1),%rax
        leaq    4(%rcx,%rdx,4),%rax
        leaq    (%rcx,%rdx,8),%rax
        movq    -4(%rcx,%rdx,4),%rax
        movq    (%rcx,%rdx,1),%rax
        leaq    8(%rsp),%rsp
        ret
Edited by Hunter200165
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information