fix(native): StrToDouble returns its Double in %xmm0, not %rax

StrToDouble was lowered only in the integer EmitExprToEax path: it called
_StrToDouble (which returns the Double in %xmm0 per the System V ABI) and left
%rax untouched, so a float assignment (D := StrToDouble(S)) read %rax — a
leftover pointer — and cvtsi2sd'd it into garbage. Native printed values like
95693543141392 instead of the parsed number. Added StrToDouble to the float
emitter (EmitFloatBuiltin) so the result is consumed from %xmm0.

The 7 StrToDouble e2e tests are converted to AssertRunsOnAll, pinning the fix on
both backends. The blanket dual-backend flip of the inline CompileAndRun helper
is documented as deliberately reverted: it breaks on non-deterministic programs
(GetProcessID) and several genuine native gaps remain open (InheritsFrom/ToString
builtins, Round .5-boundary, class const-array, interface-field-assignment RHS,
EmitSretCall record-value arg) — all logged in bugs.txt for individual fixes.
Suites migrate to AssertRunsOnAll as those gaps close.

All three fixpoints green; 3481 tests pass.
This commit is contained in:
Graeme Geldenhuys 2026-06-18 02:56:13 +01:00
parent 5dd6a3d8c9
commit 161d8e4102
3 changed files with 36 additions and 35 deletions

View file

@ -4612,6 +4612,17 @@ var
IsS: Boolean;
begin
Result := True;
{ StrToDouble(S): the RTL helper takes a string pointer in %rdi and returns
the Double in %xmm0. It MUST be handled here in the float emitter — if it
falls through to the integer EmitExprToEax path, the result (already in
%xmm0) is discarded and the leftover %rax is cvtsi2sd'd into garbage. }
if SameText(FC.Name, 'StrToDouble') and (FC.Args.Count = 1) then
begin
Self.EmitExprToEax(TASTExpr(FC.Args.Items[0]));
Self.Emit(#9'movq %rax, %rdi');
Self.Emit(#9'callq _StrToDouble');
Exit;
end;
if SameText(FC.Name, 'Sqrt') and (FC.Args.Count = 1) then
begin
Self.EmitExprToXmm0(TASTExpr(FC.Args.Items[0]));

View file

@ -324,6 +324,14 @@ function TE2ETestCase.CompileAndRun(const ASrc: string;
out AStdout: string;
out AExitCode: Integer): Boolean;
begin
{ QBE-only. A blanket dual-backend flip here is unsafe: some inline e2e
programs print non-deterministic values (e.g. GetProcessID) that cannot be
compared across two separate process runs, and several genuine native gaps
(StrToDouble/DoubleToStr formatting, InheritsFrom/ToString builtins, class
const-array, interface-field-assignment-RHS) are still open — see bugs.txt.
Suites whose programs ARE deterministic and native-clean use AssertRunsOnAll
instead, which runs both backends. As the native gaps close, more inline
suites can migrate to AssertRunsOnAll. }
Result := Self.CompileAndRunOn(beQBE, ASrc, AStdout, AExitCode)
end;

View file

@ -1144,11 +1144,13 @@ begin
AssertEquals('DoubleToStr(0.0001)', '0.0001', Trim(Output));
end;
{ StrToDouble tests run on BOTH backends (AssertRunsOnAll) — they pinned the
native StrToDouble float-return bug where the result was read from %rax instead
of %xmm0. WriteLn(DoubleToStr(D)) emits 'value' + newline. }
procedure TE2EMathTests.TestRun_StrToDouble_Simple;
var Output: string; RCode: Integer;
begin
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
AssertTrue('compile+run', CompileAndRun(
AssertRunsOnAll(
'''
program P;
var D: Double; S: String;
@ -1157,16 +1159,13 @@ begin
D := StrToDouble(S);
WriteLn(DoubleToStr(D))
end.
''', Output, RCode));
AssertEquals('exit code', 0, RCode);
AssertEquals('StrToDouble(3.14)', '3.14', Trim(Output));
''', '3.14' + Chr(10), 0);
end;
procedure TE2EMathTests.TestRun_StrToDouble_Negative;
var Output: string; RCode: Integer;
begin
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
AssertTrue('compile+run', CompileAndRun(
AssertRunsOnAll(
'''
program P;
var D: Double; S: String;
@ -1175,16 +1174,13 @@ begin
D := StrToDouble(S);
WriteLn(DoubleToStr(D))
end.
''', Output, RCode));
AssertEquals('exit code', 0, RCode);
AssertEquals('StrToDouble(-1.5)', '-1.5', Trim(Output));
''', '-1.5' + Chr(10), 0);
end;
procedure TE2EMathTests.TestRun_StrToDouble_ScientificPos;
var Output: string; RCode: Integer;
begin
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
AssertTrue('compile+run', CompileAndRun(
AssertRunsOnAll(
'''
program P;
var D: Double; S: String;
@ -1193,16 +1189,13 @@ begin
D := StrToDouble(S);
WriteLn(DoubleToStr(D))
end.
''', Output, RCode));
AssertEquals('exit code', 0, RCode);
AssertEquals('StrToDouble(1e10)', '10000000000', Trim(Output));
''', '10000000000' + Chr(10), 0);
end;
procedure TE2EMathTests.TestRun_StrToDouble_ScientificNeg;
var Output: string; RCode: Integer;
begin
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
AssertTrue('compile+run', CompileAndRun(
AssertRunsOnAll(
'''
program P;
var D: Double; S: String;
@ -1211,16 +1204,13 @@ begin
D := StrToDouble(S);
WriteLn(DoubleToStr(D))
end.
''', Output, RCode));
AssertEquals('exit code', 0, RCode);
AssertEquals('StrToDouble(1e-10)', '1e-10', Trim(Output));
''', '1e-10' + Chr(10), 0);
end;
procedure TE2EMathTests.TestRun_StrToDouble_LargeMantissa;
var Output: string; RCode: Integer;
begin
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
AssertTrue('compile+run', CompileAndRun(
AssertRunsOnAll(
'''
program P;
var D: Double; S: String;
@ -1229,16 +1219,13 @@ begin
D := StrToDouble(S);
WriteLn(DoubleToStr(D))
end.
''', Output, RCode));
AssertEquals('exit code', 0, RCode);
AssertEquals('StrToDouble large mantissa', '1234567890.12345', Trim(Output));
''', '1234567890.12345' + Chr(10), 0);
end;
procedure TE2EMathTests.TestRun_StrToDouble_Zero;
var Output: string; RCode: Integer;
begin
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
AssertTrue('compile+run', CompileAndRun(
AssertRunsOnAll(
'''
program P;
var D: Double; S: String;
@ -1247,16 +1234,13 @@ begin
D := StrToDouble(S);
WriteLn(DoubleToStr(D))
end.
''', Output, RCode));
AssertEquals('exit code', 0, RCode);
AssertEquals('StrToDouble(0)', '0', Trim(Output));
''', '0' + Chr(10), 0);
end;
procedure TE2EMathTests.TestRun_StrToDouble_RoundTrip;
var Output: string; RCode: Integer;
begin
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
AssertTrue('compile+run', CompileAndRun(
AssertRunsOnAll(
'''
program P;
var D: Double;
@ -1264,9 +1248,7 @@ begin
D := StrToDouble(DoubleToStr(3.14159265358979));
WriteLn(DoubleToStr(D))
end.
''', Output, RCode));
AssertEquals('exit code', 0, RCode);
AssertEquals('roundtrip pi', '3.14159265358979', Trim(Output));
''', '3.14159265358979' + Chr(10), 0);
end;
procedure TE2EMathTests.TestRun_AbsInt_Positive;