port: add MSVC600 CPython compatibility layer
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
/* Focused, isolated compile smoke test for Include/internal/pycore_atomic.h
|
||||
* under VC6/_M_IX86.
|
||||
*
|
||||
* Deliberately does NOT go through Python.h or any other pycore_*.h header:
|
||||
* pycore_interp.h (reached transitively from a real core translation unit
|
||||
* like Modules/main.c) additionally requires <stdbool.h>, a separate,
|
||||
* unrelated VC6 gap tracked in PORT_STATUS.md. Isolating pycore_atomic.h
|
||||
* here keeps this probe's result attributable to the atomics compatibility
|
||||
* work specifically.
|
||||
*
|
||||
* Py_BUILD_CORE is supplied by the probe script (/D Py_BUILD_CORE), the
|
||||
* same way it is for a real core translation unit. <inttypes.h> (resolved
|
||||
* to compat/msvc600/inttypes.h for VC6 via the include path) provides
|
||||
* uintptr_t, which pycore_atomic.h needs but does not itself include; in
|
||||
* a real build this comes from Python.h/pyport.h, included ahead of any
|
||||
* pycore_*.h header.
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
#include "pycore_atomic.h"
|
||||
|
||||
static _Py_atomic_int atomic_int_value;
|
||||
static _Py_atomic_address atomic_addr_value;
|
||||
|
||||
int
|
||||
vc6_atomic_smoke(void)
|
||||
{
|
||||
_Py_atomic_store(&atomic_int_value, 1);
|
||||
_Py_atomic_store_relaxed(&atomic_int_value, 2);
|
||||
_Py_atomic_store(&atomic_addr_value, (uintptr_t)0);
|
||||
_Py_atomic_store_relaxed(&atomic_addr_value, (uintptr_t)1);
|
||||
|
||||
return _Py_atomic_load(&atomic_int_value)
|
||||
+ _Py_atomic_load_relaxed(&atomic_int_value)
|
||||
+ (int)_Py_atomic_load(&atomic_addr_value)
|
||||
+ (int)_Py_atomic_load_relaxed(&atomic_addr_value);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/* Focused compile smoke test for the VC6 C99 compound-literal /
|
||||
* designated-initializer fixes applied to cpython/Modules/main.c.
|
||||
*
|
||||
* VC6 (a pre-C99 compiler) supports neither C99 compound literals
|
||||
* (`(PyStatus){...}`) nor C99 designated initializers (`{ .field = value,
|
||||
* ... }`), at any position in a block. Modules/main.c relied on both via
|
||||
* three distinct patterns:
|
||||
*
|
||||
* 1. `status = _PyStatus_OK();` -- a compound-literal macro used as a
|
||||
* plain assignment's right-hand side. Fixed by calling the existing
|
||||
* public API function PyStatus_Ok() instead, which
|
||||
* Python/initconfig.c defines as `return _PyStatus_OK();` -- the
|
||||
* identical value, no header change needed.
|
||||
* 2. `cf = _PyCompilerFlags_INIT;` -- likewise a compound-literal macro
|
||||
* used as a plain assignment. Fixed by assigning PyCompilerFlags's
|
||||
* two fields explicitly (cf_flags, cf_feature_version), matching the
|
||||
* macro's own definition field-for-field.
|
||||
* 3. `_PyArgv args = { .argc = argc, ... };` -- a designated-initializer
|
||||
* aggregate initializer with no macro involved. Fixed by declaring
|
||||
* the variable bare and assigning each field explicitly; field order
|
||||
* does not matter for plain assignment, unlike an initializer list.
|
||||
*
|
||||
* See PORT_STATUS.md ("Compound-literal/designated-initializer slice") for
|
||||
* full detail. Like vc6_mixed_decls_smoke.c, this fixture includes the
|
||||
* real "Python.h" (Py_BUILD_CORE is supplied by the probe script) because
|
||||
* PyStatus, PyCompilerFlags, and _PyArgv are real CPython types pulled in
|
||||
* through the normal public/internal header chain, the same way
|
||||
* Modules/main.c itself reaches them.
|
||||
*/
|
||||
#include "Python.h"
|
||||
#include "pycore_initconfig.h" // _PyArgv, _PyStatus_OK()
|
||||
|
||||
PyStatus
|
||||
vc6_status_ok_smoke(void)
|
||||
{
|
||||
PyStatus status;
|
||||
|
||||
/* pattern 1: was `status = _PyStatus_OK();` */
|
||||
status = PyStatus_Ok();
|
||||
return status;
|
||||
}
|
||||
|
||||
PyCompilerFlags
|
||||
vc6_compiler_flags_smoke(void)
|
||||
{
|
||||
PyCompilerFlags cf;
|
||||
|
||||
/* pattern 2: was `cf = _PyCompilerFlags_INIT;` */
|
||||
cf.cf_flags = 0;
|
||||
cf.cf_feature_version = PY_MINOR_VERSION;
|
||||
return cf;
|
||||
}
|
||||
|
||||
Py_ssize_t
|
||||
vc6_argv_designated_init_smoke(int argc, wchar_t **argv)
|
||||
{
|
||||
/* pattern 3: was `_PyArgv args = { .argc = argc, ... };` */
|
||||
_PyArgv args;
|
||||
args.argc = argc;
|
||||
args.use_bytes_argv = 0;
|
||||
args.bytes_argv = NULL;
|
||||
args.wchar_argv = argv;
|
||||
return args.argc;
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/* Focused compile smoke test for the VC6 C89-mixed-declarations fixes
|
||||
* applied to cpython/Modules/main.c.
|
||||
*
|
||||
* VC6 is a C89-only compiler (plus Microsoft extensions): block-scope
|
||||
* declarations must precede all statements in that block. Modules/main.c
|
||||
* declared many locals mid-function (typically right before first use,
|
||||
* C99 style); the fix hoists each declaration to the top of its enclosing
|
||||
* block, without initializer, and turns the original `TYPE name = expr;`
|
||||
* into a plain `name = expr;` assignment at its original position. This
|
||||
* changes no evaluation order and no semantics. See PORT_STATUS.md
|
||||
* ("Modules/main.c mixed-declarations slice") for full detail.
|
||||
*
|
||||
* This fixture is deliberately self-contained (no CPython headers) so it
|
||||
* exercises the *shape* of every hoist pattern used in main.c, independent
|
||||
* of main.c's own evolution:
|
||||
*
|
||||
* 1. a bare declaration hoisted above an early-return statement
|
||||
* (pymain_init's `preconfig`/`config`);
|
||||
* 2. a declaration whose initializer is a side-effecting call, hoisted
|
||||
* bare and assigned at its original site so the call itself still
|
||||
* happens after the same preceding statements
|
||||
* (pymain_import_readline's `mod`, pymain_run_file_obj's `fp`);
|
||||
* 3. two declarations in a row where hoisting the first turns its site
|
||||
* into an assignment statement, which would otherwise make the
|
||||
* second declaration (immediately following) a new violation too
|
||||
* (pymain_run_file_obj's `cf`/`run`);
|
||||
* 4. a declaration inside a nested block (its own scope), after
|
||||
* statements already executed in that block (pymain_run_stdin's
|
||||
* nested `exitcode`);
|
||||
* 5. a platform-conditional declaration with the same name but a
|
||||
* different type per branch, hoisted under the same #ifdef structure
|
||||
* at the top of the function (pymain_run_startup's `env`).
|
||||
*/
|
||||
|
||||
#define TARGET_WINDOWS 1
|
||||
|
||||
/* --- pattern 1: bare declaration hoisted above an early return --- */
|
||||
static int
|
||||
decl_after_early_return(int flag)
|
||||
{
|
||||
int status;
|
||||
int extra;
|
||||
|
||||
status = flag ? 0 : -1;
|
||||
if (status != 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
extra = 7;
|
||||
return extra;
|
||||
}
|
||||
|
||||
/* --- pattern 2: side-effecting-initializer declaration hoisted bare,
|
||||
* assigned at its original position so the call still happens after the
|
||||
* same preceding statements --- */
|
||||
static int
|
||||
side_effect(int *counter)
|
||||
{
|
||||
(*counter)++;
|
||||
return *counter;
|
||||
}
|
||||
|
||||
static int
|
||||
decl_with_side_effecting_init(int *counter)
|
||||
{
|
||||
int guard;
|
||||
int value;
|
||||
|
||||
guard = (*counter < 0);
|
||||
if (guard) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
value = side_effect(counter);
|
||||
return value;
|
||||
}
|
||||
|
||||
/* --- pattern 3: two declarations in a row, first one hoisted becomes an
|
||||
* assignment, so the second (immediately following) must hoist too --- */
|
||||
static int
|
||||
decl_pair_after_hoist(int *counter)
|
||||
{
|
||||
int guard;
|
||||
int a;
|
||||
int b;
|
||||
|
||||
guard = (*counter < 0);
|
||||
if (guard) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
a = side_effect(counter);
|
||||
b = side_effect(counter);
|
||||
return a + b;
|
||||
}
|
||||
|
||||
/* --- pattern 4: nested-block declaration after statements already
|
||||
* executed within that same nested block --- */
|
||||
static int
|
||||
decl_after_statement_in_nested_block(int interactive)
|
||||
{
|
||||
int outer_result = 0;
|
||||
|
||||
if (interactive) {
|
||||
int inner_value;
|
||||
|
||||
outer_result += 1;
|
||||
outer_result += 2;
|
||||
|
||||
inner_value = outer_result * 10;
|
||||
return inner_value;
|
||||
}
|
||||
|
||||
return outer_result;
|
||||
}
|
||||
|
||||
/* --- pattern 5: platform-conditional declaration, same name, different
|
||||
* type per branch, hoisted under the same #ifdef at the top --- */
|
||||
static long
|
||||
decl_platform_conditional(void)
|
||||
{
|
||||
int ret;
|
||||
#ifdef TARGET_WINDOWS
|
||||
const char *env;
|
||||
#else
|
||||
const long *env;
|
||||
#endif
|
||||
|
||||
ret = 1;
|
||||
if (!ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef TARGET_WINDOWS
|
||||
env = "PYTHONSTARTUP";
|
||||
if (env == 0) {
|
||||
return 0;
|
||||
}
|
||||
return (long)env[0];
|
||||
#else
|
||||
env = 0;
|
||||
if (env == 0) {
|
||||
return 0;
|
||||
}
|
||||
return *env;
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
int counter = 0;
|
||||
int total = 0;
|
||||
|
||||
total += decl_after_early_return(1);
|
||||
total += decl_with_side_effecting_init(&counter);
|
||||
total += decl_pair_after_hoist(&counter);
|
||||
total += decl_after_statement_in_nested_block(1);
|
||||
total += (int)decl_platform_conditional();
|
||||
|
||||
return (total >= 0) ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/* Focused compile smoke test for the VC6 C89-mixed-declarations fixes in
|
||||
* four Include/internal/pycore_*.h headers: pycore_code.h, pycore_dict.h,
|
||||
* pycore_list.h, and pycore_call.h.
|
||||
*
|
||||
* VC6 is a C89-only compiler (plus Microsoft extensions): block-scope
|
||||
* declarations must precede all statements in that block. CPython 3.11's
|
||||
* source freely uses C99-style declarations after statements (e.g. after
|
||||
* an assert()); the fix hoists each such declaration to the top of its
|
||||
* enclosing block without changing evaluation order or behavior. See
|
||||
* PORT_STATUS.md ("Mixed-declarations header slice") for full detail.
|
||||
*
|
||||
* Unlike scripts/fixtures/vc6_atomic_smoke.c and vc6_stdbool_smoke.c, this
|
||||
* fixture includes the real "Python.h" (Py_BUILD_CORE is supplied by the
|
||||
* probe script) rather than isolating the pycore_*.h headers from it:
|
||||
* these four headers assume types (PyObject, PyCodeObject, PyListObject,
|
||||
* PyDictValues, vectorcallfunc, ...) that come from the normal public
|
||||
* header chain, the same way real CPython .c sources reach them.
|
||||
*/
|
||||
#include "Python.h"
|
||||
#include "pycore_code.h"
|
||||
#include "pycore_dict.h"
|
||||
#include "pycore_list.h"
|
||||
#include "pycore_call.h"
|
||||
|
||||
int
|
||||
vc6_locals_kind_smoke(PyObject *kinds, int i, _PyLocals_Kind kind)
|
||||
{
|
||||
_PyLocals_SetKind(kinds, i, kind);
|
||||
return (int)_PyLocals_GetKind(kinds, i);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
vc6_adaptive_counter_smoke(uint16_t counter)
|
||||
{
|
||||
return adaptive_counter_backoff(counter);
|
||||
}
|
||||
|
||||
void
|
||||
vc6_dict_values_smoke(PyDictValues *values, Py_ssize_t ix)
|
||||
{
|
||||
_PyDictValues_AddToInsertionOrder(values, ix);
|
||||
}
|
||||
|
||||
int
|
||||
vc6_list_append_smoke(PyListObject *self, PyObject *newitem)
|
||||
{
|
||||
return _PyList_AppendTakeRef(self, newitem);
|
||||
}
|
||||
|
||||
vectorcallfunc
|
||||
vc6_vectorcall_smoke(PyObject *callable)
|
||||
{
|
||||
return _PyVectorcall_FunctionInline(callable);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/* Focused, isolated compile smoke test for compat/msvc600/stdbool.h under
|
||||
* VC6.
|
||||
*
|
||||
* Deliberately does NOT go through pycore_interp.h or any other pycore_*.h
|
||||
* header: this isolates the result to the <stdbool.h> shim itself, the
|
||||
* same way scripts/fixtures/vc6_atomic_smoke.c isolates pycore_atomic.h.
|
||||
* Modules/main.c (see scripts/vc6-probe-core.test.sh) is the real-header
|
||||
* reference case that proves compat/msvc600/stdbool.h is actually what
|
||||
* pycore_interp.h's `#include <stdbool.h>` resolves to via the VC6 probe
|
||||
* scripts' include path.
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
|
||||
static bool vc6_stdbool_flag = true;
|
||||
|
||||
int
|
||||
vc6_stdbool_smoke(void)
|
||||
{
|
||||
bool a = true;
|
||||
bool b = false;
|
||||
bool c = a && !b;
|
||||
|
||||
vc6_stdbool_flag = c;
|
||||
|
||||
if (!__bool_true_false_are_defined) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)(a == true) + (int)(b == false) + (int)(c == true)
|
||||
+ (int)vc6_stdbool_flag;
|
||||
}
|
||||
Reference in New Issue
Block a user