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:
parent
5dd6a3d8c9
commit
161d8e4102
|
|
@ -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]));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue