fix(native): ARC for managed-type dynamic array element assignment
When assigning to a dynamic array element where the element type is managed (string or class), the native backend now emits AddRef on the new value and Release on the old value before storing, matching the QBE backend's behaviour. Previously A[I] := 'new' would store without releasing the old string at A[I], leaking one reference per overwrite. E2E test TestRun_Native_DynArrayElemArc_String verifies string element assignment with replacement on both backends.
This commit is contained in:
parent
02b03dbbd0
commit
0317a3b454
|
|
@ -7151,6 +7151,7 @@ var
|
|||
FAE: TFieldAccessExpr;
|
||||
SSA: TStaticSubscriptAssign;
|
||||
MD: TMethodDecl;
|
||||
DAElemType: TTypeDesc;
|
||||
I: Integer;
|
||||
LThen, LElse, LEnd: string;
|
||||
LCond, LBody: string;
|
||||
|
|
@ -8602,23 +8603,39 @@ begin
|
|||
if (SSA.ResolvedArrayType <> nil) and
|
||||
(SSA.ResolvedArrayType.Kind = tyDynArray) then
|
||||
begin
|
||||
{ Dynamic array element write: A[I] := V.
|
||||
Data pointer lives directly in the variable slot. }
|
||||
DAElemType := TDynArrayTypeDesc(SSA.ResolvedArrayType).ElementType;
|
||||
Self.EmitExprToEax(SSA.ValueExpr);
|
||||
Self.Emit(#9'pushq %rax');
|
||||
{ Index * elem_size. }
|
||||
Self.EmitExprToEax(SSA.IndexExpr);
|
||||
Self.Emit(Format(#9'imulq $%d, %%rax',
|
||||
[TDynArrayTypeDesc(SSA.ResolvedArrayType).ElementType.RawSize()]));
|
||||
{ Base pointer into %rcx. }
|
||||
Self.Emit(Format(#9'imulq $%d, %%rax', [DAElemType.RawSize()]));
|
||||
if Self.IsLocal(SSA.ArrayName) then
|
||||
Self.Emit(Format(#9'movq %s, %%rcx', [Self.VarOperand(SSA.ArrayName)]))
|
||||
else
|
||||
Self.Emit(Format(#9'movq %s(%%rip), %%rcx', [SSA.ArrayName]));
|
||||
Self.Emit(#9'addq %rcx, %rax');
|
||||
Self.Emit(#9'movq %rax, %rcx');
|
||||
if DAElemType.Kind = tyString then
|
||||
begin
|
||||
Self.Emit(#9'pushq %rcx');
|
||||
Self.Emit(#9'movq 8(%rsp), %rdi');
|
||||
Self.Emit(#9'callq _StringAddRef');
|
||||
Self.Emit(#9'movq (%rsp), %rcx');
|
||||
Self.Emit(#9'movq (%rcx), %rdi');
|
||||
Self.Emit(#9'callq _StringRelease');
|
||||
Self.Emit(#9'popq %rcx');
|
||||
end
|
||||
else if DAElemType.Kind = tyClass then
|
||||
begin
|
||||
Self.Emit(#9'pushq %rcx');
|
||||
Self.Emit(#9'movq 8(%rsp), %rdi');
|
||||
Self.Emit(#9'callq _ClassAddRef');
|
||||
Self.Emit(#9'movq (%rsp), %rcx');
|
||||
Self.Emit(#9'movq (%rcx), %rdi');
|
||||
Self.Emit(#9'callq _ClassRelease');
|
||||
Self.Emit(#9'popq %rcx');
|
||||
end;
|
||||
Self.Emit(#9'popq %rax');
|
||||
Self.EmitStoreVar('(%rcx)', TDynArrayTypeDesc(SSA.ResolvedArrayType).ElementType);
|
||||
Self.EmitStoreVar('(%rcx)', DAElemType);
|
||||
Exit;
|
||||
end;
|
||||
{ Static array element write. }
|
||||
|
|
|
|||
|
|
@ -325,6 +325,8 @@ type
|
|||
procedure TestRun_Native_IntfFieldAsArg;
|
||||
{ M8b — nil assignment to interface-typed fields (implicit-Self and non-Self). }
|
||||
procedure TestRun_Native_IntfFieldNilAssign;
|
||||
{ M8b — dynarray element ARC: A[I] := 'new' releases old string at A[I]. }
|
||||
procedure TestRun_Native_DynArrayElemArc_String;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
|
@ -2861,6 +2863,21 @@ const
|
|||
end.
|
||||
''';
|
||||
|
||||
SrcDynArrayElemArcString = '''
|
||||
program P;
|
||||
var A: array of String;
|
||||
begin
|
||||
SetLength(A, 2);
|
||||
A[0] := 'first';
|
||||
A[1] := 'second';
|
||||
WriteLn(A[0]);
|
||||
WriteLn(A[1]);
|
||||
A[0] := 'replaced';
|
||||
WriteLn(A[0]);
|
||||
WriteLn(A[1])
|
||||
end.
|
||||
''';
|
||||
|
||||
{ Dyn-array field inside a class: the field must be ARC-refcounted on store
|
||||
and released when the holder is destroyed (f74e5cc). Observed by reading an
|
||||
element back after the field assignment — a dropped/garbled buffer would
|
||||
|
|
@ -4792,6 +4809,13 @@ begin
|
|||
AssertRunsOnBoth(SrcIntfFieldNilAssign, '99' + LE + 'cleared' + LE, 0);
|
||||
end;
|
||||
|
||||
procedure TE2ENativeTests.TestRun_Native_DynArrayElemArc_String;
|
||||
begin
|
||||
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertRunsOnBoth(SrcDynArrayElemArcString,
|
||||
'first' + LE + 'second' + LE + 'replaced' + LE + 'second' + LE, 0);
|
||||
end;
|
||||
|
||||
initialization
|
||||
RegisterTest(TE2ENativeTests);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue