feat(asm): lock prefix + negl/xaddl encoders

This commit is contained in:
Graeme Geldenhuys 2026-06-25 11:07:21 +01:00
parent 8467eed451
commit 85a46aa1ec
2 changed files with 83 additions and 0 deletions

View file

@ -180,6 +180,7 @@ type
NumOps: Integer;
RawLine: string; { original line text }
LineNum: Integer;
HasLock: Boolean; { a `lock` prefix preceded the mnemonic }
end;
{ Internal assembler state for a single section }
@ -706,6 +707,21 @@ begin
Mnemonic := Mnemonic + Chr(S[P]);
P := P + 1;
end;
{ `lock` prefix: record it and read the real mnemonic that follows. }
if Mnemonic = 'lock' then
begin
Result.HasLock := True;
while (P < Length(S)) and ((S[P] = Ord(' ')) or (S[P] = 9)) do
P := P + 1;
Mnemonic := '';
while (P < Length(S)) and (IsAlnum(S[P])) do
begin
Mnemonic := Mnemonic + Chr(S[P]);
P := P + 1;
end;
end;
Result.Mnemonic := Mnemonic;
{ Skip whitespace }
@ -1249,6 +1265,10 @@ begin
Op1 := AParsed.Op1;
Op2 := AParsed.Op2;
{ ---- lock prefix (F0) — emitted ahead of the instruction it guards ---- }
if AParsed.HasLock then
CBEmit(CB, $F0);
{ ---- ret ---- }
if Mnem = 'ret' then
begin
@ -1567,6 +1587,45 @@ begin
Exit;
end;
{ ---- negl / negq ---- (F7 /3, same group as notl/notq) }
if (Mnem = 'negl') or (Mnem = 'negq') then
begin
if Op1.Kind = opReg then
begin
EmitRexIfNeeded(CB, Mnem = 'negq', 0, Op1.Reg, False);
CBEmit(CB, $F7);
CBEmit(CB, MakeModRM(3, 3, Op1.Reg));
end
else
raise EAssembler.Create(Mnem + ': unsupported operand');
Result := CB.Data;
Exit;
end;
{ ---- xaddl / xaddq ---- (0F C1 /r: xadd src-reg, r/m). Atomic with the
lock prefix (emitted above). src register is in the ModRM reg field. }
if (Mnem = 'xaddl') or (Mnem = 'xaddq') then
begin
if (Op1.Kind = opReg) and IsMemLike(Op2) then
begin
EmitRexForMem(CB, Mnem = 'xaddq', Op1.Reg, Op2, False);
CBEmit(CB, $0F);
CBEmit(CB, $C1);
EncodeMemOperand(CB, ACtx, Op2, Op1.Reg and 7);
end
else if (Op1.Kind = opReg) and (Op2.Kind = opReg) then
begin
EmitRexIfNeeded(CB, Mnem = 'xaddq', Op1.Reg, Op2.Reg, False);
CBEmit(CB, $0F);
CBEmit(CB, $C1);
CBEmit(CB, MakeModRM(3, Op1.Reg, Op2.Reg));
end
else
raise EAssembler.Create(Mnem + ': unsupported operand combination');
Result := CB.Data;
Exit;
end;
{ ---- shll/shlq/shrl/shrq ---- }
if (Mnem = 'shll') or (Mnem = 'shlq') or (Mnem = 'shrl') or (Mnem = 'shrq') then
begin

View file

@ -36,6 +36,8 @@ type
procedure TestErrorMessage_HasLineNumber;
procedure TestMovqXmmToReg_SseEncoding;
procedure TestMovqRegToXmm_SseEncoding;
procedure TestNegl_F7Slash3;
procedure TestLockXaddl_AtomicEncoding;
end;
{ ---- ELF writer unit tests ---- }
@ -148,6 +150,28 @@ begin
ContainsBytes(Obj, Chr($48) + Chr($8B) + Chr($07)));
end;
procedure TAsmEncodingTests.TestNegl_F7Slash3;
var
Obj: string;
begin
{ negl %esi -> f7 de (F7 /3, reg=esi=6). Verified vs `cc`. }
Obj := AssembleToBytes('negl %esi' + LineEnding);
AssertTrue('f7 de missing',
ContainsBytes(Obj, Chr($F7) + Chr($DE)));
end;
procedure TAsmEncodingTests.TestLockXaddl_AtomicEncoding;
var
Obj: string;
begin
{ lock xaddl %eax, (%rdi) -> f0 0f c1 07. The atomic RTL primitive
(_AtomicAddInt32). f0 = lock prefix, 0f c1 = xadd, 07 = ModRM
(reg=eax=0, r/m=[rdi]). Verified vs `cc`. }
Obj := AssembleToBytes('lock xaddl %eax, (%rdi)' + LineEnding);
AssertTrue('f0 0f c1 07 missing',
ContainsBytes(Obj, Chr($F0) + Chr($0F) + Chr($C1) + Chr($07)));
end;
procedure TAsmEncodingTests.TestQuadSymbol_EmitsReloc;
var
Obj: string;