mirror of
https://github.com/php/php-src.git
synced 2024-11-27 03:44:07 +08:00
Remove custom alloca() (#8513)
* Use arena in DCE instead of multiple alloca() This requires passing the optimizer context * Use our do_alloca() instead of alloca() * Use emalloc in DEBUG builds instead of stack allocations for do_alloca() This helps detecting that we correctly free do_alloca()
This commit is contained in:
parent
344555768a
commit
8685a7f03c
@ -523,7 +523,7 @@ static inline bool may_throw_dce_exception(const zend_op *opline) {
|
||||
return opline->opcode == ZEND_ADD_ARRAY_ELEMENT && opline->op2_type == IS_UNUSED;
|
||||
}
|
||||
|
||||
int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, bool reorder_dtor_effects) {
|
||||
int dce_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *optimizer_ctx, zend_ssa *ssa, bool reorder_dtor_effects) {
|
||||
int i;
|
||||
zend_ssa_phi *phi;
|
||||
int removed_ops = 0;
|
||||
@ -536,20 +536,17 @@ int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, bool reorder_d
|
||||
ctx.op_array = op_array;
|
||||
ctx.reorder_dtor_effects = reorder_dtor_effects;
|
||||
|
||||
void *checkpoint = zend_arena_checkpoint(optimizer_ctx->arena);
|
||||
/* We have no dedicated phi vector, so we use the whole ssa var vector instead */
|
||||
ctx.instr_worklist_len = zend_bitset_len(op_array->last);
|
||||
ctx.instr_worklist = alloca(sizeof(zend_ulong) * ctx.instr_worklist_len);
|
||||
memset(ctx.instr_worklist, 0, sizeof(zend_ulong) * ctx.instr_worklist_len);
|
||||
ctx.instr_worklist = zend_arena_calloc(&optimizer_ctx->arena, ctx.instr_worklist_len, sizeof(zend_ulong));
|
||||
ctx.phi_worklist_len = zend_bitset_len(ssa->vars_count);
|
||||
ctx.phi_worklist = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len);
|
||||
memset(ctx.phi_worklist, 0, sizeof(zend_ulong) * ctx.phi_worklist_len);
|
||||
ctx.phi_worklist_no_val = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len);
|
||||
memset(ctx.phi_worklist_no_val, 0, sizeof(zend_ulong) * ctx.phi_worklist_len);
|
||||
ctx.phi_worklist = zend_arena_calloc(&optimizer_ctx->arena, ctx.phi_worklist_len, sizeof(zend_ulong));
|
||||
ctx.phi_worklist_no_val = zend_arena_calloc(&optimizer_ctx->arena, ctx.phi_worklist_len, sizeof(zend_ulong));
|
||||
|
||||
/* Optimistically assume all instructions and phis to be dead */
|
||||
ctx.instr_dead = alloca(sizeof(zend_ulong) * ctx.instr_worklist_len);
|
||||
memset(ctx.instr_dead, 0, sizeof(zend_ulong) * ctx.instr_worklist_len);
|
||||
ctx.phi_dead = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len);
|
||||
ctx.instr_dead = zend_arena_calloc(&optimizer_ctx->arena, ctx.instr_worklist_len, sizeof(zend_ulong));
|
||||
ctx.phi_dead = zend_arena_alloc(&optimizer_ctx->arena, ctx.phi_worklist_len * sizeof(zend_ulong));
|
||||
memset(ctx.phi_dead, 0xff, sizeof(zend_ulong) * ctx.phi_worklist_len);
|
||||
|
||||
/* Mark non-CV phis as live. Even if the result is unused, we generally cannot remove one
|
||||
@ -664,5 +661,7 @@ int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, bool reorder_d
|
||||
}
|
||||
} FOREACH_PHI_END();
|
||||
|
||||
zend_arena_release(&optimizer_ctx->arena, checkpoint);
|
||||
|
||||
return removed_ops;
|
||||
}
|
||||
|
@ -1076,7 +1076,7 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
|
||||
}
|
||||
|
||||
if (ZEND_OPTIMIZER_PASS_14 & ctx->optimization_level) {
|
||||
if (dce_optimize_op_array(op_array, ssa, 0)) {
|
||||
if (dce_optimize_op_array(op_array, ctx, ssa, 0)) {
|
||||
remove_nops = 1;
|
||||
}
|
||||
if (zend_dfa_optimize_jmps(op_array, ssa)) {
|
||||
|
@ -121,7 +121,7 @@ uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args);
|
||||
void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline);
|
||||
void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist);
|
||||
int sccp_optimize_op_array(zend_optimizer_ctx *ctx, zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map);
|
||||
int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, bool reorder_dtor_effects);
|
||||
int dce_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *optimizer_ctx, zend_ssa *ssa, bool reorder_dtor_effects);
|
||||
zend_result zend_ssa_escape_analysis(const zend_script *script, zend_op_array *op_array, zend_ssa *ssa);
|
||||
|
||||
typedef void (*zend_op_array_func_t)(zend_op_array *, void *context);
|
||||
|
@ -63,10 +63,6 @@
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#if HAVE_ALLOCA_H && !defined(_ALLOCA_H)
|
||||
# include <alloca.h>
|
||||
#endif
|
||||
|
||||
#if defined(ZEND_WIN32) && !defined(__clang__)
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
@ -181,6 +177,9 @@
|
||||
# define ZEND_EXTENSIONS_SUPPORT 0
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_ALLOCA_H) && !defined(_ALLOCA_H)
|
||||
# include <alloca.h>
|
||||
#endif
|
||||
/* AIX requires this to be the first thing in the file. */
|
||||
#ifndef __GNUC__
|
||||
# ifndef HAVE_ALLOCA_H
|
||||
@ -194,6 +193,25 @@ char *alloca();
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !ZEND_DEBUG && (defined(HAVE_ALLOCA) || (defined (__GNUC__) && __GNUC__ >= 2)) && !(defined(ZTS) && defined(HPUX)) && !defined(DARWIN)
|
||||
# define ZEND_ALLOCA_MAX_SIZE (32 * 1024)
|
||||
# define ALLOCA_FLAG(name) \
|
||||
bool name;
|
||||
# define SET_ALLOCA_FLAG(name) \
|
||||
name = true
|
||||
# define do_alloca_ex(size, limit, use_heap) \
|
||||
((use_heap = (UNEXPECTED((size) > (limit)))) ? emalloc(size) : alloca(size))
|
||||
# define do_alloca(size, use_heap) \
|
||||
do_alloca_ex(size, ZEND_ALLOCA_MAX_SIZE, use_heap)
|
||||
# define free_alloca(p, use_heap) \
|
||||
do { if (UNEXPECTED(use_heap)) efree(p); } while (0)
|
||||
#else
|
||||
# define ALLOCA_FLAG(name)
|
||||
# define SET_ALLOCA_FLAG(name)
|
||||
# define do_alloca(p, use_heap) emalloc(p)
|
||||
# define free_alloca(p, use_heap) efree(p)
|
||||
#endif
|
||||
|
||||
#if ZEND_GCC_VERSION >= 2096 || __has_attribute(__malloc__)
|
||||
# define ZEND_ATTRIBUTE_MALLOC __attribute__ ((__malloc__))
|
||||
#else
|
||||
@ -345,25 +363,6 @@ char *alloca();
|
||||
# define XtOffsetOf(s_type, field) offsetof(s_type, field)
|
||||
#endif
|
||||
|
||||
#if (defined(HAVE_ALLOCA) || (defined (__GNUC__) && __GNUC__ >= 2)) && !(defined(ZTS) && defined(HPUX)) && !defined(DARWIN)
|
||||
# define ZEND_ALLOCA_MAX_SIZE (32 * 1024)
|
||||
# define ALLOCA_FLAG(name) \
|
||||
bool name;
|
||||
# define SET_ALLOCA_FLAG(name) \
|
||||
name = 1
|
||||
# define do_alloca_ex(size, limit, use_heap) \
|
||||
((use_heap = (UNEXPECTED((size) > (limit)))) ? emalloc(size) : alloca(size))
|
||||
# define do_alloca(size, use_heap) \
|
||||
do_alloca_ex(size, ZEND_ALLOCA_MAX_SIZE, use_heap)
|
||||
# define free_alloca(p, use_heap) \
|
||||
do { if (UNEXPECTED(use_heap)) efree(p); } while (0)
|
||||
#else
|
||||
# define ALLOCA_FLAG(name)
|
||||
# define SET_ALLOCA_FLAG(name)
|
||||
# define do_alloca(p, use_heap) emalloc(p)
|
||||
# define free_alloca(p, use_heap) efree(p)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SIGSETJMP
|
||||
# define SETJMP(a) sigsetjmp(a, 0)
|
||||
# define LONGJMP(a,b) siglongjmp(a, b)
|
||||
|
@ -1610,7 +1610,7 @@ PHP_INSTALL_HEADERS([Zend/Optimizer], [ \
|
||||
PHP_ADD_SOURCES(TSRM, TSRM.c, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
|
||||
|
||||
PHP_ADD_SOURCES(main, main.c snprintf.c spprintf.c \
|
||||
fopen_wrappers.c alloca.c php_scandir.c \
|
||||
fopen_wrappers.c php_scandir.c \
|
||||
php_ini_builder.c \
|
||||
php_ini.c SAPI.c rfc1867.c php_content_types.c strlcpy.c \
|
||||
strlcat.c explicit_bzero.c reentrancy.c php_variables.c php_ticks.c \
|
||||
|
@ -10,7 +10,6 @@
|
||||
|
||||
#ifdef PHP_WIN32
|
||||
# define __alignof__ __alignof
|
||||
# define alloca _alloca
|
||||
#else
|
||||
# ifndef HAVE_ALIGNOF
|
||||
# include <stddef.h>
|
||||
@ -370,16 +369,23 @@ char * php_sha256_crypt_r(const char *key, const char *salt, char *buffer, int b
|
||||
|
||||
salt_len = MIN(strcspn(salt, "$"), SALT_LEN_MAX);
|
||||
key_len = strlen(key);
|
||||
char *tmp_key = NULL;
|
||||
ALLOCA_FLAG(use_heap_key);
|
||||
char *tmp_salt = NULL;
|
||||
ALLOCA_FLAG(use_heap_salt);
|
||||
|
||||
SET_ALLOCA_FLAG(use_heap_key);
|
||||
SET_ALLOCA_FLAG(use_heap_salt);
|
||||
|
||||
if ((key - (char *) 0) % __alignof__ (uint32_t) != 0) {
|
||||
char *tmp = (char *) alloca(key_len + __alignof__(uint32_t));
|
||||
key = copied_key = memcpy(tmp + __alignof__(uint32_t) - (tmp - (char *) 0) % __alignof__(uint32_t), key, key_len);
|
||||
tmp_key = (char *) do_alloca(key_len + __alignof__(uint32_t), use_heap_key);
|
||||
key = copied_key = memcpy(tmp_key + __alignof__(uint32_t) - (tmp_key - (char *) 0) % __alignof__(uint32_t), key, key_len);
|
||||
}
|
||||
|
||||
if ((salt - (char *) 0) % __alignof__(uint32_t) != 0) {
|
||||
char *tmp = (char *) alloca(salt_len + 1 + __alignof__(uint32_t));
|
||||
tmp_salt = (char *) do_alloca(salt_len + 1 + __alignof__(uint32_t), use_heap_salt);
|
||||
salt = copied_salt =
|
||||
memcpy(tmp + __alignof__(uint32_t) - (tmp - (char *) 0) % __alignof__ (uint32_t), salt, salt_len);
|
||||
memcpy(tmp_salt + __alignof__(uint32_t) - (tmp_salt - (char *) 0) % __alignof__ (uint32_t), salt, salt_len);
|
||||
copied_salt[salt_len] = 0;
|
||||
}
|
||||
|
||||
@ -443,7 +449,8 @@ char * php_sha256_crypt_r(const char *key, const char *salt, char *buffer, int b
|
||||
sha256_finish_ctx(&alt_ctx, temp_result);
|
||||
|
||||
/* Create byte sequence P. */
|
||||
cp = p_bytes = alloca(key_len);
|
||||
ALLOCA_FLAG(use_heap_p_bytes);
|
||||
cp = p_bytes = do_alloca(key_len, use_heap_p_bytes);
|
||||
for (cnt = key_len; cnt >= 32; cnt -= 32) {
|
||||
cp = __php_mempcpy((void *)cp, (const void *)temp_result, 32);
|
||||
}
|
||||
@ -461,7 +468,8 @@ char * php_sha256_crypt_r(const char *key, const char *salt, char *buffer, int b
|
||||
sha256_finish_ctx(&alt_ctx, temp_result);
|
||||
|
||||
/* Create byte sequence S. */
|
||||
cp = s_bytes = alloca(salt_len);
|
||||
ALLOCA_FLAG(use_heap_s_bytes);
|
||||
cp = s_bytes = do_alloca(salt_len, use_heap_s_bytes);
|
||||
for (cnt = salt_len; cnt >= 32; cnt -= 32) {
|
||||
cp = __php_mempcpy(cp, temp_result, 32);
|
||||
}
|
||||
@ -571,6 +579,14 @@ char * php_sha256_crypt_r(const char *key, const char *salt, char *buffer, int b
|
||||
if (copied_salt != NULL) {
|
||||
ZEND_SECURE_ZERO(copied_salt, salt_len);
|
||||
}
|
||||
if (tmp_key != NULL) {
|
||||
free_alloca(tmp_key, use_heap_key);
|
||||
}
|
||||
if (tmp_salt != NULL) {
|
||||
free_alloca(tmp_salt, use_heap_salt);
|
||||
}
|
||||
free_alloca(p_bytes, use_heap_p_bytes);
|
||||
free_alloca(s_bytes, use_heap_s_bytes);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include <limits.h>
|
||||
#ifdef PHP_WIN32
|
||||
# define __alignof__ __alignof
|
||||
# define alloca _alloca
|
||||
#else
|
||||
# ifndef HAVE_ALIGNOF
|
||||
# include <stddef.h>
|
||||
@ -404,16 +403,23 @@ php_sha512_crypt_r(const char *key, const char *salt, char *buffer, int buflen)
|
||||
|
||||
salt_len = MIN(strcspn(salt, "$"), SALT_LEN_MAX);
|
||||
key_len = strlen(key);
|
||||
char *tmp_key = NULL;
|
||||
ALLOCA_FLAG(use_heap_key);
|
||||
char *tmp_salt = NULL;
|
||||
ALLOCA_FLAG(use_heap_salt);
|
||||
|
||||
SET_ALLOCA_FLAG(use_heap_key);
|
||||
SET_ALLOCA_FLAG(use_heap_salt);
|
||||
|
||||
if ((key - (char *) 0) % __alignof__ (uint64_t) != 0) {
|
||||
char *tmp = (char *) alloca (key_len + __alignof__ (uint64_t));
|
||||
tmp_key = (char *) do_alloca(key_len + __alignof__ (uint64_t), use_heap_key);
|
||||
key = copied_key =
|
||||
memcpy(tmp + __alignof__(uint64_t) - (tmp - (char *) 0) % __alignof__(uint64_t), key, key_len);
|
||||
memcpy(tmp_key + __alignof__(uint64_t) - (tmp_key - (char *) 0) % __alignof__(uint64_t), key, key_len);
|
||||
}
|
||||
|
||||
if ((salt - (char *) 0) % __alignof__ (uint64_t) != 0) {
|
||||
char *tmp = (char *) alloca(salt_len + 1 + __alignof__(uint64_t));
|
||||
salt = copied_salt = memcpy(tmp + __alignof__(uint64_t) - (tmp - (char *) 0) % __alignof__(uint64_t), salt, salt_len);
|
||||
tmp_salt = (char *) do_alloca(salt_len + 1 + __alignof__(uint64_t), use_heap_salt);
|
||||
salt = copied_salt = memcpy(tmp_salt + __alignof__(uint64_t) - (tmp_salt - (char *) 0) % __alignof__(uint64_t), salt, salt_len);
|
||||
copied_salt[salt_len] = 0;
|
||||
}
|
||||
|
||||
@ -477,7 +483,8 @@ php_sha512_crypt_r(const char *key, const char *salt, char *buffer, int buflen)
|
||||
sha512_finish_ctx(&alt_ctx, temp_result);
|
||||
|
||||
/* Create byte sequence P. */
|
||||
cp = p_bytes = alloca(key_len);
|
||||
ALLOCA_FLAG(use_heap_p_bytes);
|
||||
cp = p_bytes = do_alloca(key_len, use_heap_p_bytes);
|
||||
for (cnt = key_len; cnt >= 64; cnt -= 64) {
|
||||
cp = __php_mempcpy((void *) cp, (const void *)temp_result, 64);
|
||||
}
|
||||
@ -496,7 +503,8 @@ php_sha512_crypt_r(const char *key, const char *salt, char *buffer, int buflen)
|
||||
sha512_finish_ctx(&alt_ctx, temp_result);
|
||||
|
||||
/* Create byte sequence S. */
|
||||
cp = s_bytes = alloca(salt_len);
|
||||
ALLOCA_FLAG(use_heap_s_bytes);
|
||||
cp = s_bytes = do_alloca(salt_len, use_heap_s_bytes);
|
||||
for (cnt = salt_len; cnt >= 64; cnt -= 64) {
|
||||
cp = __php_mempcpy(cp, temp_result, 64);
|
||||
}
|
||||
@ -618,6 +626,14 @@ php_sha512_crypt_r(const char *key, const char *salt, char *buffer, int buflen)
|
||||
if (copied_salt != NULL) {
|
||||
ZEND_SECURE_ZERO(copied_salt, salt_len);
|
||||
}
|
||||
if (tmp_key != NULL) {
|
||||
free_alloca(tmp_key, use_heap_key);
|
||||
}
|
||||
if (tmp_salt != NULL) {
|
||||
free_alloca(tmp_salt, use_heap_salt);
|
||||
}
|
||||
free_alloca(p_bytes, use_heap_p_bytes);
|
||||
free_alloca(s_bytes, use_heap_s_bytes);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
486
main/alloca.c
486
main/alloca.c
@ -1,486 +0,0 @@
|
||||
/* alloca.c -- allocate automatically reclaimed memory
|
||||
(Mostly) portable public-domain implementation -- D A Gwyn
|
||||
|
||||
This implementation of the PWB library alloca function,
|
||||
which is used to allocate space off the run-time stack so
|
||||
that it is automatically reclaimed upon procedure exit,
|
||||
was inspired by discussions with J. Q. Johnson of Cornell.
|
||||
J.Otto Tennant <jot@cray.com> contributed the Cray support.
|
||||
|
||||
There are some preprocessor constants that can
|
||||
be defined when compiling for your specific system, for
|
||||
improved efficiency; however, the defaults should be okay.
|
||||
|
||||
The general concept of this implementation is to keep
|
||||
track of all alloca-allocated blocks, and reclaim any
|
||||
that are found to be deeper in the stack than the current
|
||||
invocation. This heuristic does not reclaim storage as
|
||||
soon as it becomes invalid, but it will do so eventually.
|
||||
|
||||
As a special case, alloca(0) reclaims storage without
|
||||
allocating any. It is a good idea to use alloca(0) in
|
||||
your main control loop, etc. to force garbage collection. */
|
||||
|
||||
#include <php_config.h>
|
||||
|
||||
#if !HAVE_ALLOCA
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef emacs
|
||||
#include "blockinput.h"
|
||||
#endif
|
||||
|
||||
/* If compiling with GCC 2, this file's not needed. */
|
||||
#if !defined (__GNUC__) || __GNUC__ < 2
|
||||
|
||||
/* If someone has defined alloca as a macro,
|
||||
there must be some other way alloca is supposed to work. */
|
||||
#ifndef alloca
|
||||
|
||||
#ifdef emacs
|
||||
#ifdef static
|
||||
/* actually, only want this if static is defined as ""
|
||||
-- this is for usg, in which emacs must undefine static
|
||||
in order to make unexec workable
|
||||
*/
|
||||
#ifndef STACK_DIRECTION
|
||||
you
|
||||
lose
|
||||
-- must know STACK_DIRECTION at compile-time
|
||||
#endif /* STACK_DIRECTION undefined */
|
||||
#endif /* static */
|
||||
#endif /* emacs */
|
||||
|
||||
/* If your stack is a linked list of frames, you have to
|
||||
provide an "address metric" ADDRESS_FUNCTION macro. */
|
||||
|
||||
#if defined (CRAY) && defined (CRAY_STACKSEG_END)
|
||||
long i00afunc ();
|
||||
#define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg))
|
||||
#else
|
||||
#define ADDRESS_FUNCTION(arg) &(arg)
|
||||
#endif
|
||||
|
||||
#if __STDC__
|
||||
typedef void *pointer;
|
||||
#else
|
||||
typedef char *pointer;
|
||||
#endif
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
/* Define STACK_DIRECTION if you know the direction of stack
|
||||
growth for your system; otherwise it will be automatically
|
||||
deduced at run-time.
|
||||
|
||||
STACK_DIRECTION > 0 => grows toward higher addresses
|
||||
STACK_DIRECTION < 0 => grows toward lower addresses
|
||||
STACK_DIRECTION = 0 => direction of growth unknown */
|
||||
|
||||
#ifndef STACK_DIRECTION
|
||||
#define STACK_DIRECTION 0 /* Direction unknown. */
|
||||
#endif
|
||||
|
||||
#if STACK_DIRECTION != 0
|
||||
|
||||
#define STACK_DIR STACK_DIRECTION /* Known at compile-time. */
|
||||
|
||||
#else /* STACK_DIRECTION == 0; need run-time code. */
|
||||
|
||||
static int stack_dir; /* 1 or -1 once known. */
|
||||
#define STACK_DIR stack_dir
|
||||
|
||||
static void
|
||||
find_stack_direction ()
|
||||
{
|
||||
static char *addr = NULL; /* Address of first `dummy', once known. */
|
||||
auto char dummy; /* To get stack address. */
|
||||
|
||||
if (addr == NULL)
|
||||
{ /* Initial entry. */
|
||||
addr = ADDRESS_FUNCTION (dummy);
|
||||
|
||||
find_stack_direction (); /* Recurse once. */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Second entry. */
|
||||
if (ADDRESS_FUNCTION (dummy) > addr)
|
||||
stack_dir = 1; /* Stack grew upward. */
|
||||
else
|
||||
stack_dir = -1; /* Stack grew downward. */
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* STACK_DIRECTION == 0 */
|
||||
|
||||
/* An "alloca header" is used to:
|
||||
(a) chain together all alloca'd blocks;
|
||||
(b) keep track of stack depth.
|
||||
|
||||
It is very important that sizeof(header) agree with malloc
|
||||
alignment chunk size. The following default should work okay. */
|
||||
|
||||
#ifndef ALIGN_SIZE
|
||||
#define ALIGN_SIZE sizeof(double)
|
||||
#endif
|
||||
|
||||
typedef union hdr
|
||||
{
|
||||
char align[ALIGN_SIZE]; /* To force sizeof(header). */
|
||||
struct
|
||||
{
|
||||
union hdr *next; /* For chaining headers. */
|
||||
char *deep; /* For stack depth measure. */
|
||||
} h;
|
||||
} header;
|
||||
|
||||
static header *last_alloca_header = NULL; /* -> last alloca header. */
|
||||
|
||||
/* Return a pointer to at least SIZE bytes of storage,
|
||||
which will be automatically reclaimed upon exit from
|
||||
the procedure that called alloca. Originally, this space
|
||||
was supposed to be taken from the current stack frame of the
|
||||
caller, but that method cannot be made to work for some
|
||||
implementations of C, for example under Gould's UTX/32. */
|
||||
|
||||
pointer
|
||||
alloca (size)
|
||||
size_t size;
|
||||
{
|
||||
auto char probe; /* Probes stack depth: */
|
||||
register char *depth = ADDRESS_FUNCTION (probe);
|
||||
|
||||
#if STACK_DIRECTION == 0
|
||||
if (STACK_DIR == 0) /* Unknown growth direction. */
|
||||
find_stack_direction ();
|
||||
#endif
|
||||
|
||||
/* Reclaim garbage, defined as all alloca'd storage that
|
||||
was allocated from deeper in the stack than currently. */
|
||||
|
||||
{
|
||||
register header *hp; /* Traverses linked list. */
|
||||
|
||||
#ifdef emacs
|
||||
BLOCK_INPUT;
|
||||
#endif
|
||||
|
||||
for (hp = last_alloca_header; hp != NULL;)
|
||||
if ((STACK_DIR > 0 && hp->h.deep > depth)
|
||||
|| (STACK_DIR < 0 && hp->h.deep < depth))
|
||||
{
|
||||
register header *np = hp->h.next;
|
||||
|
||||
free ((pointer) hp); /* Collect garbage. */
|
||||
|
||||
hp = np; /* -> next header. */
|
||||
}
|
||||
else
|
||||
break; /* Rest are not deeper. */
|
||||
|
||||
last_alloca_header = hp; /* -> last valid storage. */
|
||||
|
||||
#ifdef emacs
|
||||
UNBLOCK_INPUT;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
return NULL; /* No allocation required. */
|
||||
|
||||
/* Allocate combined header + user data storage. */
|
||||
|
||||
{
|
||||
register pointer new = malloc (sizeof (header) + size);
|
||||
/* Address of header. */
|
||||
|
||||
if (new == 0)
|
||||
abort();
|
||||
|
||||
((header *) new)->h.next = last_alloca_header;
|
||||
((header *) new)->h.deep = depth;
|
||||
|
||||
last_alloca_header = (header *) new;
|
||||
|
||||
/* User storage begins just after header. */
|
||||
|
||||
return (pointer) ((char *) new + sizeof (header));
|
||||
}
|
||||
}
|
||||
|
||||
#if defined (CRAY) && defined (CRAY_STACKSEG_END)
|
||||
|
||||
#ifdef DEBUG_I00AFUNC
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#ifndef CRAY_STACK
|
||||
#define CRAY_STACK
|
||||
#ifndef CRAY2
|
||||
/* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */
|
||||
struct stack_control_header
|
||||
{
|
||||
long shgrow:32; /* Number of times stack has grown. */
|
||||
long shaseg:32; /* Size of increments to stack. */
|
||||
long shhwm:32; /* High water mark of stack. */
|
||||
long shsize:32; /* Current size of stack (all segments). */
|
||||
};
|
||||
|
||||
/* The stack segment linkage control information occurs at
|
||||
the high-address end of a stack segment. (The stack
|
||||
grows from low addresses to high addresses.) The initial
|
||||
part of the stack segment linkage control information is
|
||||
0200 (octal) words. This provides for register storage
|
||||
for the routine which overflows the stack. */
|
||||
|
||||
struct stack_segment_linkage
|
||||
{
|
||||
long ss[0200]; /* 0200 overflow words. */
|
||||
long sssize:32; /* Number of words in this segment. */
|
||||
long ssbase:32; /* Offset to stack base. */
|
||||
long:32;
|
||||
long sspseg:32; /* Offset to linkage control of previous
|
||||
segment of stack. */
|
||||
long:32;
|
||||
long sstcpt:32; /* Pointer to task common address block. */
|
||||
long sscsnm; /* Private control structure number for
|
||||
microtasking. */
|
||||
long ssusr1; /* Reserved for user. */
|
||||
long ssusr2; /* Reserved for user. */
|
||||
long sstpid; /* Process ID for pid based multi-tasking. */
|
||||
long ssgvup; /* Pointer to multitasking thread give up. */
|
||||
long sscray[7]; /* Reserved for Cray Research. */
|
||||
long ssa0;
|
||||
long ssa1;
|
||||
long ssa2;
|
||||
long ssa3;
|
||||
long ssa4;
|
||||
long ssa5;
|
||||
long ssa6;
|
||||
long ssa7;
|
||||
long sss0;
|
||||
long sss1;
|
||||
long sss2;
|
||||
long sss3;
|
||||
long sss4;
|
||||
long sss5;
|
||||
long sss6;
|
||||
long sss7;
|
||||
};
|
||||
|
||||
#else /* CRAY2 */
|
||||
/* The following structure defines the vector of words
|
||||
returned by the STKSTAT library routine. */
|
||||
struct stk_stat
|
||||
{
|
||||
long now; /* Current total stack size. */
|
||||
long maxc; /* Amount of contiguous space which would
|
||||
be required to satisfy the maximum
|
||||
stack demand to date. */
|
||||
long high_water; /* Stack high-water mark. */
|
||||
long overflows; /* Number of stack overflow ($STKOFEN) calls. */
|
||||
long hits; /* Number of internal buffer hits. */
|
||||
long extends; /* Number of block extensions. */
|
||||
long stko_mallocs; /* Block allocations by $STKOFEN. */
|
||||
long underflows; /* Number of stack underflow calls ($STKRETN). */
|
||||
long stko_free; /* Number of deallocations by $STKRETN. */
|
||||
long stkm_free; /* Number of deallocations by $STKMRET. */
|
||||
long segments; /* Current number of stack segments. */
|
||||
long maxs; /* Maximum number of stack segments so far. */
|
||||
long pad_size; /* Stack pad size. */
|
||||
long current_address; /* Current stack segment address. */
|
||||
long current_size; /* Current stack segment size. This
|
||||
number is actually corrupted by STKSTAT to
|
||||
include the fifteen word trailer area. */
|
||||
long initial_address; /* Address of initial segment. */
|
||||
long initial_size; /* Size of initial segment. */
|
||||
};
|
||||
|
||||
/* The following structure describes the data structure which trails
|
||||
any stack segment. I think that the description in 'asdef' is
|
||||
out of date. I only describe the parts that I am sure about. */
|
||||
|
||||
struct stk_trailer
|
||||
{
|
||||
long this_address; /* Address of this block. */
|
||||
long this_size; /* Size of this block (does not include
|
||||
this trailer). */
|
||||
long unknown2;
|
||||
long unknown3;
|
||||
long link; /* Address of trailer block of previous
|
||||
segment. */
|
||||
long unknown5;
|
||||
long unknown6;
|
||||
long unknown7;
|
||||
long unknown8;
|
||||
long unknown9;
|
||||
long unknown10;
|
||||
long unknown11;
|
||||
long unknown12;
|
||||
long unknown13;
|
||||
long unknown14;
|
||||
};
|
||||
|
||||
#endif /* CRAY2 */
|
||||
#endif /* not CRAY_STACK */
|
||||
|
||||
#ifdef CRAY2
|
||||
/* Determine a "stack measure" for an arbitrary ADDRESS.
|
||||
I doubt that "lint" will like this much. */
|
||||
|
||||
static long
|
||||
i00afunc (long *address)
|
||||
{
|
||||
struct stk_stat status;
|
||||
struct stk_trailer *trailer;
|
||||
long *block, size;
|
||||
long result = 0;
|
||||
|
||||
/* We want to iterate through all of the segments. The first
|
||||
step is to get the stack status structure. We could do this
|
||||
more quickly and more directly, perhaps, by referencing the
|
||||
$LM00 common block, but I know that this works. */
|
||||
|
||||
STKSTAT (&status);
|
||||
|
||||
/* Set up the iteration. */
|
||||
|
||||
trailer = (struct stk_trailer *) (status.current_address
|
||||
+ status.current_size
|
||||
- 15);
|
||||
|
||||
/* There must be at least one stack segment. Therefore it is
|
||||
a fatal error if "trailer" is null. */
|
||||
|
||||
if (trailer == 0)
|
||||
abort ();
|
||||
|
||||
/* Discard segments that do not contain our argument address. */
|
||||
|
||||
while (trailer != 0)
|
||||
{
|
||||
block = (long *) trailer->this_address;
|
||||
size = trailer->this_size;
|
||||
if (block == 0 || size == 0)
|
||||
abort ();
|
||||
trailer = (struct stk_trailer *) trailer->link;
|
||||
if ((block <= address) && (address < (block + size)))
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set the result to the offset in this segment and add the sizes
|
||||
of all predecessor segments. */
|
||||
|
||||
result = address - block;
|
||||
|
||||
if (trailer == 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (trailer->this_size <= 0)
|
||||
abort ();
|
||||
result += trailer->this_size;
|
||||
trailer = (struct stk_trailer *) trailer->link;
|
||||
}
|
||||
while (trailer != 0);
|
||||
|
||||
/* We are done. Note that if you present a bogus address (one
|
||||
not in any segment), you will get a different number back, formed
|
||||
from subtracting the address of the first block. This is probably
|
||||
not what you want. */
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
#else /* not CRAY2 */
|
||||
/* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP.
|
||||
Determine the number of the cell within the stack,
|
||||
given the address of the cell. The purpose of this
|
||||
routine is to linearize, in some sense, stack addresses
|
||||
for alloca. */
|
||||
|
||||
static long
|
||||
i00afunc (long address)
|
||||
{
|
||||
long stkl = 0;
|
||||
|
||||
long size, pseg, this_segment, stack;
|
||||
long result = 0;
|
||||
|
||||
struct stack_segment_linkage *ssptr;
|
||||
|
||||
/* Register B67 contains the address of the end of the
|
||||
current stack segment. If you (as a subprogram) store
|
||||
your registers on the stack and find that you are past
|
||||
the contents of B67, you have overflowed the segment.
|
||||
|
||||
B67 also points to the stack segment linkage control
|
||||
area, which is what we are really interested in. */
|
||||
|
||||
stkl = CRAY_STACKSEG_END ();
|
||||
ssptr = (struct stack_segment_linkage *) stkl;
|
||||
|
||||
/* If one subtracts 'size' from the end of the segment,
|
||||
one has the address of the first word of the segment.
|
||||
|
||||
If this is not the first segment, 'pseg' will be
|
||||
nonzero. */
|
||||
|
||||
pseg = ssptr->sspseg;
|
||||
size = ssptr->sssize;
|
||||
|
||||
this_segment = stkl - size;
|
||||
|
||||
/* It is possible that calling this routine itself caused
|
||||
a stack overflow. Discard stack segments which do not
|
||||
contain the target address. */
|
||||
|
||||
while (!(this_segment <= address && address <= stkl))
|
||||
{
|
||||
#ifdef DEBUG_I00AFUNC
|
||||
fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl);
|
||||
#endif
|
||||
if (pseg == 0)
|
||||
break;
|
||||
stkl = stkl - pseg;
|
||||
ssptr = (struct stack_segment_linkage *) stkl;
|
||||
size = ssptr->sssize;
|
||||
pseg = ssptr->sspseg;
|
||||
this_segment = stkl - size;
|
||||
}
|
||||
|
||||
result = address - this_segment;
|
||||
|
||||
/* If you subtract pseg from the current end of the stack,
|
||||
you get the address of the previous stack segment's end.
|
||||
This seems a little convoluted to me, but I'll bet you save
|
||||
a cycle somewhere. */
|
||||
|
||||
while (pseg != 0)
|
||||
{
|
||||
#ifdef DEBUG_I00AFUNC
|
||||
fprintf (stderr, "%011o %011o\n", pseg, size);
|
||||
#endif
|
||||
stkl = stkl - pseg;
|
||||
ssptr = (struct stack_segment_linkage *) stkl;
|
||||
size = ssptr->sssize;
|
||||
pseg = ssptr->sspseg;
|
||||
result += size;
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
|
||||
#endif /* not CRAY2 */
|
||||
#endif /* CRAY */
|
||||
|
||||
#endif /* no alloca */
|
||||
#endif /* not GCC version 2 */
|
||||
#endif /* HAVE_ALLOCA */
|
Loading…
Reference in New Issue
Block a user