Mert Efe Sensoy
Back to Blog

Eight Characters: Porting Berkeley SoftFloat to MVS 3.8j

5 min readBy Mert Efe Sensoy

A 1981 operating system gives an external symbol at most eight characters, with letters in uppercase. Berkeley SoftFloat names its functions things like softfloat_roundPackToF64. ONFLY needs both, and reconciling them took three separate failures measured on MVS 3.8j under the Hercules emulator.

Why a fly-brain simulation carries its own floating point

ONFLY runs a fruit fly feeding circuit as a mainframe workload, and requires that identical requests produce identical fingerprints on every platform it targets. Hardware arithmetic cannot deliver that across S/370 hexadecimal floating point and IEEE binary, so the engine implements IEEE 754 in software.

Upstream states the constraint that decides which release you get. Since Release 3, SoftFloat depends on a 64-bit integer type in C, and a compiler without one is directed to the older Release 2c for the 32-bit and 64-bit formats (Berkeley SoftFloat).

GCCMVS is that compiler: it has a 64-bit type but cannot compile it reliably. Measured on TK5, it failed on 64-bit integers in four independent ways: internal compiler errors at four distinct points, a silently wrong variable 64-bit left shift, no 64-bit runtime helpers in PDPCLIB, and a code generation bug asking for them the wrong way. Release 2c's bits32 build carries a float64 as two 32-bit halves and forms no 64-bit integer anywhere.

Three include names that all resolve to one PDS member

GCCMVS resolves #include "name" to the partitioned dataset member given by the first eight characters of the name, uppercased, with punctuation and extension included. Measured on TK5 on 2026-09-11 against a single member SOFTFLOA: the spellings "softfloat.h", "softfloat-macros", "softfloat-specialize" and "softfloa.h" all resolved to it, return code 0.

Release 2c's bits32/softfloat.c includes three of those names, and three files cannot occupy one member. Concatenating libraries on the DD statement does not help, because MVS takes a member from the first library that has it. Renaming the files was off the table, since the vendored tree stays byte-identical to the upstream release, whose archive digests the repository records.

GCC's -remap option exists for filesystems with very short names, which is precisely this situation. On this port it is accepted and inert. The build amalgamates instead: the includes are inlined in order at submit time and never written to disk, so no second copy of the source exists to drift from the first.

What Assembler XF said about 38 of 42 external names

With the includes resolved, Release 2c compiled at return code 0 and the assembler rejected the output:

IFO196  FLOAT64@ HAS BEEN PREVIOUSLY DEFINED
IFO196  FLOAT32@ HAS BEEN PREVIOUSLY DEFINED
IFO196  INT32@TO HAS BEEN PREVIOUSLY DEFINED
IFO189  INVALID ENTRY OPERAND, LINKAGE CANNOT BE PERFORMED

Truncated to eight characters, 38 of Release 2c's 42 externals fall into three colliding groups: eighteen float32_*, eighteen float64_* and two int32_to_*. Splitting the library across translation units only moves the collision from the assembler to the linkage editor.

The remedy is a generated #define prologue that gives every external a compliant name. It is applied on x86 as well as on MVS, so the ordinary test suite exercises the renaming mechanism instead of leaving MVS to discover it.

Five names a #define would have deleted rather than renamed

The same treatment was then extended to Release 3e, so the lint could enforce with no exemption, and five of its thirteen colliding names could not take it. Each is written #ifndef <name> in primitives.h and again in its own source file. That is upstream's documented extension point, where defining the name declares that the platform supplies the function, and upstream then omits both the declaration and the definition.

A rename macro would therefore have removed those five functions. ONFLY uses the extension point as designed: softfloat/onfprim.c carries upstream's own bodies under compliant names, derived mechanically by a script, with third_party/ untouched.

Copying a function body does not prove the copy behaves identically. The evidence is the suite run afterwards: 520,752 TestFloat cases across both backends, 449 kernel steps, and the 13 golden acceptance fingerprints, computed long before any of this existed and all unchanged (VL-28).

The lint that listed SoftFloat's collisions without enforcing them

The project's C-04 lint had been reporting SoftFloat's over-length externals since before the port began, while enforcing only on names ONFLY itself defines. That is how Release 2c reached Assembler XF before anyone noticed. It now enforces, and run against the unrenamed object it reproduces the same three groups the assembler found.

The lint catches the collision before a job is ever submitted. GCCMVS truncates external names to eight characters and uppercases them, so two names that differ only after character 8, or only in case, become one symbol, and Assembler XF rejects the pair (VL-25).

Agreement between the two releases was checked on x86 by running both through the same Berkeley TestFloat vectors: 260,376 cases across six operations, zero mismatches, 18,408 NaN cases excluded. TestFloat computes its expected results with SoftFloat itself (TestFloat), and ONFLY's generator links a stock Release 3e build, so the run shows 2c reproducing 3e's arithmetic on TestFloat's vectors.

What has actually been compiled on MVS so far

The float library and its three test drivers came first. On 2026-09-11 GCCMVS also built ONFLY's engine units on MVS 3.8j TK5 under Hercules, and the whole engine path, from decode through simulation to fingerprint, ran correctly there (VL-31, VL-35). The MVS TestFloat run still covers 4,500 cases against x86's 260,376, stratified rather than a prefix, and still a sample.

Four workarounds are load bearing: the -O1 pin that threads between two distinct compiler bugs, the Hercules 819/1047 codepage, the include amalgamation and the symbol renames. The pin, the amalgamation and the renames are in the repository, in tools/mvsbld.py and a generated header; the codepage is host configuration, and a Hercules restart silently reverts it, so the submit tool refuses to send a job when it has.

ONFLY has since been renamed FlyBatch, and the -O1 pin later proved to carry a silent miscompile of its own, covered in Nineteen Matching Fingerprints Missed a Compiler Bug on MVS.

Browse the C-04 lint and the rename generators in the FlyBatch repository if you are porting anything to a PDS-based toolchain; the eight-character rule will find you before your test suite does.

Sources

Every claim above traces to one of these. Links open in a new tab.

  1. FlyBatch (formerly ONFLY)

    GitHubRepositoryaccessed Oct 1, 2026

  2. Berkeley SoftFloat

    John R. HauserVendor researchaccessed Oct 1, 2026

  3. Berkeley TestFloat

    John R. HauserVendor researchaccessed Oct 1, 2026