<commit_before>/*
 * Copyright (C) 1995-2008 University of Karlsruhe.  All right reserved.
 *
 * This file is part of libFirm.
 *
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation and appearing in the file LICENSE.GPL included in the
 * packaging of this file.
 *
 * Licensees holding valid libFirm Professional Edition licenses may use
 * this file in accordance with the libFirm Commercial License.
 * Agreement provided with the Software.
 *
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE.
 */

/**
 * @file
 * @brief   Optimization of function calls.
 * @author  <NAME>
 * @version $Id$
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <adt/raw_bitset.h>

#include "funccall_t.h"

#include "irnode_t.h"
#include "irgraph_t.h"
#include "irgmod.h"
#include "irgwalk.h"
#include "irvrfy.h"
#include "dbginfo_t.h"
#include "irflag_t.h"
#include "ircons.h"
#include "iredges_t.h"
#include "analyze_irg_args.h"
#include "irhooks.h"
#include "debug.h"

DEBUG_ONLY(static firm_dbg_module_t *dbg;)

/**
 * The walker environment for updating function calls.
 */
typedef struct _env_t {
	unsigned n_calls_SymConst;
	unsigned n_calls_Sel;
	ir_node  *const_call_list;       /**< The list of all const function calls that will be changed. */
	ir_node  *pure_call_list;        /**< The list of all pure function calls that will be changed. */
	ir_node  *nothrow_call_list;     /**< The list of all nothrow function calls that will be changed. */
	ir_node  *proj_list;             /**< The list of all potential Proj nodes that must be fixed. */
} env_t;

/** If non-null, evaluates entities for being a heap alloc. */
static check_alloc_entity_func is_alloc_entity = NULL;

/** Ready IRG's are marked in the ready set. */
static unsigned *ready_set;

/** IRG's that are in progress are marked here. */
static unsigned *busy_set;

/**
 * We misuse the mtp_property_inherited flag as temporary here.
 * The is ok, as we cannot set or get it anyway using the
 * get_addtional_properties API.
 */
#define mtp_temporary  mtp_property_inherited

/**
 * Walker: Collect all calls to const and pure functions
 * to lists. Collect all Proj(Call) nodes into a Proj list.
 */
static void collect_const_and_pure_calls(ir_node *node, void *env) {
	env_t *ctx = env;
	ir_node *call, *ptr;
	ir_entity *ent;
	unsigned prop;

	if (is_Call(node)) {
		call = node;

		/* set the link to NULL for all non-const/pure calls */
		set_irn_link(call, NULL);
		ptr = get_Call_ptr(call);
		if (is_Global(ptr)) {
			ent = get_Global_entity(ptr);

			prop = get_entity_additional_properties(ent);
			if ((prop & (mtp_property_const|mtp_property_pure)) == 0)
				return;
			++ctx->n_calls_SymConst;
		} else if (get_opt_closed_world() &&
		           is_Sel(ptr) &&
		           get_irg_callee_info_state(current_ir_graph) == irg_callee_info_consistent) {
			/* If all possible callees are const functions, we can remove the memory edge. */
			int i, n_callees = get_Call_n_callees(call);
			if (n_callees == 0) {
				/* This is kind of strange:  dying code or a Call that will raise an exception
				   when executed as there is no implementation to call.  So better not
				   optimize. */
				return;
			}

			/* note that const function are a subset of pure ones */
			prop = mtp_property_const | mtp_property_pure;
			for (i = 0; i < n_callees; ++i) {
				ent = get_Call_callee(call, i);
				if (ent == unknown_entity) {
					/* we don't know which entity is called here */
					return;
				}
				prop &= get_entity_additional_properties(ent);
				if (prop == mtp_no_property)
					return;
			}
			++ctx->n_calls_Sel;
		} else
			return;

		/* ok, if we get here we found a call to a const or a pure function */
		if (prop & mtp_property_pure) {
			set_irn_link(call, ctx->pure_call_list);
			ctx->pure_call_list = call;
		} else {
			set_irn_link(call, ctx->const_call_list);
			ctx->const_call_list = call;
		}
	} else if (is_Proj(node)) {
		/*
		 * Collect all memory and exception Proj's from
		 * calls.
		 */
		call = get_Proj_pred(node);
		if (! is_Call(call))
			return;

		/* collect the Proj's in the Proj list */
		switch (get_Proj_proj(node)) {
		case pn_Call_M_regular:
		case pn_Call_X_except:
		case pn_Call_X_regular:
		case pn_Call_M_except:
			set_irn_link(node, ctx->proj_list);
			ctx->proj_list = node;
			break;
		default:
			break;
		}
	}
}  /* collect_const_and_pure_calls */

/**
 * Fix the list of collected Calls.
 *
 * @param irg        the graph that contained calls to pure functions
 * @param call_list  the list of all call sites of const functions
 * @param proj_list  the list of all memory/exception Proj's of this call sites
 */
static void fix_const_call_list(ir_graph *irg, ir_node *call_list, ir_node *proj_list) {
	ir_node *call, *next, *mem, *proj;
	int exc_changed = 0;
	ir_graph *rem = current_ir_graph;

	current_ir_graph = irg;

	/* First step: fix all calls by removing it's memory input.
	   It's original memory input is preserved in their link fields. */
	for (call = call_list; call; call = next) {
		next = get_irn_link(call);
		mem  = get_Call_mem(call);

		set_irn_link(call, mem);
		set_Call_mem(call, get_irg_no_mem(irg));

		/*
		 * Sorrily we cannot simply set the node to 'float'.
		 * There is a reason for that:
		 *
		 * - The call might be inside a loop/if that is NOT entered
		 *   and calls a endless function. Setting the call to float
		 *   would allow to move it out from the loop/if causing this
		 *   function be called even if the loop/if is not entered ...
		 *
		 * This could be fixed using post-dominators for calls and Pin nodes
		 * but need some more analyzes to ensure that a call that potential
		 * never returns is not executed before some code that generates
		 * observable states...
		 */<commit_msg>Fix comments.<commit_after>/*
 * Copyright (C) 1995-2008 University of Karlsruhe.  All right reserved.
 *
 * This file is part of libFirm.
 *
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation and appearing in the file LICENSE.GPL included in the
 * packaging of this file.
 *
 * Licensees holding valid libFirm Professional Edition licenses may use
 * this file in accordance with the libFirm Commercial License.
 * Agreement provided with the Software.
 *
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE.
 */

/**
 * @file
 * @brief   Optimization of function calls.
 * @author  <NAME>
 * @version $Id$
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <adt/raw_bitset.h>

#include "funccall_t.h"

#include "irnode_t.h"
#include "irgraph_t.h"
#include "irgmod.h"
#include "irgwalk.h"
#include "irvrfy.h"
#include "dbginfo_t.h"
#include "irflag_t.h"
#include "ircons.h"
#include "iredges_t.h"
#include "analyze_irg_args.h"
#include "irhooks.h"
#include "debug.h"

DEBUG_ONLY(static firm_dbg_module_t *dbg;)

/**
 * The walker environment for updating function calls.
 */
typedef struct _env_t {
	unsigned n_calls_SymConst;
	unsigned n_calls_Sel;
	ir_node  *const_call_list;       /**< The list of all const function calls that will be changed. */
	ir_node  *pure_call_list;        /**< The list of all pure function calls that will be changed. */
	ir_node  *nothrow_call_list;     /**< The list of all nothrow function calls that will be changed. */
	ir_node  *proj_list;             /**< The list of all potential Proj nodes that must be fixed. */
} env_t;

/** If non-null, evaluates entities for being a heap alloc. */
static check_alloc_entity_func is_alloc_entity = NULL;

/** Ready IRG's are marked in the ready set. */
static unsigned *ready_set;

/** IRG's that are in progress are marked here. */
static unsigned *busy_set;

/**
 * We misuse the mtp_property_inherited flag as temporary here.
 * The is ok, as we cannot set or get it anyway using the
 * get_addtional_properties API.
 */
#define mtp_temporary  mtp_property_inherited

/**
 * Walker: Collect all calls to const and pure functions
 * to lists. Collect all Proj(Call) nodes into a Proj list.
 */
static void collect_const_and_pure_calls(ir_node *node, void *env) {
	env_t *ctx = env;
	ir_node *call, *ptr;
	ir_entity *ent;
	unsigned prop;

	if (is_Call(node)) {
		call = node;

		/* set the link to NULL for all non-const/pure calls */
		set_irn_link(call, NULL);
		ptr = get_Call_ptr(call);
		if (is_Global(ptr)) {
			ent = get_Global_entity(ptr);

			prop = get_entity_additional_properties(ent);
			if ((prop & (mtp_property_const|mtp_property_pure)) == 0)
				return;
			++ctx->n_calls_SymConst;
		} else if (get_opt_closed_world() &&
		           is_Sel(ptr) &&
		           get_irg_callee_info_state(current_ir_graph) == irg_callee_info_consistent) {
			/* If all possible callees are const functions, we can remove the memory edge. */
			int i, n_callees = get_Call_n_callees(call);
			if (n_callees == 0) {
				/* This is kind of strange:  dying code or a Call that will raise an exception
				   when executed as there is no implementation to call.  So better not
				   optimize. */
				return;
			}

			/* note that const function are a subset of pure ones */
			prop = mtp_property_const | mtp_property_pure;
			for (i = 0; i < n_callees; ++i) {
				ent = get_Call_callee(call, i);
				if (ent == unknown_entity) {
					/* we don't know which entity is called here */
					return;
				}
				prop &= get_entity_additional_properties(ent);
				if (prop == mtp_no_property)
					return;
			}
			++ctx->n_calls_Sel;
		} else
			return;

		/* ok, if we get here we found a call to a const or a pure function */
		if (prop & mtp_property_pure) {
			set_irn_link(call, ctx->pure_call_list);
			ctx->pure_call_list = call;
		} else {
			set_irn_link(call, ctx->const_call_list);
			ctx->const_call_list = call;
		}
	} else if (is_Proj(node)) {
		/*
		 * Collect all memory and exception Proj's from
		 * calls.
		 */
		call = get_Proj_pred(node);
		if (! is_Call(call))
			return;

		/* collect the Proj's in the Proj list */
		switch (get_Proj_proj(node)) {
		case pn_Call_M_regular:
		case pn_Call_X_except:
		case pn_Call_X_regular:
		case pn_Call_M_except:
			set_irn_link(node, ctx->proj_list);
			ctx->proj_list = node;
			break;
		default:
			break;
		}
	}
}  /* collect_const_and_pure_calls */

/**
 * Fix the list of collected Calls.
 *
 * @param irg        the graph that contained calls to pure functions
 * @param call_list  the list of all call sites of const functions
 * @param proj_list  the list of all memory/exception Proj's of this call sites
 */
static void fix_const_call_list(ir_graph *irg, ir_node *call_list, ir_node *proj_list) {
	ir_node *call, *next, *mem, *proj;
	int exc_changed = 0;
	ir_graph *rem = current_ir_graph;

	current_ir_graph = irg;

	/* First step: fix all calls by removing their memory input.
	 * The original memory input is preserved in their link fields. */
	for (call = call_list; call; call = next) {
		next = get_irn_link(call);
		mem  = get_Call_mem(call);

		set_irn_link(call, mem);
		set_Call_mem(call, get_irg_no_mem(irg));

		/*
		 * Unfortunately we cannot simply set the node to 'float'.
		 * There is a reason for that:
		 *
		 * - The call might be inside a loop/if that is NOT entered
		 *   and calls a endless function. Setting the call to float
		 *   would allow to move it out from the loop/if causing this
		 *   function be called even if the loop/if is not entered ...
		 *
		 * This could be fixed using post-dominators for calls and Pin nodes
		 * but need some more analyzes to ensure that a call that potential
		 * never returns is not executed before some code that generates
		 * observable states...
		 */
{'fraction_non_alphanumeric': 0.09608957219251336, 'fraction_numerical': 0.0021724598930481284, 'mean_word_length': 3.3939060205580027, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2177155', 'n_tokens_mistral': 4348, 'n_tokens_neox': 3947, 'n_words': 1389}