Skip to content

portable: two fixes - #620

Open
cyclistmass wants to merge 2 commits into
Clozure:arm64from
cyclistmass:portable-two
Open

portable: two fixes#620
cyclistmass wants to merge 2 commits into
Clozure:arm64from
cyclistmass:portable-two

Conversation

@cyclistmass

Copy link
Copy Markdown
Contributor

1. *compile-file-pathname* must hold a pathname, not a namestring.
CLHS gives it a value type of pathname or nil. CCL binds it to a STRING
whenever physical-pathname-p is true of the merged source, which needs a
directory component and is therefore the common case.

back-translate-pathname loses the type at lib/pathnames.lisp:50-55:

(defun back-translate-pathname (path &optional hosts)
  (let ((newpath (back-translate-pathname-1 path hosts)))
    (cond ((equalp path newpath)
           (namestring (pathname path)))   ; a STRING
          (t newpath))))                   ; a PATHNAME

back-translate-pathname-1 returns its argument unchanged when no logical-host
translation matches, so almost every caller gets the string branch.
%compile-file is the caller that stores the result where the standard
constrains it: lib/nfcomp.lisp:204 gates on physical-pathname-p, :205
back-translates, and :221 binds *compile-file-pathname* to the result.

Measured on x86-64 at 1.13 (v1.13-309-g7cff8ca5), varying only
*default-pathname-defaults*:

DPD = #P""              -> #P"...-5.lsp"   PATHNAME             CF.16 passes
DPD = #P"<dir>/"        -> "<dir>/...lsp"  SIMPLE-BASE-STRING   CF.16 fails

ansi-tests COMPILE-FILE.16 fails on stock x86-64 CCL at 1.12.2 and at 1.13,
21679 tests with that one failure. A red-then-green in one process, redefining
only this function, moves value 2 of the test from FAIL to PASS.
*compile-file-truename* is correct throughout and passes in both halves.

That gate is also where the failure hides: a suite driver that sets
*default-pathname-defaults* exposes the defect, and one that only chdirs does
not. Our own arm64 suite reports no failure for that second reason, so it is
not evidence either way.

The fix goes at the function, not at the binding. The three other callers in
the tree, at lib/source-files.lisp:745, cocoa-ide/search-files.lisp:542 and
cocoa-ide/search-files-pre-lion.lisp:188, all wrap the result in namestring
already, so none of them can observe the change. The fix site is byte-identical
on master and arm64.

2. Keep x8664-only tag constants out of the shared dispatch.
library/core-files.lisp compiles only under #+:linuxx8664-target today, and
its central tag dispatch reads as if it were shared, because it uses target::
constants. Two of those exist only in the x8664 tag model: TAG-TRA and
FULLTAG-FUNCTION. On arm64 and ppc64 a function is an ordinary uvector
reached through the FULLTAG-MISC clause. Those two clauses get
#+x8664-target, the idiom l1-clos-boot.lisp already uses for the same
per-target dispatch.

uvheader-type in the same file hard-codes x8664:: for the five
header-class fulltags, and those differ across targets. x8664 has
fulltag-nodeheader-1 = 6 while arm64 has fulltag-nodeheader-0 = 6. On any
other target the code would look up every uvector header class in the wrong
table and report the wrong type in silence. An arm64 function header, subtag
#x96 with low nibble 6, would land under *nodeheader-1-types*.

Both changes are no-ops on linuxx8664, where TARGET is the X8664 package.
Neither one enables core-files anywhere new on its own.


Verified at this base. Built and run on linuxarm64 at ec578745 with all
ten patches applied: ANSI 21679 tests, 0 failures. tests/ccl.lsp 243 tests, 0
failures. Image 281a49e5, kernel 67bb66b4.

library/core-files.lisp is compiled only under #+:linuxx8664-target
today, but its central tag dispatch is written against target::
constants as if it were shared code.  Two of them exist only in the
x8664 tag model: TAG-TRA (x8664-arch.lisp is the only definer in the
whole tree) and FULLTAG-FUNCTION (on arm64 and ppc64 a function is an
ordinary uvector reached through the FULLTAG-MISC clause; the arm64
tag assignment deliberately has no function fulltag).  Guard those two
clauses with #+x8664-target, the idiom l1-clos-boot.lisp already uses
for the same per-target dispatch in the %class-of table.

uvheader-type, in the same file, hard-codes x8664:: for the five
header-class fulltags.  Those values differ across targets -- x8664
has fulltag-nodeheader-1 = 6 while arm64 has fulltag-nodeheader-0 = 6
-- so on any other target every uvector header class would be looked
up in the wrong table and the reported type would be silently wrong:
an arm64 function header (subtag #x96, low nibble 6) would be filed
under *nodeheader-1-types* instead of *nodeheader-0-types*.  Use
target:: so the same-architecture assumption the file already states
("Assumes we're running on same architecture as core file") holds.

Both changes are no-ops on linuxx8664, where TARGET is the X8664
package.  They do not enable core-files anywhere new by themselves:
off linuxx8664 the file still needs, at least, %%raw-obj (a
defx86lapfunction), uvheader-byte-size (x8664-misc-byte-count),
uvheader-p's numeric header-class range, the recover-fn-from-rip-*
function recovery, and an xcf layout on targets that lack one.  Note
also that level-1/l1-boot-2.lisp boot-provides "CORE-FILES" on every
platform, so off linuxx8664 (require 'core-files) silently no-ops
instead of reporting that the facility does not exist.
…tring

CLHS says *compile-file-pathname* is "the pathname of the file being compiled,
merged with the defaults", and its value type is a pathname or nil.  In CCL it
is a STRING whenever the source pathname has a directory component, which is to
say almost always outside a bare `(compile-file "foo.lisp")' from the current
directory.

ansi-tests COMPILE-FILE.16 has been failing on this for as long as we have been
running the suite.  It is not architecture-specific -- it reproduces identically
on x86-64 at 1.12.2 and at 1.13, and the source is portable.

=== THE PATH THE VALUE TAKES

lib/nfcomp.lisp starts with a pathname, and every step keeps it one:

    (let* ((orig-src (merge-pathnames src))          ; a PATHNAME
    ...
    (when (physical-pathname-p orig-src)
      (setq orig-src (back-translate-pathname orig-src '("home" "ccl"))))
    ...
           (*compile-file-pathname* orig-src)
back-translate-pathname is where the type is lost.  lib/pathnames.lisp:

    (defun back-translate-pathname (path &optional hosts)
      (let ((newpath (back-translate-pathname-1 path hosts)))
        (cond ((equalp path newpath)
               ;; (fcomp-standard-source path)
               (namestring (pathname path)))        ; <- a STRING
              (t newpath))))                        ; <- a PATHNAME

The function returns a string or a pathname depending on whether a logical-host
translation happened to match.  back-translate-pathname-1 returns its argument
unchanged when nothing matches, which is the common case, so the string branch
is the one almost every caller gets.  A function named back-translate-PATHNAME
returning a namestring half the time is the defect; %compile-file is just the
caller that stores the result somewhere the standard constrains.

The commented-out `(fcomp-standard-source path)' on the line above is the
vestige of whatever this once did.

=== MEASURED, both branches, on 1.13 (v1.13-309-g7cff8ca5) x86-64

*compile-file-pathname* after compiling the same file, varying only
*default-pathname-defaults*:

  DPD = #P""                       -> #P"compile-file-test-file-5.lsp"
                                      type PATHNAME               COMPILE-FILE.16 passes
  DPD = #P"<dir>/ansi-tests/"      -> "<dir>/ansi-tests/compile-file-test-file-5.lsp"
                                      type SIMPLE-BASE-STRING       COMPILE-FILE.16 fails

physical-pathname-p (lib/pathnames.lisp) is the gate, and it requires a
directory component:

  #P"<dir>/ansi-tests/compile-file-test-file-5.lsp"  physical-pathname-p = T   branch RUNS
  #P"tests:ansi-tests;compile-file-test-file-5.lsp"  physical-pathname-p = NIL  branch SKIPPED

That is also why the failure looks intermittent across harnesses rather than
constant: a suite driver that sets *default-pathname-defaults* to a directory
exposes it, and one that only chdir's does not, because CCL's `cwd'
(level-1/l1-files.lisp) calls %chdir and never assigns
*default-pathname-defaults*.

Red-then-green control, in one process, redefining only this function:

  BEFORE  *compile-file-pathname* = "<dir>/ansi-tests/compile-file-test-file-5.lsp"
          (SIMPLE-BASE-STRING 57)            COMPILE-FILE.16 value 2 -> FAIL
  AFTER   *compile-file-pathname* = #P"<dir>/ansi-tests/compile-file-test-file-5.lsp"
          PATHNAME                           COMPILE-FILE.16 value 2 -> PASS

*compile-file-truename* was correct throughout (nfcomp.lisp is
`(truename src)'), and passes in both halves -- only the second value of the
test moves.

=== WHY THE FIX IS AT THE FUNCTION AND NOT AT THE BINDING

Coercing at nfcomp.lisp would fix COMPILE-FILE.16 and leave the
type-inconsistent function in place for the next caller.  Every other caller in
the tree already wraps the result in `namestring', so none of them can observe
the change:

    lib/source-files.lisp                (namestring (back-translate-pathname file-name))
    cocoa-ide/search-files.lisp          (namestring (ccl::back-translate-pathname ...))
    cocoa-ide/search-files-pre-lion.lisp (namestring (ccl::back-translate-pathname ...))

and `(namestring <pathname>)' is the same string it returns today.  Inside
%compile-file the only other use of orig-src is
`(namestring orig-src)', which is likewise unaffected.

The fix site is byte-identical on origin/master and origin/arm64.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant