Skip to content

Changes allowing old Microsoft C Compilers still to build OCaml#384

Closed
dra27 wants to merge 8 commits intoocaml:trunkfrom
dra27:enable-older-mscrt
Closed

Changes allowing old Microsoft C Compilers still to build OCaml#384
dra27 wants to merge 8 commits intoocaml:trunkfrom
dra27:enable-older-mscrt

Conversation

@dra27
Copy link
Member

@dra27 dra27 commented Dec 31, 2015

The product of some testing of very old Microsoft C Compilers - posting the PR to ask very quickly for help with the very weird patch in 47d6ce2!

Each commit fixes three distinct faults:

  • b9ca576 (see also MPR4887) more correctly bounds the code for _set_invalid_parameter_handler with the proper detection for the secure API changes.
  • 9f310fa fixes the lack of support for variadic macros in all editions of the C pre-processor prior to Visual Studio 2008 (I haven't bothered to correct the DEBUG branch for compilers that old - if someone ever has the need to debug otherlibs/win32unix/select.c on such a compiler, they're welcome to write the tedious macro to support it!)
  • 47d6ce2 is necessary for Unix.in_channel_of_descr to work when compiled with Visual Studio .NET 2003. I freely admit that this compiler is so old as to be almost not worth any attention, but I was wondering if an expert looking at the simple patch (it was identified as a result of inserting printfs to identify the failure and then going from there...) may lead to a more proper patch with comments. It's pretty cool if OCaml does still build successfully on a compiler released 12 years ago - it also has the "benefit" of meaning that the MSVC port can be built using the same runtime as the MinGW port (i.e. you "need" Visual Studio .NET 2003 to build against msvcrt.dll)

Or the whole thing can be dropped - the whole exercise is part of revising the Windows build instructions and seeing just how far back support still extends. I personally find it interesting/impressive that so few changes are apparently necessary...

@dra27
Copy link
Member Author

dra27 commented Dec 31, 2015

A quick test, as I happen to have a VM with Visual Studio 6 on it, confirms that Visual Studio .NET 2003 is the oldest Microsoft C Compiler which can compile OCaml (given these patches) - Visual C++ 6 can't even build ocamlrun 😄

@gasche
Copy link
Member

gasche commented Dec 31, 2015

I had a look a the proposed patches: they look safe (low regression risk).

@dra27
Copy link
Member Author

dra27 commented Dec 31, 2015 via email

@dra27
Copy link
Member Author

dra27 commented Dec 31, 2015

Actually, having just read some more, it would definitely have to change - I assumed fflush(NULL) would be an erroneous call (effectively a no-op) but having RTFM'd, the actual behaviour of flushing all buffers is really not what I meant! However, fflush(stdin) also "works"...

@dra27 dra27 force-pushed the enable-older-mscrt branch from 47d6ce2 to 670f21f Compare January 1, 2016 15:17
@dra27
Copy link
Member Author

dra27 commented Jan 1, 2016

One bug fix, one update in the light of some further testing:

  • The DEBUG_PRINT fix test was incorrect and was breaking mingw - added defined(_MSC_VER) && which fixes that 😊
  • As I was there, updated the compilation for Visual C++ 2003 to "flush" stdin and also added a guard around it so that it's only used for versions of Visual Studio older than 2005, but this still isn't a patch that should be committed...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't that rather be:
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) && __STDC_SECURE_LIB__ >= 200411L
?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find a place either for GCC or MSVC where it's explicitly documented, but an undefined token is treated as zero (this certainly works for the relevant old versions of cl)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't know that … it's in the C standard (§ 6.10.1):

After all replacements due to macro expansion and the defined unary operator have been performed, all remaining identifiers (including those lexically identical to keywords) are replaced with the pp-number 0

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah - good find! I was going to say that in the absence of documentation confirming the behaviour, it would be better to change it!

@dra27 dra27 force-pushed the enable-older-mscrt branch 2 times, most recently from d593a89 to 8155ea4 Compare January 9, 2016 14:19
@dra27
Copy link
Member Author

dra27 commented Jan 12, 2016

Four more commits extending OCaml right the way back to Visual Studio 6.0! I don't have a copy of Visual Studio 5.0, but I did test with Visual C++ 4 (!) which is unable to correctly parse the Windows 2003 Platform SDK headers, so for now Visual Studio 6 (1998) remains the oldest! I'm in the process of setting up a CI server for all of these versions, so the changes for now would not be abandoned. The patch for _vscprintf may well get adapted further - I think permitting that code to be used in the mingw port will re-enable Windows 2000 "support".

@gasche
Copy link
Member

gasche commented Jan 12, 2016

cc @damiendoligez @xavierleroy ?

I would be in favor of merging these, but there is a mild risk of Windows regression so I wonder if Damien would want it in the 4.03 release.

@dra27
Copy link
Member Author

dra27 commented Jan 12, 2016

I'm still running a lot of tests to verify these patches...

@dra27 dra27 force-pushed the enable-older-mscrt branch from d573fe9 to 6ee095f Compare January 12, 2016 14:57
@dra27
Copy link
Member Author

dra27 commented Jan 12, 2016

... and those tests were quite important because the floating point code had introduced a devastating regression in byterun/floats.c 😊

byterun/floats.c Outdated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did the return type disappear??

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh - a refactoring typo, I expect. It's amazing (by which I mean horrifying!) that it still works!

@damiendoligez
Copy link
Member

This is interesting, but I'm not feeling much pressure to integrate it before 4.03. On the other hand, I'm a bit wary of possible regressions.

Let's merge this into trunk just after branching 4.03.

@damiendoligez damiendoligez added this to the 4.04-or-later milestone Jan 12, 2016
@dra27
Copy link
Member Author

dra27 commented Jan 12, 2016

You should be wary - 6573528 has introduced an msvc64 regression 😡 (which I shall shortly push a fix for).
It'll be great to see this on trunk and then use CI to track when supporting these old compilers really does become too much burden!
Is there a fixed date for branching 4.03?

@dra27 dra27 force-pushed the enable-older-mscrt branch from 6ee095f to 89cba28 Compare January 12, 2016 18:41
@alainfrisch
Copy link
Contributor

@dra27 Out of curiosity: which of older versions of MSVC have free versions available?

@dra27
Copy link
Member Author

dra27 commented Jan 15, 2016

Visual Studio 2005 is the first one, I think - although there are various compilers and assemblers in older Windows SDKs, they were by MSDN subscription only, so not free. Added to that is that Microsoft only started including an x86 compiler in the Windows SDK with Windows Vista IIRC. I've got copies of VS 2002 & 2003 Pro thanks the Microsoft e-Academy licensing from when I was at Cambridge, and I bought VS 6 very cheaply shortly after VS 2002 came out. What was remarkably is the number of files still available from microsoft.com if you can determine the full URL! I managed to get 90/180 day trial of all versions from Visual Studio 2005+ onwards.
I haven't yet written it up properly, but there's a lot of info (including download URLs) on the page "Visual Studio Testing" at http://1drv.ms/1QeDkV0). There are free versions of both x64 and x86 available for Visual Studio 2008 onwards - the only free version of Visual Studio 2005 x64 (at least that I can find) is a pre-release version included in the Windows Server 2003 SP1 SDK.

@alainfrisch
Copy link
Contributor

While I really appreciate the effort, I'm wondering whether the added complexity is worth it. @dra27 : do you have an actual need for supporting older MS C compilers, or is this work just a (fun) challenge?

@dra27
Copy link
Member Author

dra27 commented Jan 18, 2016

(As it happens, I am still forced to use Visual Studio 6 for one client, but not for anything OCaml-related!!)
It was indeed as a challenge to see how far back it goes - what surprised me is that the complexity is fairly minimal and the significant parts affect only otherwise highly stable parts of the runtime. The blocks are all clearly delineated with #ifdef's showing that they're specific to very old compilers.
Assuming I can get CI running for this, would it be OK to put in trunk (post 4.03) so that support for them gets dropped when it's actually too much hassle (new feature requiring special support) rather than because they're old?

@dra27 dra27 force-pushed the enable-older-mscrt branch from 89cba28 to 9a56541 Compare February 16, 2016 16:46
There is still at least one Microsoft-supported version of the CRT which
does not include the secure versions of various functions, and
consequently does not include `_set_invalid_parameter_handler`.

Tests for the definition and inclusion of
caml_install_invalid_parameter_handler refined to detect
`__STDC_SECURE_LIB__` defined in `crtdefs.h`. This is a badly documented,
but standardised, define. Although `__STDC_SECURE_LIB__` is compatible
with MinGW, I have retained the test for `_MSC_VER` and so kept this as an
MSVC-only patch.
Support was added for variadic macros in the RTM version of Visual Studio
2005 (CL Version 14), but there are variants of the version 14 compiler
included in SDKs released before Visual Studio 2005 itself which do not
support them. Fix the non-DEBUG version of win32unix to compile correctly
and not display a warning for DEBUG_PRINT calls in
`otherlibs/win32unix/select.c`.
Microsoft introduced the `LL` suffix for integer literals in Visual
Studio .NET 2003 - earlier versions use `i64`
Visual Studio 6 and earlier have somewhat insane handling of comparisons
with nan values. Provide alternate (slower) versions of float comparison
functions using isnan rather than standardized comparison behaviour.
_vscprintf was added to Microsoft Visual C++ in .NET 2002. Provide an
implementation of it for older compilers.
Older Microsoft C compilers can't generate code for casting unsigned
__int64 to double and the __pragma directive is not available for
suppressing warnings.
This bizarre patch results in a full test-suite pass for Visual Studio
.NET 2002 & 2003. Without it, the following code segfaults (resulting in
6 failing tests):

```ocaml
  #load "unix.cma";;
  let (ifd, _) = Unix.pipe ();;
  Unix.in_channel_of_descr ifd;;
```
@dra27 dra27 force-pushed the enable-older-mscrt branch from 9a56541 to 66b579f Compare February 16, 2016 16:55
@alainfrisch
Copy link
Contributor

Thanks! Rebased and merged on trunk.

@dra27
Copy link
Member Author

dra27 commented Mar 10, 2016

Ta - there will be a few others when I get back to it ... the symlink stuff requires a few tweaks in the headers, but I'll open a new PR, obviously

@dra27 dra27 deleted the enable-older-mscrt branch April 6, 2016 14:04
EduardoRFS pushed a commit to EduardoRFS/ocaml that referenced this pull request Dec 6, 2020
Add a primitive to insert nop instruction
mshinwell pushed a commit to mshinwell/ocaml that referenced this pull request Apr 1, 2021
stedolan pushed a commit to stedolan/ocaml that referenced this pull request Dec 13, 2021
chambart pushed a commit to chambart/ocaml-1 that referenced this pull request Jan 4, 2022
chambart pushed a commit to chambart/ocaml-1 that referenced this pull request Feb 1, 2022
23a7f73 flambda-backend: Fix some Debuginfo.t scopes in the frontend (ocaml#248)
33a04a6 flambda-backend: Attempt to shrink the heap before calling the assembler (ocaml#429)
8a36a16 flambda-backend: Fix to allow stage 2 builds in Flambda 2 -Oclassic mode (ocaml#442)
d828db6 flambda-backend: Rename -no-extensions flag to -disable-all-extensions (ocaml#425)
68c39d5 flambda-backend: Fix mistake with extension records (ocaml#423)
423f312 flambda-backend: Refactor -extension and -standard flags (ocaml#398)
585e023 flambda-backend: Improved simplification of array operations (ocaml#384)
faec6b1 flambda-backend: Typos (ocaml#407)
8914940 flambda-backend: Ensure allocations are initialised, even dead ones (ocaml#405)
6b58001 flambda-backend: Move compiler flag -dcfg out of ocaml/ subdirectory (ocaml#400)
4fd57cf flambda-backend: Use ghost loc for extension to avoid expressions with overlapping locations (ocaml#399)
8d993c5 flambda-backend: Let's fix instead of reverting flambda_backend_args (ocaml#396)
d29b133 flambda-backend: Revert "Move flambda-backend specific flags out of ocaml/ subdirectory (ocaml#382)" (ocaml#395)
d0cda93 flambda-backend: Revert ocaml#373 (ocaml#393)
1c6eee1 flambda-backend: Fix "make check_all_arches" in ocaml/ subdirectory (ocaml#388)
a7960dd flambda-backend: Move flambda-backend specific flags out of ocaml/ subdirectory (ocaml#382)
bf7b1a8 flambda-backend: List and Array Comprehensions (ocaml#147)
f2547de flambda-backend: Compile more stdlib files with -O3 (ocaml#380)
3620c58 flambda-backend: Four small inliner fixes (ocaml#379)
2d165d2 flambda-backend: Regenerate ocaml/configure
3838b56 flambda-backend: Bump Menhir to version 20210419 (ocaml#362)
43c14d6 flambda-backend: Re-enable -flambda2-join-points (ocaml#374)
5cd2520 flambda-backend: Disable inlining of recursive functions by default (ocaml#372)
e98b277 flambda-backend: Import ocaml#10736 (stack limit increases) (ocaml#373)
82c8086 flambda-backend: Use hooks for type tree and parse tree (ocaml#363)
33bbc93 flambda-backend: Fix parsecmm.mly in ocaml subdirectory (ocaml#357)
9650034 flambda-backend: Right-to-left evaluation of arguments of String.get and friends (ocaml#354)
f7d3775 flambda-backend: Revert "Magic numbers" (ocaml#360)
0bd2fa6 flambda-backend: Add [@inline ready] attribute and remove [@inline hint] (not [@inlined hint]) (ocaml#351)
cee74af flambda-backend: Ensure that functions are evaluated after their arguments (ocaml#353)
954be59 flambda-backend: Bootstrap
dd5c299 flambda-backend: Change prefix of all magic numbers to avoid clashes with upstream.
c2b1355 flambda-backend: Fix wrong shift generation in Cmm_helpers (ocaml#347)
739243b flambda-backend: Add flambda_oclassic attribute (ocaml#348)
dc9b7fd flambda-backend: Only speculate during inlining if argument types have useful information (ocaml#343)
aa190ec flambda-backend: Backport fix from PR#10719 (ocaml#342)
c53a574 flambda-backend: Reduce max inlining depths at -O2 and -O3 (ocaml#334)
a2493dc flambda-backend: Tweak error messages in Compenv.
1c7b580 flambda-backend: Change Name_abstraction to use a parameterized type (ocaml#326)
07e0918 flambda-backend: Save cfg to file (ocaml#257)
9427a8d flambda-backend: Make inlining parameters more aggressive (ocaml#332)
fe0610f flambda-backend: Do not cache young_limit in a processor register (upstream PR 9876) (ocaml#315)
56f28b8 flambda-backend: Fix an overflow bug in major GC work computation (ocaml#310)
8e43a49 flambda-backend: Cmm invariants (port upstream PR 1400) (ocaml#258)
e901f16 flambda-backend: Add attributes effects and coeffects (#18)
aaa1cdb flambda-backend: Expose Flambda 2 flags via OCAMLPARAM (ocaml#304)
62db54f flambda-backend: Fix freshening substitutions
57231d2 flambda-backend: Evaluate signature substitutions lazily (upstream PR 10599) (ocaml#280)
a1a07de flambda-backend: Keep Sys.opaque_identity in Cmm and Mach (port upstream PR 9412) (ocaml#238)
faaf149 flambda-backend: Rename Un_cps -> To_cmm (ocaml#261)
ecb0201 flambda-backend: Add "-dcfg" flag to ocamlopt (ocaml#254)
32ec58a flambda-backend: Bypass Simplify (ocaml#162)
bd4ce4a flambda-backend: Revert "Semaphore without probes: dummy notes (ocaml#142)" (ocaml#242)
c98530f flambda-backend: Semaphore without probes: dummy notes (ocaml#142)
c9b6a04 flambda-backend: Remove hack for .depend from runtime/dune  (ocaml#170)
6e5d4cf flambda-backend: Build and install Semaphore (ocaml#183)
924eb60 flambda-backend: Special constructor for %sys_argv primitive (ocaml#166)
2ac6334 flambda-backend: Build ocamldoc (ocaml#157)
c6f7267 flambda-backend: Add -mbranches-within-32B to major_gc.c compilation (where supported)
a99fdee flambda-backend: Merge pull request ocaml#10195 from stedolan/mark-prefetching
bd72dcb flambda-backend: Prefetching optimisations for sweeping (ocaml#9934)
27fed7e flambda-backend: Add missing index param for Obj.field (ocaml#145)
cd48b2f flambda-backend: Fix camlinternalOO at -O3 with Flambda 2 (ocaml#132)
9d85430 flambda-backend: Fix testsuite execution (ocaml#125)
ac964ca flambda-backend: Comment out `[@inlined]` annotation. (ocaml#136)
ad4afce flambda-backend: Fix magic numbers (test suite) (ocaml#135)
9b033c7 flambda-backend: Disable the comparison of bytecode programs (`ocamltest`) (ocaml#128)
e650abd flambda-backend: Import flambda2 changes (`Asmpackager`) (ocaml#127)
14dcc38 flambda-backend: Fix error with Record_unboxed (bug in block kind patch) (ocaml#119)
2d35761 flambda-backend: Resurrect [@inline never] annotations in camlinternalMod (ocaml#121)
f5985ad flambda-backend: Magic numbers for cmx and cmxa files (ocaml#118)
0e8b9f0 flambda-backend: Extend conditions to include flambda2 (ocaml#115)
99870c8 flambda-backend: Fix Translobj assertions for Flambda 2 (ocaml#112)
5106317 flambda-backend: Minor fix for "lazy" compilation in Matching with Flambda 2 (ocaml#110)
dba922b flambda-backend: Oclassic/O2/O3 etc (ocaml#104)
f88af3e flambda-backend: Wire in the remaining Flambda 2 flags (ocaml#103)
678d647 flambda-backend: Wire in the Flambda 2 inlining flags (ocaml#100)
1a8febb flambda-backend: Formatting of help text for some Flambda 2 options (ocaml#101)
9ae1c7a flambda-backend: First set of command-line flags for Flambda 2 (ocaml#98)
bc0bc5e flambda-backend: Add config variables flambda_backend, flambda2 and probes (ocaml#99)
efb8304 flambda-backend: Build our own ocamlobjinfo from tools/objinfo/ at the root (ocaml#95)
d2cfaca flambda-backend: Add mutability annotations to Pfield etc. (ocaml#88)
5532555 flambda-backend: Lambda block kinds (ocaml#86)
0c597ba flambda-backend: Revert VERSION, etc. back to 4.12.0 (mostly reverts 822d0a0 from upstream 4.12) (ocaml#93)
037c3d0 flambda-backend: Float blocks
7a9d190 flambda-backend: Allow --enable-middle-end=flambda2 etc (ocaml#89)
9057474 flambda-backend: Root scanning fixes for Flambda 2 (ocaml#87)
08e02a3 flambda-backend: Ensure that Lifthenelse has a boolean-valued condition (ocaml#63)
77214b7 flambda-backend: Obj changes for Flambda 2 (ocaml#71)
ecfdd72 flambda-backend: Cherry-pick 9432cfdadb043a191b414a2caece3e4f9bbc68b7 (ocaml#84)
d1a4396 flambda-backend: Add a `returns` field to `Cmm.Cextcall` (ocaml#74)
575dff5 flambda-backend: CMM traps (ocaml#72)
8a87272 flambda-backend: Remove Obj.set_tag and Obj.truncate (ocaml#73)
d9017ae flambda-backend: Merge pull request ocaml#80 from mshinwell/fb-backport-pr10205
3a4824e flambda-backend: Backport PR#10205 from upstream: Avoid overwriting closures while initialising recursive modules
f31890e flambda-backend: Install missing headers of ocaml/runtime/caml (ocaml#77)
83516f8 flambda-backend: Apply node created for probe should not be annotated as tailcall (ocaml#76)
bc430cb flambda-backend: Add Clflags.is_flambda2 (ocaml#62)
ed87247 flambda-backend: Preallocation of blocks in Translmod for value let rec w/ flambda2 (ocaml#59)
a4b04d5 flambda-backend: inline never on Gc.create_alarm (ocaml#56)
cef0bb6 flambda-backend: Config.flambda2 (ocaml#58)
ff0e4f7 flambda-backend: Pun labelled arguments with type constraint in function applications (ocaml#53)
d72c5fb flambda-backend: Remove Cmm.memory_chunk.Double_u (ocaml#42)
9d34d99 flambda-backend: Install missing artifacts
10146f2 flambda-backend: Add ocamlcfg (ocaml#34)
819d38a flambda-backend: Use OC_CFLAGS, OC_CPPFLAGS, and SHAREDLIB_CFLAGS for foreign libs (#30)
f98b564 flambda-backend: Pass -function-sections iff supported. (#29)
e0eef5e flambda-backend: Bootstrap (#11 part 2)
17374b4 flambda-backend: Add [@@Builtin] attribute to Primitives (#11 part 1)
85127ad flambda-backend: Add builtin, effects and coeffects fields to Cextcall (#12)
b670bcf flambda-backend: Replace tuple with record in Cextcall (#10)
db451b5 flambda-backend: Speedups in Asmlink (#8)
2fe489d flambda-backend: Cherry-pick upstream PR#10184 from upstream, dynlink invariant removal (rev 3dc3cd7 upstream)
d364bfa flambda-backend: Local patch against upstream: enable function sections in the Dune build
886b800 flambda-backend: Local patch against upstream: remove Raw_spacetime_lib (does not build with -m32)
1a7db7c flambda-backend: Local patch against upstream: make dune ignore ocamldoc/ directory
e411dd3 flambda-backend: Local patch against upstream: remove ocaml/testsuite/tests/tool-caml-tex/
1016d03 flambda-backend: Local patch against upstream: remove ocaml/dune-project and ocaml/ocaml-variants.opam
93785e3 flambda-backend: To upstream: export-dynamic for otherlibs/dynlink/ via the natdynlinkops files (still needs .gitignore + way of generating these files)
63db8c1 flambda-backend: To upstream: stop using -O3 in otherlibs/Makefile.otherlibs.common
eb2f1ed flambda-backend: To upstream: stop using -O3 for dynlink/
6682f8d flambda-backend: To upstream: use flambda_o3 attribute instead of -O3 in the Makefile for systhreads/
de197df flambda-backend: To upstream: renamed ocamltest_unix.xxx files for dune
bf3773d flambda-backend: To upstream: dune build fixes (depends on previous to-upstream patches)
6fbc80e flambda-backend: To upstream: refactor otherlibs/dynlink/, removing byte/ and native/
71a03ef flambda-backend: To upstream: fix to Ocaml_modifiers in ocamltest
686d6e3 flambda-backend: To upstream: fix dependency problem with Instruct
c311155 flambda-backend: To upstream: remove threadUnix
52e6e78 flambda-backend: To upstream: stabilise filenames used in backtraces: stdlib/, otherlibs/systhreads/, toplevel/toploop.ml
7d08e0e flambda-backend: To upstream: use flambda_o3 attribute in stdlib
403b82e flambda-backend: To upstream: flambda_o3 attribute support (includes bootstrap)
65032b1 flambda-backend: To upstream: use nolabels attribute instead of -nolabels for otherlibs/unix/
f533fad flambda-backend: To upstream: remove Compflags, add attributes, etc.
49fc1b5 flambda-backend: To upstream: Add attributes and bootstrap compiler
a4b9e0d flambda-backend: Already upstreamed: stdlib capitalisation patch
4c1c259 flambda-backend: ocaml#9748 from xclerc/share-ev_defname (cherry-pick 3e937fc)
00027c4 flambda-backend: permanent/default-to-best-fit (cherry-pick 64240fd)
2561dd9 flambda-backend: permanent/reraise-by-default (cherry-pick 50e9490)
c0aa4f4 flambda-backend: permanent/gc-tuning (cherry-pick e9d6d2f)

git-subtree-dir: ocaml
git-subtree-split: 23a7f73
EmileTrotignon pushed a commit to EmileTrotignon/ocaml that referenced this pull request Jan 12, 2024
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.

5 participants