1403 lines
47 KiB
C
1403 lines
47 KiB
C
/**
|
|
* \file
|
|
* Array bounds check removal
|
|
*
|
|
* Author:
|
|
* Massimiliano Mantione (massi@ximian.com)
|
|
*
|
|
* (C) 2004 Ximian, Inc. http://www.ximian.com
|
|
*/
|
|
#include <config.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#include <mono/metadata/debug-helpers.h>
|
|
#include <mono/metadata/mempool.h>
|
|
#include <mono/metadata/opcodes.h>
|
|
#include <mono/metadata/mempool-internals.h>
|
|
#include <mono/utils/mono-compiler.h>
|
|
|
|
#include <config.h>
|
|
|
|
#ifndef DISABLE_JIT
|
|
|
|
#include "abcremoval.h"
|
|
|
|
#if SIZEOF_VOID_P == 8
|
|
#define OP_PCONST OP_I8CONST
|
|
#else
|
|
#define OP_PCONST OP_ICONST
|
|
#endif
|
|
|
|
|
|
#define TRACE_ABC_REMOVAL (verbose_level > 2)
|
|
#define REPORT_ABC_REMOVAL (verbose_level > 1)
|
|
|
|
/*
|
|
* A little hack for the verbosity level.
|
|
* The verbosity level is stored in the cfg, but not all functions that must
|
|
* print something see the cfg, so we store the verbosity level here at the
|
|
* beginning of the algorithm.
|
|
* This is not thread safe (does not handle correctly different verbosity
|
|
* levels in different threads), and is not exact in case of dynamic changes
|
|
* of the verbosity level...
|
|
* Anyway, this is not needed, all that can happen is that something more
|
|
* (or less) is logged, the result is in any case correct.
|
|
*/
|
|
static int verbose_level;
|
|
|
|
|
|
#define RELATION_BETWEEN_VALUES(value,related_value) (\
|
|
((value) > (related_value))? MONO_GT_RELATION :\
|
|
(((value) < (related_value))? MONO_LT_RELATION : MONO_EQ_RELATION))
|
|
|
|
#define MAKE_VALUE_ANY(v) do{\
|
|
(v).type = MONO_ANY_SUMMARIZED_VALUE;\
|
|
} while (0)
|
|
|
|
#define MAKE_VALUE_RELATION_ANY(r) do{\
|
|
(r)->relation = MONO_ANY_RELATION;\
|
|
MAKE_VALUE_ANY((r)->related_value);\
|
|
} while (0)
|
|
|
|
#define INITIALIZE_VALUE_RELATION(r) do{\
|
|
MAKE_VALUE_RELATION_ANY((r));\
|
|
(r)->next = NULL;\
|
|
} while (0)
|
|
|
|
#define MONO_NEGATED_RELATION(r) ((MonoValueRelation)((~(r))&MONO_ANY_RELATION))
|
|
#define MONO_SYMMETRIC_RELATION(r) ((MonoValueRelation)(((r)&MONO_EQ_RELATION)|(((r)&MONO_LT_RELATION)<<1)|((r&MONO_GT_RELATION)>>1)))
|
|
|
|
|
|
|
|
static void
|
|
print_relation (int relation) {
|
|
int print_or = 0;
|
|
printf ("(");
|
|
if (relation & MONO_LT_RELATION) {
|
|
printf ("LT");
|
|
print_or = 1;
|
|
}
|
|
if (relation & MONO_EQ_RELATION) {
|
|
if (print_or) {
|
|
printf ("|");
|
|
}
|
|
printf ("EQ");
|
|
print_or = 1;
|
|
}
|
|
if (relation & MONO_GT_RELATION) {
|
|
if (print_or) {
|
|
printf ("|");
|
|
}
|
|
printf ("GT");
|
|
print_or = 1;
|
|
}
|
|
printf (")");
|
|
}
|
|
|
|
static void
|
|
print_summarized_value (MonoSummarizedValue *value) {
|
|
switch (value->type) {
|
|
case MONO_ANY_SUMMARIZED_VALUE:
|
|
printf ("ANY");
|
|
break;
|
|
case MONO_CONSTANT_SUMMARIZED_VALUE:
|
|
printf ("CONSTANT %d", value->value.constant.value);
|
|
break;
|
|
case MONO_VARIABLE_SUMMARIZED_VALUE:
|
|
printf ("VARIABLE %d, delta %d", value->value.variable.variable, value->value.variable.delta);
|
|
break;
|
|
case MONO_PHI_SUMMARIZED_VALUE: {
|
|
int phi;
|
|
printf ("PHI (");
|
|
for (phi = 0; phi < value->value.phi.number_of_alternatives; phi++) {
|
|
if (phi) printf (",");
|
|
printf ("%d", value->value.phi.phi_alternatives [phi]);
|
|
}
|
|
printf (")");
|
|
break;
|
|
}
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_summarized_value_relation (MonoSummarizedValueRelation *relation) {
|
|
printf ("Relation ");
|
|
print_relation (relation->relation);
|
|
printf (" with value ");
|
|
print_summarized_value (&(relation->related_value));
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
print_summarized_value_relation_chain (MonoSummarizedValueRelation *relation) {
|
|
printf ("Relations:\n");
|
|
while (relation) {
|
|
print_summarized_value_relation (relation);
|
|
printf ("\n");
|
|
relation = relation->next;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
print_evaluation_context_status (MonoRelationsEvaluationStatus status) {
|
|
if (status == MONO_RELATIONS_EVALUATION_NOT_STARTED) {
|
|
printf ("EVALUATION_NOT_STARTED");
|
|
} else {
|
|
gboolean print_or = FALSE;
|
|
|
|
printf ("(");
|
|
if (status & MONO_RELATIONS_EVALUATION_IN_PROGRESS) {
|
|
if (print_or) printf ("|");
|
|
printf ("EVALUATION_IN_PROGRESS");
|
|
print_or = TRUE;
|
|
}
|
|
if (status & MONO_RELATIONS_EVALUATION_COMPLETED) {
|
|
if (print_or) printf ("|");
|
|
printf ("EVALUATION_COMPLETED");
|
|
print_or = TRUE;
|
|
}
|
|
if (status & MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_ASCENDING) {
|
|
if (print_or) printf ("|");
|
|
printf ("RECURSIVELY_ASCENDING");
|
|
print_or = TRUE;
|
|
}
|
|
if (status & MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_DESCENDING) {
|
|
if (print_or) printf ("|");
|
|
printf ("RECURSIVELY_DESCENDING");
|
|
print_or = TRUE;
|
|
}
|
|
if (status & MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_INDEFINITE) {
|
|
if (print_or) printf ("|");
|
|
printf ("RECURSIVELY_INDEFINITE");
|
|
print_or = TRUE;
|
|
}
|
|
printf (")");
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
print_evaluation_context_ranges (MonoRelationsEvaluationRanges *ranges) {
|
|
printf ("(ranges: zero [%d,%d], variable [%d,%d])", ranges->zero.lower, ranges->zero.upper, ranges->variable.lower, ranges->variable.upper);
|
|
}
|
|
|
|
static void
|
|
print_evaluation_context (MonoRelationsEvaluationContext *context, MonoRelationsEvaluationStatus status) {
|
|
print_evaluation_context_status (status);
|
|
if (status & (MONO_RELATIONS_EVALUATION_IN_PROGRESS|MONO_RELATIONS_EVALUATION_COMPLETED)) {
|
|
print_evaluation_context_ranges (&(context->ranges));
|
|
}
|
|
printf ("\n");
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
print_evaluation_area (MonoVariableRelationsEvaluationArea *area) {
|
|
int i;
|
|
printf ("Dump of evaluation area (%d variables):\n", area->cfg->num_varinfo);
|
|
for (i = 0; i < area->cfg->num_varinfo; i++) {
|
|
printf ("Variable %d: ", i);
|
|
print_evaluation_context (&(area->contexts [i]));
|
|
print_summarized_value_relation_chain (&(area->relations [i]));
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_evaluation_area_contexts (MonoVariableRelationsEvaluationArea *area) {
|
|
int i;
|
|
printf ("Dump of evaluation area contexts (%d variables):\n", area->cfg->num_varinfo);
|
|
for (i = 0; i < area->cfg->num_varinfo; i++) {
|
|
printf ("Variable %d: ", i);
|
|
print_evaluation_context (&(area->contexts [i]));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Check if the delta of an integer variable value is safe with respect
|
|
* to the variable size in bytes and its kind (signed or unsigned).
|
|
* If the delta is not safe, make the value an "any".
|
|
*/
|
|
static G_GNUC_UNUSED void
|
|
check_delta_safety (MonoVariableRelationsEvaluationArea *area, MonoSummarizedValue *value) {
|
|
if (value->type == MONO_VARIABLE_SUMMARIZED_VALUE) {
|
|
int variable = value->value.variable.variable;
|
|
int delta = value->value.variable.delta;
|
|
if ((area->variable_value_kind [variable]) & MONO_UNSIGNED_VALUE_FLAG) {
|
|
if (delta < 0) {
|
|
MAKE_VALUE_ANY (*value);
|
|
}
|
|
} else {
|
|
if (((area->variable_value_kind [variable]) & MONO_INTEGER_VALUE_SIZE_BITMASK) < 4) {
|
|
MAKE_VALUE_ANY (*value);
|
|
} else if (delta > 16) {
|
|
MAKE_VALUE_ANY (*value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* get_relation_from_ins:
|
|
*
|
|
* Obtain relations from a MonoInst.
|
|
*
|
|
* result_value_kind: the "expected" kind of result;
|
|
* result: the "summarized" value
|
|
* returns the "actual" kind of result, if guessable (otherwise MONO_UNKNOWN_INTEGER_VALUE)
|
|
*/
|
|
static MonoIntegerValueKind
|
|
get_relation_from_ins (MonoVariableRelationsEvaluationArea *area, MonoInst *ins, MonoSummarizedValueRelation *result, MonoIntegerValueKind result_value_kind)
|
|
{
|
|
MonoIntegerValueKind value_kind;
|
|
MonoSummarizedValue *value = &result->related_value;
|
|
|
|
if (ins->type == STACK_I8) {
|
|
value_kind = MONO_INTEGER_VALUE_SIZE_8;
|
|
} else if (ins->type == STACK_I4) {
|
|
value_kind = MONO_INTEGER_VALUE_SIZE_4;
|
|
} else {
|
|
value_kind = MONO_UNKNOWN_INTEGER_VALUE;
|
|
}
|
|
|
|
result->relation = MONO_EQ_RELATION;
|
|
MAKE_VALUE_ANY (*value);
|
|
|
|
switch (ins->opcode) {
|
|
case OP_ICONST:
|
|
value->type = MONO_CONSTANT_SUMMARIZED_VALUE;
|
|
value->value.constant.value = ins->inst_c0;
|
|
break;
|
|
case OP_MOVE:
|
|
value->type = MONO_VARIABLE_SUMMARIZED_VALUE;
|
|
value->value.variable.variable = ins->sreg1;
|
|
value->value.variable.delta = 0;
|
|
break;
|
|
case OP_SEXT_I4:
|
|
value->type = MONO_VARIABLE_SUMMARIZED_VALUE;
|
|
value->value.variable.variable = ins->sreg1;
|
|
value->value.variable.delta = 0;
|
|
value_kind = MONO_INTEGER_VALUE_SIZE_8;
|
|
break;
|
|
case OP_PHI:
|
|
value->type = MONO_PHI_SUMMARIZED_VALUE;
|
|
value->value.phi.number_of_alternatives = *(ins->inst_phi_args);
|
|
value->value.phi.phi_alternatives = ins->inst_phi_args + 1;
|
|
break;
|
|
case OP_IADD_IMM:
|
|
value->type = MONO_VARIABLE_SUMMARIZED_VALUE;
|
|
value->value.variable.variable = ins->sreg1;
|
|
value->value.variable.delta = ins->inst_imm;
|
|
/* FIXME: */
|
|
//check_delta_safety (area, result);
|
|
break;
|
|
case OP_ISUB_IMM:
|
|
value->type = MONO_VARIABLE_SUMMARIZED_VALUE;
|
|
value->value.variable.variable = ins->sreg1;
|
|
value->value.variable.delta = -ins->inst_imm;
|
|
/* FIXME: */
|
|
//check_delta_safety (area, result);
|
|
break;
|
|
case OP_IREM_UN:
|
|
/* The result of an unsigned remainder is 0 < x < the divisor */
|
|
result->relation = MONO_LT_RELATION;
|
|
value->type = MONO_VARIABLE_SUMMARIZED_VALUE;
|
|
value->value.variable.variable = ins->sreg2;
|
|
value->value.variable.delta = 0;
|
|
value_kind = MONO_UNSIGNED_INTEGER_VALUE_SIZE_4;
|
|
break;
|
|
case OP_LDLEN:
|
|
/*
|
|
* We represent arrays by their length, so r1<-ldlen r2 is stored
|
|
* as r1 == r2 in the evaluation graph.
|
|
*/
|
|
value->type = MONO_VARIABLE_SUMMARIZED_VALUE;
|
|
value->value.variable.variable = ins->sreg1;
|
|
value->value.variable.delta = 0;
|
|
value_kind = MONO_UNSIGNED_INTEGER_VALUE_SIZE_4;
|
|
break;
|
|
case OP_NEWARR:
|
|
value->type = MONO_VARIABLE_SUMMARIZED_VALUE;
|
|
value->value.variable.variable = ins->sreg1;
|
|
value->value.variable.delta = 0;
|
|
area->defs [ins->dreg] = ins;
|
|
break;
|
|
case OP_LDADDR:
|
|
/* The result is non-null */
|
|
result->relation = MONO_GT_RELATION;
|
|
value->type = MONO_CONSTANT_SUMMARIZED_VALUE;
|
|
value->value.constant.value = 0;
|
|
break;
|
|
|
|
/* FIXME: Add more opcodes */
|
|
default:
|
|
/* These opcodes are not currently handled while running SciMark, first
|
|
* column is the number of times the warning was shown:
|
|
*
|
|
* 1 add_imm
|
|
* 1 float_conv_to_i8
|
|
* 1 int_mul_ovf_un
|
|
* 1 int_neg
|
|
* 1 int_or
|
|
* 1 int_shr_un
|
|
* 1 localloc
|
|
* 1 long_ceq
|
|
* 1 long_rem
|
|
* 1 long_sub
|
|
* 2 int_ceq
|
|
* 2 int_conv_to_i2
|
|
* 2 int_min
|
|
* 2 lcall
|
|
* 2 long_div
|
|
* 3 int_conv_to_u2
|
|
* 3 long_shr_imm
|
|
* 4 int_rem
|
|
* 4 int_rem_imm
|
|
* 4 loadi1_membase
|
|
* 4 loadu4_membase
|
|
* 5 int_div
|
|
* 5 shl_imm
|
|
* 6 int_div_imm
|
|
* 6 int_mul
|
|
* 9 int_mul_imm
|
|
* 9 zext_i4
|
|
* 10 int_shr_imm
|
|
* 12 int_shr_un_imm
|
|
* 12 long_add_imm
|
|
* 12 outarg_vtretaddr
|
|
* 12 strlen
|
|
* 13 int_or_imm
|
|
* 23 call_membase
|
|
* 23 int_conv_to_u1
|
|
* 23 long_add
|
|
* 24 int_and_imm
|
|
* 24 int_shl_imm
|
|
* 24 loadu2_membase
|
|
* 29 loadi8_membase
|
|
* 31 llvm_outarg_vt
|
|
* 34 int_sub
|
|
* 34 loadu1_membase
|
|
* 42 int_add
|
|
* 85 ldaddr
|
|
* 116 loadi4_membase
|
|
* 159 x86_lea
|
|
* 179 sext_i4
|
|
* 291 load_membase
|
|
* 462 i8const
|
|
* 472 call
|
|
*/
|
|
|
|
break;
|
|
}
|
|
return value_kind;
|
|
}
|
|
|
|
static MonoValueRelation
|
|
get_relation_from_branch_instruction (MonoInst *ins)
|
|
{
|
|
if (MONO_IS_COND_BRANCH_OP (ins)) {
|
|
CompRelation rel = mono_opcode_to_cond (ins->opcode);
|
|
|
|
switch (rel) {
|
|
case CMP_EQ:
|
|
return MONO_EQ_RELATION;
|
|
case CMP_NE:
|
|
return MONO_NE_RELATION;
|
|
case CMP_LE:
|
|
case CMP_LE_UN:
|
|
return MONO_LE_RELATION;
|
|
case CMP_GE:
|
|
case CMP_GE_UN:
|
|
return MONO_GE_RELATION;
|
|
case CMP_LT:
|
|
case CMP_LT_UN:
|
|
return MONO_LT_RELATION;
|
|
case CMP_GT:
|
|
case CMP_GT_UN:
|
|
return MONO_GT_RELATION;
|
|
default:
|
|
g_assert_not_reached ();
|
|
return MONO_ANY_RELATION;
|
|
}
|
|
} else {
|
|
return MONO_ANY_RELATION;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Given a BB, find its entry condition and put its relations in a
|
|
* "MonoAdditionalVariableRelationsForBB" structure.
|
|
* bb: the BB
|
|
* relations: the resulting relations (entry condition of the given BB)
|
|
*/
|
|
static void
|
|
get_relations_from_previous_bb (MonoVariableRelationsEvaluationArea *area, MonoBasicBlock *bb, MonoAdditionalVariableRelationsForBB *relations)
|
|
{
|
|
MonoBasicBlock *in_bb;
|
|
MonoInst *ins, *compare, *branch;
|
|
MonoValueRelation branch_relation;
|
|
MonoValueRelation symmetric_relation;
|
|
gboolean code_path;
|
|
|
|
INITIALIZE_VALUE_RELATION (&(relations->relation1.relation));
|
|
relations->relation1.relation.relation_is_static_definition = FALSE;
|
|
relations->relation1.relation.next = NULL;
|
|
relations->relation1.insertion_point = NULL;
|
|
relations->relation1.variable = -1;
|
|
INITIALIZE_VALUE_RELATION (&(relations->relation2.relation));
|
|
relations->relation2.relation.relation_is_static_definition = FALSE;
|
|
relations->relation2.relation.next = NULL;
|
|
relations->relation2.insertion_point = NULL;
|
|
relations->relation2.variable = -1;
|
|
|
|
if (bb->in_count == 1) { /* Should write the code to "sum" conditions... */
|
|
in_bb = bb->in_bb [0];
|
|
|
|
if ((in_bb->last_ins == NULL) || (in_bb->code == in_bb->last_ins))
|
|
return;
|
|
|
|
for (ins = in_bb->code; ins->next != in_bb->last_ins; ins = ins->next)
|
|
;
|
|
|
|
compare = ins;
|
|
branch = ins->next;
|
|
branch_relation = get_relation_from_branch_instruction (branch);
|
|
|
|
if (branch_relation != MONO_ANY_RELATION) {
|
|
if (branch->inst_true_bb == bb) {
|
|
code_path = TRUE;
|
|
} else if (branch->inst_false_bb == bb) {
|
|
code_path = FALSE;
|
|
} else {
|
|
code_path = TRUE;
|
|
g_assert_not_reached ();
|
|
}
|
|
if (!code_path)
|
|
branch_relation = MONO_NEGATED_RELATION (branch_relation);
|
|
symmetric_relation = MONO_SYMMETRIC_RELATION (branch_relation);
|
|
|
|
/* FIXME: Other compare opcodes */
|
|
if (compare->opcode == OP_ICOMPARE) {
|
|
relations->relation1.variable = compare->sreg1;
|
|
relations->relation1.relation.relation = branch_relation;
|
|
relations->relation1.relation.related_value.type = MONO_VARIABLE_SUMMARIZED_VALUE;
|
|
relations->relation1.relation.related_value.value.variable.variable = compare->sreg2;
|
|
relations->relation1.relation.related_value.value.variable.delta = 0;
|
|
|
|
relations->relation2.variable = compare->sreg2;
|
|
relations->relation2.relation.relation = symmetric_relation;
|
|
relations->relation2.relation.related_value.type = MONO_VARIABLE_SUMMARIZED_VALUE;
|
|
relations->relation2.relation.related_value.value.variable.variable = compare->sreg1;
|
|
relations->relation2.relation.related_value.value.variable.delta = 0;
|
|
} else if (compare->opcode == OP_ICOMPARE_IMM) {
|
|
relations->relation1.variable = compare->sreg1;
|
|
relations->relation1.relation.relation = branch_relation;
|
|
relations->relation1.relation.related_value.type = MONO_CONSTANT_SUMMARIZED_VALUE;
|
|
relations->relation1.relation.related_value.value.constant.value = compare->inst_imm;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Add the given relations to the evaluation area.
|
|
* area: the evaluation area
|
|
* change: the relations that must be added
|
|
*/
|
|
static void
|
|
apply_change_to_evaluation_area (MonoVariableRelationsEvaluationArea *area, MonoAdditionalVariableRelation *change)
|
|
{
|
|
MonoSummarizedValueRelation *base_relation;
|
|
|
|
if (change->relation.relation != MONO_ANY_RELATION) {
|
|
base_relation = &(area->relations [change->variable]);
|
|
while ((base_relation->next != NULL) && (base_relation->next->relation_is_static_definition)) {
|
|
base_relation = base_relation->next;
|
|
}
|
|
change->insertion_point = base_relation;
|
|
change->relation.next = base_relation->next;
|
|
base_relation->next = &(change->relation);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Remove the given relation from the evaluation area.
|
|
* change: the relation that must be removed
|
|
*/
|
|
static void
|
|
remove_change_from_evaluation_area (MonoAdditionalVariableRelation *change)
|
|
{
|
|
if (change->insertion_point != NULL) {
|
|
change->insertion_point->next = change->relation.next;
|
|
change->relation.next = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
clean_contexts (MonoVariableRelationsEvaluationArea *area, int number)
|
|
{
|
|
memset(area->statuses, MONO_RELATIONS_EVALUATION_NOT_STARTED, number * sizeof(MonoRelationsEvaluationStatus));
|
|
}
|
|
|
|
|
|
/*
|
|
* Perform the intersection of a range and a constant value (taking into
|
|
* account the relation that the value has with the range).
|
|
* range: the range that will be intersected with the value
|
|
* value: the value that will be intersected with the range
|
|
* relation: the relation between the range and the value
|
|
*/
|
|
static void
|
|
intersect_value( MonoRelationsEvaluationRange *range, int value, MonoValueRelation relation )
|
|
{
|
|
switch (relation) {
|
|
case MONO_NO_RELATION:
|
|
MONO_MAKE_RELATIONS_EVALUATION_RANGE_IMPOSSIBLE (*range);
|
|
break;
|
|
case MONO_ANY_RELATION:
|
|
break;
|
|
case MONO_EQ_RELATION:
|
|
MONO_UPPER_EVALUATION_RANGE_INTERSECTION (range->upper, value);
|
|
MONO_LOWER_EVALUATION_RANGE_INTERSECTION (range->lower, value);
|
|
break;
|
|
case MONO_NE_RELATION: {
|
|
/* IMPROVEMENT Figure this out! (ignoring it is safe anyway) */
|
|
break;
|
|
}
|
|
case MONO_LT_RELATION:
|
|
MONO_UPPER_EVALUATION_RANGE_INTERSECTION (range->upper, MONO_UPPER_EVALUATION_RANGE_NOT_EQUAL (value));
|
|
break;
|
|
case MONO_LE_RELATION:
|
|
MONO_UPPER_EVALUATION_RANGE_INTERSECTION (range->upper, value);
|
|
break;
|
|
case MONO_GT_RELATION:
|
|
MONO_LOWER_EVALUATION_RANGE_INTERSECTION (range->lower, MONO_LOWER_EVALUATION_RANGE_NOT_EQUAL (value));
|
|
break;
|
|
case MONO_GE_RELATION:
|
|
MONO_LOWER_EVALUATION_RANGE_INTERSECTION (range->lower, value);
|
|
break;
|
|
default:
|
|
g_assert_not_reached();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Perform the intersection of two pairs of ranges (taking into account the
|
|
* relation between the ranges and a given delta).
|
|
* ranges: the ranges that will be intersected
|
|
* other_ranges the other ranges that will be intersected
|
|
* delta: the delta between the pairs of ranges
|
|
* relation: the relation between the pairs of ranges
|
|
*/
|
|
static void
|
|
intersect_ranges( MonoRelationsEvaluationRanges *ranges, MonoRelationsEvaluationRanges *other_ranges, int delta, MonoValueRelation relation )
|
|
{
|
|
if (delta == 0) {
|
|
switch (relation) {
|
|
case MONO_NO_RELATION:
|
|
MONO_MAKE_RELATIONS_EVALUATION_RANGES_IMPOSSIBLE (*ranges);
|
|
break;
|
|
case MONO_ANY_RELATION:
|
|
break;
|
|
case MONO_EQ_RELATION:
|
|
MONO_RELATIONS_EVALUATION_RANGES_INTERSECTION (*ranges, *other_ranges);
|
|
break;
|
|
case MONO_NE_RELATION: {
|
|
/* FIXME Figure this out! (ignoring it is safe anyway) */
|
|
break;
|
|
}
|
|
case MONO_LT_RELATION:
|
|
MONO_UPPER_EVALUATION_RANGE_INTERSECTION (ranges->zero.upper, MONO_UPPER_EVALUATION_RANGE_NOT_EQUAL (other_ranges->zero.upper));
|
|
MONO_UPPER_EVALUATION_RANGE_INTERSECTION (ranges->variable.upper, MONO_UPPER_EVALUATION_RANGE_NOT_EQUAL (other_ranges->variable.upper));
|
|
break;
|
|
case MONO_LE_RELATION:
|
|
MONO_UPPER_EVALUATION_RANGE_INTERSECTION (ranges->zero.upper, other_ranges->zero.upper);
|
|
MONO_UPPER_EVALUATION_RANGE_INTERSECTION (ranges->variable.upper, other_ranges->variable.upper);
|
|
break;
|
|
case MONO_GT_RELATION:
|
|
MONO_LOWER_EVALUATION_RANGE_INTERSECTION (ranges->zero.lower, MONO_LOWER_EVALUATION_RANGE_NOT_EQUAL (other_ranges->zero.lower));
|
|
MONO_LOWER_EVALUATION_RANGE_INTERSECTION (ranges->variable.lower, MONO_LOWER_EVALUATION_RANGE_NOT_EQUAL (other_ranges->variable.lower));
|
|
break;
|
|
case MONO_GE_RELATION:
|
|
MONO_LOWER_EVALUATION_RANGE_INTERSECTION (ranges->zero.lower, other_ranges->zero.lower);
|
|
MONO_LOWER_EVALUATION_RANGE_INTERSECTION (ranges->variable.lower, other_ranges->variable.lower);
|
|
break;
|
|
default:
|
|
g_assert_not_reached();
|
|
}
|
|
} else {
|
|
MonoRelationsEvaluationRanges translated_ranges = *other_ranges;
|
|
MONO_ADD_DELTA_SAFELY_TO_RANGES (translated_ranges, delta);
|
|
intersect_ranges( ranges, &translated_ranges, FALSE, relation );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Recursive method that traverses the relation graph to evaluate the
|
|
* relation between two variables.
|
|
* At the end of the execution, the resulting ranges are in the context of
|
|
* the "starting" variable.
|
|
* area: the current evaluation area (it contains the relation graph and
|
|
* memory for all the evaluation contexts is already allocated)
|
|
* variable: starting variable (the value ranges in its context are the result
|
|
* of the execution of this procedure)
|
|
* target_variable: the variable with respect to which the starting variable
|
|
* is evaluated (tipically the starting variable is the index
|
|
* and the target one is the array (which means its length))
|
|
* father_context: the previous evaluation context in recursive invocations
|
|
* (or NULL for the first invocation)
|
|
*/
|
|
static void
|
|
evaluate_relation_with_target_variable (MonoVariableRelationsEvaluationArea *area, const int variable, const int target_variable, MonoRelationsEvaluationContext *father_context)
|
|
{
|
|
MonoRelationsEvaluationContext * const context = &(area->contexts [variable]);
|
|
MonoRelationsEvaluationStatus * const status = &(area->statuses [variable]);
|
|
|
|
// First of all, we check the evaluation status
|
|
// (what must be done is *very* different in each case)
|
|
switch (*status) {
|
|
case MONO_RELATIONS_EVALUATION_NOT_STARTED: {
|
|
MonoSummarizedValueRelation *relation = &(area->relations [variable]);
|
|
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("Evaluating variable %d (target variable %d)\n", variable, target_variable);
|
|
print_summarized_value_relation (relation);
|
|
printf ("\n");
|
|
}
|
|
|
|
// We properly inizialize the context
|
|
*status = MONO_RELATIONS_EVALUATION_IN_PROGRESS;
|
|
context->father = father_context;
|
|
MONO_MAKE_RELATIONS_EVALUATION_RANGES_WEAK (context->ranges);
|
|
|
|
// If we have found the target variable, we can set the range
|
|
// related to it in the context to "equal" (which is [0,0])
|
|
if (variable == target_variable) {
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("Target variable reached (%d), continuing to evaluate relations with constants\n", variable);
|
|
}
|
|
context->ranges.variable.lower = 0;
|
|
context->ranges.variable.upper = 0;
|
|
}
|
|
|
|
// Examine all relations for this variable (scan the list)
|
|
// The contribute of each relation will be intersected (logical and)
|
|
while (relation != NULL) {
|
|
context->current_relation = relation;
|
|
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("Processing (%d): ", variable);
|
|
print_summarized_value_relation (relation);
|
|
printf ("\n");
|
|
}
|
|
|
|
// We decie what to do according the the type of the related value
|
|
switch (relation->related_value.type) {
|
|
case MONO_ANY_SUMMARIZED_VALUE:
|
|
// No added information, skip it
|
|
break;
|
|
case MONO_CONSTANT_SUMMARIZED_VALUE:
|
|
// Intersect range with constant (taking into account the relation)
|
|
intersect_value (&(context->ranges.zero), relation->related_value.value.constant.value, relation->relation);
|
|
break;
|
|
case MONO_VARIABLE_SUMMARIZED_VALUE:
|
|
// Generally, evaluate related variable and intersect ranges.
|
|
// However, some check is necessary...
|
|
|
|
// If the relation is "ANY", nothing to do (no added information)
|
|
if (relation->relation != MONO_ANY_RELATION) {
|
|
int related_variable = relation->related_value.value.variable.variable;
|
|
MonoRelationsEvaluationContext *related_context = &(area->contexts [related_variable]);
|
|
MonoRelationsEvaluationStatus related_status = area->statuses [related_variable];
|
|
|
|
// The second condition in the "or" avoids messing with "back edges" in the graph traversal
|
|
// (they are simply ignored instead of triggering the handling of recursion)
|
|
if ( (related_status == MONO_RELATIONS_EVALUATION_NOT_STARTED) || !
|
|
((related_context->current_relation->related_value.type == MONO_VARIABLE_SUMMARIZED_VALUE) &&
|
|
(related_context->current_relation->related_value.value.variable.variable == variable))) {
|
|
// Evaluate the related variable
|
|
evaluate_relation_with_target_variable (area, related_variable, target_variable, context);
|
|
|
|
// Check if we are part of a recursive loop
|
|
if (*status & MONO_RELATIONS_EVALUATION_IS_RECURSIVE) {
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("Recursivity detected for variable %d (target variable %d), status ", variable, target_variable);
|
|
print_evaluation_context_status (*status);
|
|
}
|
|
|
|
// If we are, check if the evaluation of the related variable is complete
|
|
if (related_status == MONO_RELATIONS_EVALUATION_COMPLETED) {
|
|
// If it is complete, we are part of a recursive definition.
|
|
// Since it is a *definition* (and definitions are evaluated *before*
|
|
// conditions because they are first in the list), intersection is not
|
|
// strictly necessary, we simply copy the ranges and apply the delta
|
|
context->ranges = related_context->ranges;
|
|
/* Delta has already been checked for over/under-flow when evaluating values */
|
|
MONO_ADD_DELTA_SAFELY_TO_RANGES (context->ranges, relation->related_value.value.variable.delta);
|
|
*status = MONO_RELATIONS_EVALUATION_COMPLETED;
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf (", ranges already computed, result: \n");
|
|
print_evaluation_context_ranges (&(context->ranges));
|
|
printf (" (delta is %d)\n", relation->related_value.value.variable.delta);
|
|
}
|
|
} else {
|
|
// If it is not complete, do nothing (we do not have enough information)
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf (", ranges not computed\n");
|
|
}
|
|
}
|
|
} else {
|
|
// If we are not (the common case) intersect the result
|
|
intersect_ranges( &(context->ranges), &(related_context->ranges), relation->related_value.value.variable.delta, relation->relation );
|
|
}
|
|
} else {
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("Relation is a back-edge in this traversal, skipping\n");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case MONO_PHI_SUMMARIZED_VALUE: {
|
|
// We must compute all PHI alternatives, combining the results (with a union, which is a logical "or"),
|
|
// and intersect this result with the ranges in the context; we must also take into account recursions
|
|
// (with loops that can be ascending, descending, or indefinite)
|
|
MonoRelationsEvaluationRanges phi_ranges;
|
|
int phi;
|
|
gboolean is_ascending = FALSE;
|
|
gboolean is_descending = FALSE;
|
|
|
|
MONO_MAKE_RELATIONS_EVALUATION_RANGES_IMPOSSIBLE (phi_ranges);
|
|
for (phi = 0; phi < relation->related_value.value.phi.number_of_alternatives; phi++) {
|
|
int phi_alternative = relation->related_value.value.phi.phi_alternatives [phi];
|
|
evaluate_relation_with_target_variable (area, phi_alternative, target_variable, context);
|
|
|
|
// This means we are part of a recursive loop
|
|
if (*status & MONO_RELATIONS_EVALUATION_IS_RECURSIVE) {
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("Recursivity detected for variable %d (target variable %d), status ", variable, target_variable);
|
|
print_evaluation_context_status (*status);
|
|
printf ("\n");
|
|
}
|
|
if (*status & MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_ASCENDING) {
|
|
is_ascending = TRUE;
|
|
}
|
|
if (*status & MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_DESCENDING) {
|
|
is_descending = TRUE;
|
|
}
|
|
if (*status & MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_INDEFINITE) {
|
|
is_ascending = TRUE;
|
|
is_descending = TRUE;
|
|
}
|
|
|
|
// Clear "recursivity" bits in the status (recursion has been handled)
|
|
*status = MONO_RELATIONS_EVALUATION_IN_PROGRESS;
|
|
} else {
|
|
MONO_RELATIONS_EVALUATION_RANGES_UNION (phi_ranges, area->contexts [phi_alternative].ranges);
|
|
}
|
|
}
|
|
|
|
// Apply the effects of all recursive loops
|
|
if (is_ascending) {
|
|
phi_ranges.zero.upper = INT_MAX;
|
|
phi_ranges.variable.upper = INT_MAX;
|
|
}
|
|
if (is_descending) {
|
|
phi_ranges.zero.lower = INT_MIN;
|
|
phi_ranges.variable.lower = INT_MIN;
|
|
}
|
|
|
|
// Intersect final result
|
|
MONO_RELATIONS_EVALUATION_RANGES_INTERSECTION (context->ranges, phi_ranges);
|
|
break;
|
|
}
|
|
default:
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
// Pass to next relation
|
|
relation = relation->next;
|
|
}
|
|
|
|
// Check if any recursivity bits are still in the status, and in any case clear them
|
|
if (*status & MONO_RELATIONS_EVALUATION_IS_RECURSIVE) {
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("Recursivity for variable %d (target variable %d) discards computation, status ", variable, target_variable);
|
|
print_evaluation_context_status (*status);
|
|
printf ("\n");
|
|
}
|
|
// If yes, we did not have enough information (most likely we were evaluated inside a PHI, but we also
|
|
// depended on the same PHI, which was still in evaluation...), so clear the status to "NOT_STARTED"
|
|
// (if we will be evaluated again, the PHI will be already done, so our evaluation will succeed)
|
|
*status = MONO_RELATIONS_EVALUATION_NOT_STARTED;
|
|
} else {
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("Ranges for variable %d (target variable %d) computed: ", variable, target_variable);
|
|
print_evaluation_context_ranges (&(context->ranges));
|
|
printf ("\n");
|
|
}
|
|
// If not (the common case) the evaluation is complete, and the result is in the context
|
|
*status = MONO_RELATIONS_EVALUATION_COMPLETED;
|
|
}
|
|
break;
|
|
}
|
|
case MONO_RELATIONS_EVALUATION_IN_PROGRESS: {
|
|
// This means we are in a recursive loop
|
|
MonoRelationsEvaluationContext *current_context = father_context;
|
|
MonoRelationsEvaluationContext *last_context = context->father;
|
|
gboolean evaluation_can_be_recursive = TRUE;
|
|
gboolean evaluation_is_definition = TRUE;
|
|
int path_value = 0;
|
|
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("Evaluation of variable %d (target variable %d) already in progress\n", variable, target_variable);
|
|
print_evaluation_context (context, *status);
|
|
print_summarized_value_relation (context->current_relation);
|
|
printf ("\n");
|
|
}
|
|
|
|
// We must check if the loop can be a recursive definition (we scan the whole loop)
|
|
while (current_context != last_context) {
|
|
if (current_context == NULL) {
|
|
printf ("Broken recursive ring in ABC removal\n");
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
if (current_context->current_relation->relation_is_static_definition) {
|
|
if (current_context->current_relation->related_value.type == MONO_VARIABLE_SUMMARIZED_VALUE) {
|
|
/* No need to check path_value for over/under-flow, since delta should be safe */
|
|
path_value += current_context->current_relation->related_value.value.variable.delta;
|
|
} else if (current_context->current_relation->related_value.type != MONO_PHI_SUMMARIZED_VALUE) {
|
|
evaluation_can_be_recursive = FALSE;
|
|
}
|
|
} else {
|
|
evaluation_is_definition = FALSE;
|
|
evaluation_can_be_recursive = FALSE;
|
|
}
|
|
|
|
current_context = current_context->father;
|
|
}
|
|
|
|
// If this is a recursive definition, we properly flag the status in all the involved contexts
|
|
if (evaluation_is_definition) {
|
|
MonoRelationsEvaluationStatus recursive_status;
|
|
if (evaluation_can_be_recursive) {
|
|
if (path_value > 0) {
|
|
recursive_status = MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_ASCENDING;
|
|
} else if (path_value < 0) {
|
|
recursive_status = MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_DESCENDING;
|
|
} else {
|
|
recursive_status = MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_INDEFINITE;
|
|
}
|
|
} else {
|
|
recursive_status = MONO_RELATIONS_EVALUATION_IS_RECURSIVELY_INDEFINITE;
|
|
}
|
|
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("Recursivity accepted (");
|
|
print_evaluation_context_status (recursive_status);
|
|
printf (")\n");
|
|
}
|
|
|
|
current_context = father_context;
|
|
while (current_context != last_context) {
|
|
int index = current_context - area->contexts;
|
|
MonoRelationsEvaluationStatus *current_status = &(area->statuses [index]);
|
|
*current_status = (MonoRelationsEvaluationStatus)(*current_status | recursive_status);
|
|
current_context = current_context->father;
|
|
}
|
|
} else {
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("Recursivity rejected (some relation in the cycle is not a defintion)\n");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case MONO_RELATIONS_EVALUATION_COMPLETED: {
|
|
return;
|
|
}
|
|
default:
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("Variable %d (target variable %d) already in a recursive ring, skipping\n", variable, target_variable);
|
|
print_evaluation_context (context, *status);
|
|
print_summarized_value_relation (context->current_relation);
|
|
printf ("\n");
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Apply the given value kind to the given range
|
|
*/
|
|
static void
|
|
apply_value_kind_to_range (MonoRelationsEvaluationRange *range, MonoIntegerValueKind value_kind)
|
|
{
|
|
if (value_kind != MONO_UNKNOWN_INTEGER_VALUE) {
|
|
if (value_kind & MONO_UNSIGNED_VALUE_FLAG) {
|
|
if (range->lower < 0) {
|
|
range->lower = 0;
|
|
}
|
|
if ((value_kind & MONO_INTEGER_VALUE_SIZE_BITMASK) == 1) {
|
|
if (range->upper > 0xff) {
|
|
range->upper = 0xff;
|
|
}
|
|
} else if ((value_kind & MONO_INTEGER_VALUE_SIZE_BITMASK) == 2) {
|
|
if (range->upper > 0xffff) {
|
|
range->upper = 0xffff;
|
|
}
|
|
}
|
|
} else {
|
|
if ((value_kind & MONO_INTEGER_VALUE_SIZE_BITMASK) == 1) {
|
|
if (range->lower < -0x80) {
|
|
range->lower = -0x80;
|
|
}
|
|
if (range->upper > 0x7f) {
|
|
range->upper = 0x7f;
|
|
}
|
|
} else if ((value_kind & MONO_INTEGER_VALUE_SIZE_BITMASK) == 2) {
|
|
if (range->lower < -0x8000) {
|
|
range->lower = -0x8000;
|
|
}
|
|
if (range->upper > 0x7fff) {
|
|
range->upper = 0x7fff;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Attempt the removal of bounds checks from a MonoInst.
|
|
* inst: the MonoInst
|
|
* area: the current evaluation area (it contains the relation graph and
|
|
* memory for all the evaluation contexts is already allocated)
|
|
*/
|
|
static void
|
|
remove_abc_from_inst (MonoInst *ins, MonoVariableRelationsEvaluationArea *area)
|
|
{
|
|
/* FIXME: Add support for 'constant' arrays and constant indexes */
|
|
|
|
int array_variable = ins->sreg1;
|
|
int index_variable = ins->sreg2;
|
|
MonoRelationsEvaluationContext *array_context = &(area->contexts [array_variable]);
|
|
MonoRelationsEvaluationContext *index_context = &(area->contexts [index_variable]);
|
|
|
|
clean_contexts (area, area->cfg->next_vreg);
|
|
|
|
evaluate_relation_with_target_variable (area, index_variable, array_variable, NULL);
|
|
evaluate_relation_with_target_variable (area, array_variable, array_variable, NULL);
|
|
|
|
if ((index_context->ranges.zero.lower >=0) && ((index_context->ranges.variable.upper < 0)||(index_context->ranges.zero.upper < array_context->ranges.zero.lower))) {
|
|
if (REPORT_ABC_REMOVAL) {
|
|
printf ("ARRAY-ACCESS: removed bounds check on array %d with index %d\n",
|
|
array_variable, index_variable);
|
|
}
|
|
NULLIFY_INS (ins);
|
|
} else {
|
|
if (TRACE_ABC_REMOVAL) {
|
|
if (index_context->ranges.zero.lower >= 0) {
|
|
printf ("ARRAY-ACCESS: Removed lower bound check on array %d with index %d\n", array_variable, index_variable);
|
|
}
|
|
if (index_context->ranges.variable.upper < 0) {
|
|
printf ("ARRAY-ACCESS: Removed upper bound check (through variable) on array %d with index %d\n", array_variable, index_variable);
|
|
}
|
|
if (index_context->ranges.zero.upper < array_context->ranges.zero.lower) {
|
|
printf ("ARRAY-ACCESS: Removed upper bound check (through constant) on array %d with index %d\n", array_variable, index_variable);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
eval_non_null (MonoVariableRelationsEvaluationArea *area, int reg)
|
|
{
|
|
MonoRelationsEvaluationContext *context = &(area->contexts [reg]);
|
|
|
|
clean_contexts (area, area->cfg->next_vreg);
|
|
evaluate_relation_with_target_variable (area, reg, reg, NULL);
|
|
|
|
return context->ranges.zero.lower > 0;
|
|
}
|
|
|
|
static void
|
|
add_non_null (MonoVariableRelationsEvaluationArea *area, MonoCompile *cfg, int reg,
|
|
GSList **check_relations)
|
|
{
|
|
MonoAdditionalVariableRelation *rel;
|
|
|
|
rel = (MonoAdditionalVariableRelation *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoAdditionalVariableRelation));
|
|
rel->variable = reg;
|
|
rel->relation.relation = MONO_GT_RELATION;
|
|
rel->relation.related_value.type = MONO_CONSTANT_SUMMARIZED_VALUE;
|
|
rel->relation.related_value.value.constant.value = 0;
|
|
|
|
apply_change_to_evaluation_area (area, rel);
|
|
|
|
*check_relations = g_slist_append_mempool (cfg->mempool, *check_relations, rel);
|
|
}
|
|
|
|
/*
|
|
* Process a BB removing bounds checks from array accesses.
|
|
* It does the following (in sequence):
|
|
* - Get the BB entry condition
|
|
* - Add its relations to the relation graph in the evaluation area
|
|
* - Process all the MonoInst trees in the BB
|
|
* - Recursively process all the children BBs in the dominator tree
|
|
* - Remove the relations previously added to the relation graph
|
|
*
|
|
* bb: the BB that must be processed
|
|
* area: the current evaluation area (it contains the relation graph and
|
|
* memory for all the evaluation contexts is already allocated)
|
|
*/
|
|
static void
|
|
process_block (MonoCompile *cfg, MonoBasicBlock *bb, MonoVariableRelationsEvaluationArea *area) {
|
|
int inst_index;
|
|
MonoInst *ins;
|
|
MonoAdditionalVariableRelationsForBB additional_relations;
|
|
GSList *dominated_bb, *l;
|
|
GSList *check_relations = NULL;
|
|
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("\nProcessing block %d [dfn %d]...\n", bb->block_num, bb->dfn);
|
|
}
|
|
|
|
if (bb->region != -1)
|
|
return;
|
|
|
|
get_relations_from_previous_bb (area, bb, &additional_relations);
|
|
if (TRACE_ABC_REMOVAL) {
|
|
if (additional_relations.relation1.relation.relation != MONO_ANY_RELATION) {
|
|
printf ("Adding relation 1 on variable %d: ", additional_relations.relation1.variable);
|
|
print_summarized_value_relation (&(additional_relations.relation1.relation));
|
|
printf ("\n");
|
|
}
|
|
if (additional_relations.relation2.relation.relation != MONO_ANY_RELATION) {
|
|
printf ("Adding relation 2 on variable %d: ", additional_relations.relation2.variable);
|
|
print_summarized_value_relation (&(additional_relations.relation2.relation));
|
|
printf ("\n");
|
|
}
|
|
}
|
|
apply_change_to_evaluation_area (area, &(additional_relations.relation1));
|
|
apply_change_to_evaluation_area (area, &(additional_relations.relation2));
|
|
|
|
inst_index = 0;
|
|
for (ins = bb->code; ins; ins = ins->next) {
|
|
MonoAdditionalVariableRelation *rel;
|
|
int array_var, index_var;
|
|
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("Processing instruction %d\n", inst_index);
|
|
inst_index++;
|
|
}
|
|
|
|
if (ins->opcode == OP_BOUNDS_CHECK) { /* Handle OP_LDELEMA2D, too */
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("Attempting check removal...\n");
|
|
}
|
|
|
|
array_var = ins->sreg1;
|
|
index_var = ins->sreg2;
|
|
|
|
remove_abc_from_inst (ins, area);
|
|
|
|
/* We can derive additional relations from the bounds check */
|
|
if (ins->opcode != OP_NOP) {
|
|
rel = (MonoAdditionalVariableRelation *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoAdditionalVariableRelation));
|
|
rel->variable = index_var;
|
|
rel->relation.relation = MONO_LT_RELATION;
|
|
rel->relation.related_value.type = MONO_VARIABLE_SUMMARIZED_VALUE;
|
|
rel->relation.related_value.value.variable.variable = array_var;
|
|
rel->relation.related_value.value.variable.delta = 0;
|
|
|
|
apply_change_to_evaluation_area (area, rel);
|
|
|
|
check_relations = g_slist_append_mempool (cfg->mempool, check_relations, rel);
|
|
|
|
rel = (MonoAdditionalVariableRelation *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoAdditionalVariableRelation));
|
|
rel->variable = index_var;
|
|
rel->relation.relation = MONO_GE_RELATION;
|
|
rel->relation.related_value.type = MONO_CONSTANT_SUMMARIZED_VALUE;
|
|
rel->relation.related_value.value.constant.value = 0;
|
|
|
|
apply_change_to_evaluation_area (area, rel);
|
|
|
|
check_relations = g_slist_append_mempool (cfg->mempool, check_relations, rel);
|
|
}
|
|
}
|
|
|
|
if (ins->opcode == OP_CHECK_THIS) {
|
|
if (eval_non_null (area, ins->sreg1)) {
|
|
if (REPORT_ABC_REMOVAL)
|
|
printf ("ARRAY-ACCESS: removed check_this instruction.\n");
|
|
NULLIFY_INS (ins);
|
|
}
|
|
}
|
|
|
|
if (ins->opcode == OP_NOT_NULL)
|
|
add_non_null (area, cfg, ins->sreg1, &check_relations);
|
|
|
|
/*
|
|
* FIXME: abcrem equates an array with its length,
|
|
* so a = new int [100] implies a != null, but a = new int [0] doesn't.
|
|
*/
|
|
/*
|
|
* Eliminate MONO_INST_FAULT flags if possible.
|
|
*/
|
|
if (COMPILE_LLVM (cfg) && (ins->opcode == OP_LDLEN ||
|
|
ins->opcode == OP_BOUNDS_CHECK ||
|
|
ins->opcode == OP_STRLEN ||
|
|
(MONO_IS_LOAD_MEMBASE (ins) && (ins->flags & MONO_INST_FAULT)) ||
|
|
(MONO_IS_STORE_MEMBASE (ins) && (ins->flags & MONO_INST_FAULT)))) {
|
|
int reg;
|
|
|
|
if (MONO_IS_STORE_MEMBASE (ins))
|
|
reg = ins->inst_destbasereg;
|
|
else if (MONO_IS_LOAD_MEMBASE (ins))
|
|
reg = ins->inst_basereg;
|
|
else
|
|
reg = ins->sreg1;
|
|
|
|
/*
|
|
* This doesn't work because LLVM can move the non-faulting loads before the faulting
|
|
* ones (test_0_llvm_moving_faulting_loads ()).
|
|
* So only do it if we know the load cannot be moved before the instruction which ensures it is not
|
|
* null (i.e. the def of its sreg).
|
|
*/
|
|
if (area->defs [reg] && area->defs [reg]->opcode == OP_NEWARR) {
|
|
if (REPORT_ABC_REMOVAL)
|
|
printf ("ARRAY-ACCESS: removed MONO_INST_FAULT flag.\n");
|
|
ins->flags &= ~MONO_INST_FAULT;
|
|
}
|
|
/*
|
|
if (eval_non_null (area, reg)) {
|
|
if (REPORT_ABC_REMOVAL)
|
|
printf ("ARRAY-ACCESS: removed MONO_INST_FAULT flag.\n");
|
|
ins->flags &= ~MONO_INST_FAULT;
|
|
} else {
|
|
add_non_null (area, cfg, reg, &check_relations);
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("Processing block %d [dfn %d] done.\n", bb->block_num, bb->dfn);
|
|
}
|
|
|
|
for (dominated_bb = bb->dominated; dominated_bb != NULL; dominated_bb = dominated_bb->next) {
|
|
process_block (cfg, (MonoBasicBlock*) (dominated_bb->data), area);
|
|
}
|
|
|
|
for (l = check_relations; l; l = l->next)
|
|
remove_change_from_evaluation_area ((MonoAdditionalVariableRelation *)l->data);
|
|
|
|
remove_change_from_evaluation_area (&(additional_relations.relation1));
|
|
remove_change_from_evaluation_area (&(additional_relations.relation2));
|
|
}
|
|
|
|
static MonoIntegerValueKind
|
|
type_to_value_kind (MonoType *type)
|
|
{
|
|
if (type->byref)
|
|
return MONO_UNKNOWN_INTEGER_VALUE;
|
|
switch (type->type) {
|
|
case MONO_TYPE_I1:
|
|
return MONO_INTEGER_VALUE_SIZE_1;
|
|
break;
|
|
case MONO_TYPE_U1:
|
|
return MONO_UNSIGNED_INTEGER_VALUE_SIZE_1;
|
|
break;
|
|
case MONO_TYPE_I2:
|
|
return MONO_INTEGER_VALUE_SIZE_2;
|
|
break;
|
|
case MONO_TYPE_U2:
|
|
return MONO_UNSIGNED_INTEGER_VALUE_SIZE_2;
|
|
break;
|
|
case MONO_TYPE_I4:
|
|
return MONO_INTEGER_VALUE_SIZE_4;
|
|
break;
|
|
case MONO_TYPE_U4:
|
|
return MONO_UNSIGNED_INTEGER_VALUE_SIZE_4;
|
|
break;
|
|
case MONO_TYPE_I:
|
|
return (MonoIntegerValueKind)SIZEOF_VOID_P;
|
|
break;
|
|
case MONO_TYPE_U:
|
|
return (MonoIntegerValueKind)(MONO_UNSIGNED_VALUE_FLAG | SIZEOF_VOID_P);
|
|
break;
|
|
case MONO_TYPE_I8:
|
|
return MONO_INTEGER_VALUE_SIZE_8;
|
|
break;
|
|
case MONO_TYPE_U8:
|
|
return MONO_UNSIGNED_INTEGER_VALUE_SIZE_8;
|
|
default:
|
|
return MONO_UNKNOWN_INTEGER_VALUE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* mono_perform_abc_removal:
|
|
* \param cfg Control Flow Graph
|
|
*
|
|
* Performs the ABC removal from a cfg in SSA form.
|
|
* It does the following:
|
|
* - Prepare the evaluation area
|
|
* - Allocate memory for the relation graph in the evaluation area
|
|
* (of course, only for variable definitions) and summarize there all
|
|
* variable definitions
|
|
* - Allocate memory for the evaluation contexts in the evaluation area
|
|
* - Recursively process all the BBs in the dominator tree (it is enough
|
|
* to invoke the processing on the entry BB)
|
|
*
|
|
* cfg: the method code
|
|
*/
|
|
void
|
|
mono_perform_abc_removal (MonoCompile *cfg)
|
|
{
|
|
MonoVariableRelationsEvaluationArea area;
|
|
MonoBasicBlock *bb;
|
|
int i;
|
|
|
|
verbose_level = cfg->verbose_level;
|
|
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("\nRemoving array bound checks in %s\n", mono_method_full_name (cfg->method, TRUE));
|
|
}
|
|
|
|
area.cfg = cfg;
|
|
area.relations = (MonoSummarizedValueRelation *)
|
|
mono_mempool_alloc (cfg->mempool, sizeof (MonoSummarizedValueRelation) * (cfg->next_vreg) * 2);
|
|
|
|
area.contexts = (MonoRelationsEvaluationContext *)
|
|
mono_mempool_alloc0 (cfg->mempool, sizeof (MonoRelationsEvaluationContext) * (cfg->next_vreg));
|
|
|
|
area.statuses = (MonoRelationsEvaluationStatus *)
|
|
mono_mempool_alloc0 (cfg->mempool, sizeof (MonoRelationsEvaluationStatus) * (cfg->next_vreg));
|
|
|
|
area.variable_value_kind = (MonoIntegerValueKind *)
|
|
mono_mempool_alloc (cfg->mempool, sizeof (MonoIntegerValueKind) * (cfg->next_vreg));
|
|
area.defs = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * cfg->next_vreg);
|
|
for (i = 0; i < cfg->next_vreg; i++) {
|
|
area.variable_value_kind [i] = MONO_UNKNOWN_INTEGER_VALUE;
|
|
area.relations [i].relation = MONO_EQ_RELATION;
|
|
area.relations [i].relation_is_static_definition = TRUE;
|
|
MAKE_VALUE_ANY (area.relations [i].related_value);
|
|
area.relations [i].next = NULL;
|
|
area.defs [i] = NULL;
|
|
}
|
|
|
|
for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
|
|
MonoInst *ins;
|
|
|
|
if (TRACE_ABC_REMOVAL)
|
|
printf ("\nABCREM BLOCK %d:\n", bb->block_num);
|
|
|
|
for (ins = bb->code; ins; ins = ins->next) {
|
|
const char *spec = INS_INFO (ins->opcode);
|
|
gint32 idx, *reg;
|
|
|
|
if (spec [MONO_INST_DEST] == ' ' || MONO_IS_STORE_MEMBASE (ins))
|
|
continue;
|
|
|
|
MONO_INS_FOR_EACH_REG (ins, idx, reg) {
|
|
MonoInst *var = get_vreg_to_inst (cfg, *reg);
|
|
if (var && (!MONO_VARINFO (cfg, var->inst_c0)->def))
|
|
break;
|
|
}
|
|
if (idx < MONO_INST_LEN) {
|
|
if (TRACE_ABC_REMOVAL)
|
|
printf ("Global register %d is not in the SSA form, skipping.\n", *reg);
|
|
continue;
|
|
}
|
|
|
|
if (spec [MONO_INST_DEST] == 'i') {
|
|
MonoIntegerValueKind effective_value_kind;
|
|
MonoRelationsEvaluationRange range;
|
|
MonoSummarizedValueRelation *type_relation;
|
|
MonoInst *var;
|
|
|
|
if (TRACE_ABC_REMOVAL)
|
|
mono_print_ins (ins);
|
|
|
|
var = get_vreg_to_inst (cfg, ins->dreg);
|
|
if (var)
|
|
area.variable_value_kind [ins->dreg] = type_to_value_kind (var->inst_vtype);
|
|
|
|
effective_value_kind = get_relation_from_ins (&area, ins, &area.relations [ins->dreg], area.variable_value_kind [ins->dreg]);
|
|
|
|
MONO_MAKE_RELATIONS_EVALUATION_RANGE_WEAK (range);
|
|
apply_value_kind_to_range (&range, area.variable_value_kind [ins->dreg]);
|
|
apply_value_kind_to_range (&range, effective_value_kind);
|
|
|
|
if (range.upper < INT_MAX) {
|
|
type_relation = (MonoSummarizedValueRelation *) mono_mempool_alloc (cfg->mempool, sizeof (MonoSummarizedValueRelation));
|
|
type_relation->relation = MONO_LE_RELATION;
|
|
type_relation->related_value.type = MONO_CONSTANT_SUMMARIZED_VALUE;
|
|
type_relation->related_value.value.constant.value = range.upper;
|
|
type_relation->relation_is_static_definition = TRUE;
|
|
type_relation->next = area.relations [ins->dreg].next;
|
|
area.relations [ins->dreg].next = type_relation;
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("[var%d <= %d]", ins->dreg, range.upper);
|
|
}
|
|
}
|
|
if (range.lower > INT_MIN) {
|
|
type_relation = (MonoSummarizedValueRelation *) mono_mempool_alloc (cfg->mempool, sizeof (MonoSummarizedValueRelation));
|
|
type_relation->relation = MONO_GE_RELATION;
|
|
type_relation->related_value.type = MONO_CONSTANT_SUMMARIZED_VALUE;
|
|
type_relation->related_value.value.constant.value = range.lower;
|
|
type_relation->relation_is_static_definition = TRUE;
|
|
type_relation->next = area.relations [ins->dreg].next;
|
|
area.relations [ins->dreg].next = type_relation;
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("[var%d >= %d]", ins->dreg, range.lower);
|
|
}
|
|
}
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("Summarized variable %d: ", ins->dreg);
|
|
print_summarized_value (&(area.relations [ins->dreg].related_value));
|
|
printf ("\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Add symmetric relations */
|
|
for (i = 0; i < cfg->next_vreg; i++) {
|
|
if (area.relations [i].related_value.type == MONO_VARIABLE_SUMMARIZED_VALUE) {
|
|
int related_index = cfg->next_vreg + i;
|
|
int related_variable = area.relations [i].related_value.value.variable.variable;
|
|
|
|
area.relations [related_index].relation = MONO_EQ_RELATION;
|
|
area.relations [related_index].relation_is_static_definition = TRUE;
|
|
area.relations [related_index].related_value.type = MONO_VARIABLE_SUMMARIZED_VALUE;
|
|
area.relations [related_index].related_value.value.variable.variable = i;
|
|
area.relations [related_index].related_value.value.variable.delta = - area.relations [i].related_value.value.variable.delta;
|
|
|
|
area.relations [related_index].next = area.relations [related_variable].next;
|
|
area.relations [related_variable].next = &(area.relations [related_index]);
|
|
|
|
if (TRACE_ABC_REMOVAL) {
|
|
printf ("Added symmetric summarized value for variable variable %d (to %d): ", i, related_variable);
|
|
print_summarized_value (&(area.relations [related_index].related_value));
|
|
printf ("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
process_block (cfg, cfg->bblocks [0], &area);
|
|
}
|
|
|
|
#else /* !DISABLE_JIT */
|
|
|
|
MONO_EMPTY_SOURCE_FILE (abcremoval);
|
|
|
|
#endif /* !DISABLE_JIT */
|