refactor(codegen): hoist symbol mangler to blaise.codegen (F5)
QBEMangle (TCodeGenQBE method) and NativeMangle (native free function) applied the same metacharacter mapping: '<' ',' ' ' -> '_', drop '>', and '$' '@' '^' -> '_D_' '_V_' '_P_'. Two copies of the symbol-naming rule is a link-time-breakage risk if one side gains a case the other lacks. Hoist a single CodegenMangle into blaise.codegen (distinct name, per the F4 method-binding gotcha); both backends delegate. The shared version keeps QBEMangle's clean-name fast-path (no per-character concat when no metacharacter is present) and reads bytes via StrAt — so NativeMangle now also uses the stage-stable StrAt instead of the Ord(S[i]) idiom the house rules warn against. Cross-backend dedup, no behaviour change: native .s AND QBE IR both byte-identical on a generics + overload program (TList<Integer>, TList<string>, Show$i/Show$S → TList_Integer_*, Show_D_i/Show_D_S). All four fixpoints — which mangle the entire compiler's generic/overload symbol set — and the full suite on both test runners are unchanged.
This commit is contained in:
parent
1e0b7fc46a
commit
1c250fb13e
|
|
@ -1670,25 +1670,10 @@ end;
|
||||||
{ Mangle a name for use as an assembly label: replace characters that are
|
{ Mangle a name for use as an assembly label: replace characters that are
|
||||||
invalid in label names with underscores, matching QBE backend's QBEMangle. }
|
invalid in label names with underscores, matching QBE backend's QBEMangle. }
|
||||||
function NativeMangle(const AName: string): string;
|
function NativeMangle(const AName: string): string;
|
||||||
var
|
|
||||||
I, C: Integer;
|
|
||||||
begin
|
begin
|
||||||
Result := '';
|
{ Delegates to the shared backend-neutral mangler in blaise.codegen
|
||||||
for I := 0 to Length(AName) - 1 do
|
(formerly a per-character twin of the QBE backend's QBEMangle). }
|
||||||
begin
|
Result := CodegenMangle(AName);
|
||||||
C := Ord(AName[I]);
|
|
||||||
case C of
|
|
||||||
60: Result := Result + '_'; { '<' }
|
|
||||||
62: ; { '>' — skip }
|
|
||||||
44: Result := Result + '_'; { ',' }
|
|
||||||
32: Result := Result + '_'; { ' ' — e.g. 'class of T' }
|
|
||||||
36: Result := Result + '_D_'; { '$' }
|
|
||||||
64: Result := Result + '_V_'; { '@' }
|
|
||||||
94: Result := Result + '_P_'; { '^' }
|
|
||||||
else
|
|
||||||
Result := Result + Chr(C);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TX86_64Backend.ClassSymName(const AClassName: string): string;
|
function TX86_64Backend.ClassSymName(const AClassName: string): string;
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ unit blaise.codegen;
|
||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
uAST, uSymbolTable, uDebugFacts, blaise.codegen.target;
|
uAST, uSymbolTable, uDebugFacts, blaise.codegen.target, uStrCompat;
|
||||||
|
|
||||||
type
|
type
|
||||||
TRecReturnClass = (
|
TRecReturnClass = (
|
||||||
|
|
@ -133,6 +133,13 @@ function RecretClassify(ARec: TRecordTypeDesc;
|
||||||
twins ExprOwnsRef / NativeExprOwnsRef). }
|
twins ExprOwnsRef / NativeExprOwnsRef). }
|
||||||
function ArcExprOwnsRef(AExpr: TASTExpr): Boolean;
|
function ArcExprOwnsRef(AExpr: TASTExpr): Boolean;
|
||||||
|
|
||||||
|
{ Mangle a Blaise symbol name into an assembler-legal identifier, shared by
|
||||||
|
both backends (formerly QBEMangle / NativeMangle). Replaces the generic/
|
||||||
|
overload metacharacters '<' ',' ' ' -> '_', drops '>', and maps the type-code
|
||||||
|
sigils '$' '@' '^' to '_D_' '_V_' '_P_'. A clean name (no metacharacter) is
|
||||||
|
returned unchanged via a fast pre-scan that avoids per-character concat. }
|
||||||
|
function CodegenMangle(const AName: string): string;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
function RecretManagedClean(ARec: TRecordTypeDesc): Boolean;
|
function RecretManagedClean(ARec: TRecordTypeDesc): Boolean;
|
||||||
|
|
@ -357,4 +364,43 @@ begin
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function CodegenMangle(const AName: string): string;
|
||||||
|
var
|
||||||
|
I, C: Integer;
|
||||||
|
Clean: Boolean;
|
||||||
|
begin
|
||||||
|
{ Fast path: the overwhelming majority of names contain none of the mangled
|
||||||
|
characters — return the input unchanged instead of rebuilding it with one
|
||||||
|
concat (and its ARC churn) per character. }
|
||||||
|
Clean := True;
|
||||||
|
for I := 0 to Length(AName) - 1 do
|
||||||
|
begin
|
||||||
|
C := StrAt(AName, I);
|
||||||
|
if (C = 60) or (C = 62) or (C = 44) or (C = 36) or (C = 64) or
|
||||||
|
(C = 94) or (C = 32) then
|
||||||
|
begin
|
||||||
|
Clean := False;
|
||||||
|
break;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
if Clean then
|
||||||
|
Exit(AName);
|
||||||
|
Result := '';
|
||||||
|
for I := 0 to Length(AName) - 1 do
|
||||||
|
begin
|
||||||
|
C := StrAt(AName, I);
|
||||||
|
case C of
|
||||||
|
60: Result := Result + '_'; { '<' }
|
||||||
|
62: ; { '>' — skip }
|
||||||
|
44: Result := Result + '_'; { ',' }
|
||||||
|
32: Result := Result + '_'; { ' ' — e.g. 'class of T' }
|
||||||
|
36: Result := Result + '_D_'; { '$' — overload delimiter }
|
||||||
|
64: Result := Result + '_V_'; { '@' — var-param prefix }
|
||||||
|
94: Result := Result + '_P_'; { '^' — pointer prefix }
|
||||||
|
else
|
||||||
|
Result := Result + Chr(C);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|
|
||||||
|
|
@ -13382,43 +13382,10 @@ begin
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TCodeGenQBE.QBEMangle(const AName: string): string;
|
function TCodeGenQBE.QBEMangle(const AName: string): string;
|
||||||
var
|
|
||||||
I: Integer;
|
|
||||||
C: Integer;
|
|
||||||
Clean: Boolean;
|
|
||||||
begin
|
begin
|
||||||
{ Fast path: the overwhelming majority of names contain none of the
|
{ Delegates to the shared backend-neutral mangler in blaise.codegen
|
||||||
mangled characters — return the input unchanged instead of rebuilding
|
(formerly a per-character twin of the native backend's NativeMangle). }
|
||||||
it with one concat (and its ARC churn) per character. }
|
Result := CodegenMangle(AName);
|
||||||
Clean := True;
|
|
||||||
for I := 0 to Length(AName) - 1 do
|
|
||||||
begin
|
|
||||||
C := StrAt(AName, I);
|
|
||||||
if (C = 60) or (C = 62) or (C = 44) or (C = 36) or (C = 64) or
|
|
||||||
(C = 94) or (C = 32) then
|
|
||||||
begin
|
|
||||||
Clean := False;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if Clean then
|
|
||||||
Exit(AName);
|
|
||||||
Result := '';
|
|
||||||
for I := 0 to Length(AName) - 1 do
|
|
||||||
begin
|
|
||||||
C := StrAt(AName, I);
|
|
||||||
case C of
|
|
||||||
60: Result := Result + '_'; { '<' }
|
|
||||||
62: ; { '>' — skip }
|
|
||||||
44: Result := Result + '_'; { ',' }
|
|
||||||
32: Result := Result + '_'; { ' ' — e.g. 'class of T' }
|
|
||||||
36: Result := Result + '_D_'; { '$' — overload delimiter }
|
|
||||||
64: Result := Result + '_V_'; { '@' — var-param prefix }
|
|
||||||
94: Result := Result + '_P_'; { '^' — pointer prefix }
|
|
||||||
else
|
|
||||||
Result := Result + Chr(C);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TCodeGenQBE.MethodEmitName(AMDecl: TMethodDecl;
|
function TCodeGenQBE.MethodEmitName(AMDecl: TMethodDecl;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue