You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
756 lines
29 KiB
756 lines
29 KiB
#include "rc_validate.h"
|
|
|
|
#include "rc_compat.h"
|
|
#include "rc_internal.h"
|
|
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
|
|
static unsigned rc_max_value(const rc_operand_t* operand)
|
|
{
|
|
if (operand->type == RC_OPERAND_CONST)
|
|
return operand->value.num;
|
|
|
|
if (!rc_operand_is_memref(operand))
|
|
return 0xFFFFFFFF;
|
|
|
|
switch (operand->size) {
|
|
case RC_MEMSIZE_BIT_0:
|
|
case RC_MEMSIZE_BIT_1:
|
|
case RC_MEMSIZE_BIT_2:
|
|
case RC_MEMSIZE_BIT_3:
|
|
case RC_MEMSIZE_BIT_4:
|
|
case RC_MEMSIZE_BIT_5:
|
|
case RC_MEMSIZE_BIT_6:
|
|
case RC_MEMSIZE_BIT_7:
|
|
return 1;
|
|
|
|
case RC_MEMSIZE_LOW:
|
|
case RC_MEMSIZE_HIGH:
|
|
return 0xF;
|
|
|
|
case RC_MEMSIZE_BITCOUNT:
|
|
return 8;
|
|
|
|
case RC_MEMSIZE_8_BITS:
|
|
return (operand->type == RC_OPERAND_BCD) ? 165 : 0xFF;
|
|
|
|
case RC_MEMSIZE_16_BITS:
|
|
case RC_MEMSIZE_16_BITS_BE:
|
|
return (operand->type == RC_OPERAND_BCD) ? 16665 : 0xFFFF;
|
|
|
|
case RC_MEMSIZE_24_BITS:
|
|
case RC_MEMSIZE_24_BITS_BE:
|
|
return (operand->type == RC_OPERAND_BCD) ? 1666665 : 0xFFFFFF;
|
|
|
|
default:
|
|
return (operand->type == RC_OPERAND_BCD) ? 166666665 : 0xFFFFFFFF;
|
|
}
|
|
}
|
|
|
|
static unsigned rc_scale_value(unsigned value, char oper, const rc_operand_t* operand)
|
|
{
|
|
switch (oper) {
|
|
case RC_OPERATOR_MULT:
|
|
{
|
|
unsigned long long scaled = ((unsigned long long)value) * rc_max_value(operand);
|
|
if (scaled > 0xFFFFFFFF)
|
|
return 0xFFFFFFFF;
|
|
|
|
return (unsigned)scaled;
|
|
}
|
|
|
|
case RC_OPERATOR_DIV:
|
|
{
|
|
const unsigned min_val = (operand->type == RC_OPERAND_CONST) ? operand->value.num : 1;
|
|
return value / min_val;
|
|
}
|
|
|
|
case RC_OPERATOR_AND:
|
|
return rc_max_value(operand);
|
|
|
|
case RC_OPERATOR_XOR:
|
|
return value | rc_max_value(operand);
|
|
|
|
default:
|
|
return value;
|
|
}
|
|
}
|
|
|
|
static int rc_validate_get_condition_index(const rc_condset_t* condset, const rc_condition_t* condition)
|
|
{
|
|
int index = 1;
|
|
const rc_condition_t* scan;
|
|
for (scan = condset->conditions; scan != NULL; scan = scan->next)
|
|
{
|
|
if (scan == condition)
|
|
return index;
|
|
|
|
++index;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rc_validate_range(unsigned min_val, unsigned max_val, char oper, unsigned max, char result[], const size_t result_size) {
|
|
switch (oper) {
|
|
case RC_OPERATOR_AND:
|
|
if (min_val > max) {
|
|
snprintf(result, result_size, "Mask has more bits than source");
|
|
return 0;
|
|
}
|
|
else if (min_val == 0 && max_val == 0) {
|
|
snprintf(result, result_size, "Result of mask always 0");
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case RC_OPERATOR_EQ:
|
|
if (min_val > max) {
|
|
snprintf(result, result_size, "Comparison is never true");
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case RC_OPERATOR_NE:
|
|
if (min_val > max) {
|
|
snprintf(result, result_size, "Comparison is always true");
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case RC_OPERATOR_GE:
|
|
if (min_val > max) {
|
|
snprintf(result, result_size, "Comparison is never true");
|
|
return 0;
|
|
}
|
|
if (max_val == 0) {
|
|
snprintf(result, result_size, "Comparison is always true");
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case RC_OPERATOR_GT:
|
|
if (min_val >= max) {
|
|
snprintf(result, result_size, "Comparison is never true");
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case RC_OPERATOR_LE:
|
|
if (min_val >= max) {
|
|
snprintf(result, result_size, "Comparison is always true");
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case RC_OPERATOR_LT:
|
|
if (min_val > max) {
|
|
snprintf(result, result_size, "Comparison is always true");
|
|
return 0;
|
|
}
|
|
if (max_val == 0) {
|
|
snprintf(result, result_size, "Comparison is never true");
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int rc_validate_condset(const rc_condset_t* condset, char result[], const size_t result_size, unsigned max_address) {
|
|
const rc_condition_t* cond;
|
|
unsigned max_val;
|
|
int index = 1;
|
|
unsigned long long add_source_max = 0;
|
|
int in_add_hits = 0;
|
|
int in_add_address = 0;
|
|
int is_combining = 0;
|
|
|
|
if (!condset) {
|
|
*result = '\0';
|
|
return 1;
|
|
}
|
|
|
|
for (cond = condset->conditions; cond; cond = cond->next, ++index) {
|
|
unsigned max = rc_max_value(&cond->operand1);
|
|
const int is_memref1 = rc_operand_is_memref(&cond->operand1);
|
|
const int is_memref2 = rc_operand_is_memref(&cond->operand2);
|
|
|
|
if (!in_add_address) {
|
|
if (is_memref1 && cond->operand1.value.memref->address > max_address) {
|
|
snprintf(result, result_size, "Condition %d: Address %04X out of range (max %04X)",
|
|
index, cond->operand1.value.memref->address, max_address);
|
|
return 0;
|
|
}
|
|
if (is_memref2 && cond->operand2.value.memref->address > max_address) {
|
|
snprintf(result, result_size, "Condition %d: Address %04X out of range (max %04X)",
|
|
index, cond->operand2.value.memref->address, max_address);
|
|
return 0;
|
|
}
|
|
}
|
|
else {
|
|
in_add_address = 0;
|
|
}
|
|
|
|
switch (cond->type) {
|
|
case RC_CONDITION_ADD_SOURCE:
|
|
max = rc_scale_value(max, cond->oper, &cond->operand2);
|
|
add_source_max += max;
|
|
is_combining = 1;
|
|
continue;
|
|
|
|
case RC_CONDITION_SUB_SOURCE:
|
|
max = rc_scale_value(max, cond->oper, &cond->operand2);
|
|
if (add_source_max < max) /* potential underflow - may be expected */
|
|
add_source_max = 0xFFFFFFFF;
|
|
is_combining = 1;
|
|
continue;
|
|
|
|
case RC_CONDITION_ADD_ADDRESS:
|
|
if (cond->operand1.type == RC_OPERAND_DELTA || cond->operand1.type == RC_OPERAND_PRIOR) {
|
|
snprintf(result, result_size, "Condition %d: Using pointer from previous frame", index);
|
|
return 0;
|
|
}
|
|
in_add_address = 1;
|
|
is_combining = 1;
|
|
continue;
|
|
|
|
case RC_CONDITION_ADD_HITS:
|
|
case RC_CONDITION_SUB_HITS:
|
|
in_add_hits = 1;
|
|
is_combining = 1;
|
|
break;
|
|
|
|
case RC_CONDITION_AND_NEXT:
|
|
case RC_CONDITION_OR_NEXT:
|
|
case RC_CONDITION_RESET_NEXT_IF:
|
|
is_combining = 1;
|
|
break;
|
|
|
|
default:
|
|
if (in_add_hits) {
|
|
if (cond->required_hits == 0) {
|
|
snprintf(result, result_size, "Condition %d: Final condition in AddHits chain must have a hit target", index);
|
|
return 0;
|
|
}
|
|
|
|
in_add_hits = 0;
|
|
}
|
|
|
|
is_combining = 0;
|
|
break;
|
|
}
|
|
|
|
/* if we're in an add source chain, check for overflow */
|
|
if (add_source_max) {
|
|
const unsigned long long overflow = add_source_max + max;
|
|
if (overflow > 0xFFFFFFFFUL)
|
|
max = 0xFFFFFFFF;
|
|
else
|
|
max += (unsigned)add_source_max;
|
|
}
|
|
|
|
/* check for comparing two differently sized memrefs */
|
|
max_val = rc_max_value(&cond->operand2);
|
|
if (max_val != max && add_source_max == 0 && is_memref1 && is_memref2) {
|
|
snprintf(result, result_size, "Condition %d: Comparing different memory sizes", index);
|
|
return 0;
|
|
}
|
|
|
|
/* if either side is a memref, or there's a running add source chain, check for impossible comparisons */
|
|
if (is_memref1 || is_memref2 || add_source_max) {
|
|
const size_t prefix_length = snprintf(result, result_size, "Condition %d: ", index);
|
|
|
|
unsigned min_val;
|
|
switch (cond->operand2.type) {
|
|
case RC_OPERAND_CONST:
|
|
min_val = cond->operand2.value.num;
|
|
break;
|
|
|
|
case RC_OPERAND_FP:
|
|
min_val = (int)cond->operand2.value.dbl;
|
|
|
|
/* cannot compare an integer memory reference to a non-integral floating point value */
|
|
/* assert: is_memref1 (because operand2==FP means !is_memref2) */
|
|
if (!add_source_max && !rc_operand_is_float_memref(&cond->operand1) &&
|
|
(float)min_val != cond->operand2.value.dbl) {
|
|
snprintf(result + prefix_length, result_size - prefix_length, "Comparison is never true");
|
|
return 0;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
min_val = 0;
|
|
|
|
/* cannot compare an integer memory reference to a non-integral floating point value */
|
|
/* assert: is_memref2 (because operand1==FP means !is_memref1) */
|
|
if (cond->operand1.type == RC_OPERAND_FP && !add_source_max && !rc_operand_is_float_memref(&cond->operand2) &&
|
|
(float)((int)cond->operand1.value.dbl) != cond->operand1.value.dbl) {
|
|
snprintf(result + prefix_length, result_size - prefix_length, "Comparison is never true");
|
|
return 0;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (rc_operand_is_float(&cond->operand2) && rc_operand_is_float(&cond->operand1)) {
|
|
/* both sides are floats, don't validate range*/
|
|
} else if (!rc_validate_range(min_val, max_val, cond->oper, max, result + prefix_length, result_size - prefix_length)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
add_source_max = 0;
|
|
}
|
|
|
|
if (is_combining) {
|
|
snprintf(result, result_size, "Final condition type expects another condition to follow");
|
|
return 0;
|
|
}
|
|
|
|
*result = '\0';
|
|
return 1;
|
|
}
|
|
|
|
int rc_validate_memrefs(const rc_memref_t* memref, char result[], const size_t result_size, unsigned max_address) {
|
|
if (max_address < 0xFFFFFFFF) {
|
|
while (memref) {
|
|
if (memref->address > max_address) {
|
|
snprintf(result, result_size, "Address %04X out of range (max %04X)", memref->address, max_address);
|
|
return 0;
|
|
}
|
|
|
|
memref = memref->next;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int rc_validate_is_combining_condition(const rc_condition_t* condition)
|
|
{
|
|
switch (condition->type)
|
|
{
|
|
case RC_CONDITION_ADD_ADDRESS:
|
|
case RC_CONDITION_ADD_HITS:
|
|
case RC_CONDITION_ADD_SOURCE:
|
|
case RC_CONDITION_AND_NEXT:
|
|
case RC_CONDITION_OR_NEXT:
|
|
case RC_CONDITION_RESET_NEXT_IF:
|
|
case RC_CONDITION_SUB_HITS:
|
|
case RC_CONDITION_SUB_SOURCE:
|
|
return 1;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static const rc_condition_t* rc_validate_next_non_combining_condition(const rc_condition_t* condition)
|
|
{
|
|
int is_combining = rc_validate_is_combining_condition(condition);
|
|
for (condition = condition->next; condition != NULL; condition = condition->next)
|
|
{
|
|
if (rc_validate_is_combining_condition(condition))
|
|
is_combining = 1;
|
|
else if (is_combining)
|
|
is_combining = 0;
|
|
else
|
|
return condition;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int rc_validate_get_opposite_comparison(int oper)
|
|
{
|
|
switch (oper)
|
|
{
|
|
case RC_OPERATOR_EQ: return RC_OPERATOR_NE;
|
|
case RC_OPERATOR_NE: return RC_OPERATOR_EQ;
|
|
case RC_OPERATOR_LT: return RC_OPERATOR_GE;
|
|
case RC_OPERATOR_LE: return RC_OPERATOR_GT;
|
|
case RC_OPERATOR_GT: return RC_OPERATOR_LE;
|
|
case RC_OPERATOR_GE: return RC_OPERATOR_LT;
|
|
default: return oper;
|
|
}
|
|
}
|
|
|
|
static const rc_operand_t* rc_validate_get_comparison(const rc_condition_t* condition, int* comparison, unsigned* value)
|
|
{
|
|
if (rc_operand_is_memref(&condition->operand1))
|
|
{
|
|
if (condition->operand2.type != RC_OPERAND_CONST)
|
|
return NULL;
|
|
|
|
*comparison = condition->oper;
|
|
*value = condition->operand2.value.num;
|
|
return &condition->operand1;
|
|
}
|
|
|
|
if (condition->operand1.type != RC_OPERAND_CONST)
|
|
return NULL;
|
|
|
|
if (!rc_operand_is_memref(&condition->operand2))
|
|
return NULL;
|
|
|
|
*comparison = rc_validate_get_opposite_comparison(condition->oper);
|
|
*value = condition->operand1.value.num;
|
|
return &condition->operand2;
|
|
}
|
|
|
|
enum {
|
|
RC_OVERLAP_NONE = 0,
|
|
RC_OVERLAP_CONFLICTING,
|
|
RC_OVERLAP_REDUNDANT,
|
|
RC_OVERLAP_DEFER
|
|
};
|
|
|
|
static int rc_validate_comparison_overlap(int comparison1, unsigned value1, int comparison2, unsigned value2)
|
|
{
|
|
/* NOTE: this only cares if comp2 conflicts with comp1.
|
|
* If comp1 conflicts with comp2, we'll catch that later (return RC_OVERLAP_NONE for now) */
|
|
switch (comparison2)
|
|
{
|
|
case RC_OPERATOR_EQ:
|
|
switch (comparison1) /* comp1 comp2 comp1 comp2 comp1 comp2 */
|
|
{ /* value1 = value2 value1 < value2 value1 > value2 */
|
|
case RC_OPERATOR_EQ: /* a == 1 && a == 1 | a == 1 && a == 2 | a == 2 && a == 1 */
|
|
/* redundant conflict conflict */
|
|
return (value1 == value2) ? RC_OVERLAP_REDUNDANT : RC_OVERLAP_CONFLICTING;
|
|
case RC_OPERATOR_LE: /* a <= 1 && a == 1 | a <= 1 && a == 2 | a <= 2 && a == 1 */
|
|
/* defer conflict defer */
|
|
return (value1 < value2) ? RC_OVERLAP_CONFLICTING : RC_OVERLAP_DEFER;
|
|
case RC_OPERATOR_GE: /* a >= 1 && a == 1 | a >= 1 && a == 2 | a >= 2 && a == 1 */
|
|
/* defer defer conflict */
|
|
return (value1 > value2) ? RC_OVERLAP_CONFLICTING : RC_OVERLAP_DEFER;
|
|
case RC_OPERATOR_NE: /* a != 1 && a == 1 | a != 1 && a == 2 | a != 2 && a == 1 */
|
|
/* conflict defer defer */
|
|
return (value1 == value2) ? RC_OVERLAP_CONFLICTING : RC_OVERLAP_DEFER;
|
|
case RC_OPERATOR_LT: /* a < 1 && a == 1 | a < 1 && a == 2 | a < 2 && a == 1 */
|
|
/* conflict conflict defer */
|
|
return (value1 <= value2) ? RC_OVERLAP_CONFLICTING : RC_OVERLAP_DEFER;
|
|
case RC_OPERATOR_GT: /* a > 1 && a == 1 | a > 1 && a == 2 | a > 2 && a == 1 */
|
|
/* conflict defer conflict */
|
|
return (value1 >= value2) ? RC_OVERLAP_CONFLICTING : RC_OVERLAP_DEFER;
|
|
}
|
|
break;
|
|
|
|
case RC_OPERATOR_NE:
|
|
switch (comparison1) /* comp1 comp2 comp1 comp2 comp1 comp2 */
|
|
{ /* value1 = value2 value1 < value2 value1 > value2 */
|
|
case RC_OPERATOR_EQ: /* a == 1 && a != 1 | a == 1 && a != 2 | a == 2 && a != 1 */
|
|
/* conflict redundant redundant */
|
|
return (value1 == value2) ? RC_OVERLAP_CONFLICTING : RC_OVERLAP_REDUNDANT;
|
|
case RC_OPERATOR_LE: /* a <= 1 && a != 1 | a <= 1 && a != 2 | a <= 2 && a != 1 */
|
|
/* none redundant none */
|
|
return (value1 < value2) ? RC_OVERLAP_REDUNDANT : RC_OVERLAP_NONE;
|
|
case RC_OPERATOR_GE: /* a >= 1 && a != 1 | a >= 1 && a != 2 | a >= 2 && a != 1 */
|
|
/* none none redundant */
|
|
return (value1 > value2) ? RC_OVERLAP_REDUNDANT : RC_OVERLAP_NONE;
|
|
case RC_OPERATOR_NE: /* a != 1 && a != 1 | a != 1 && a != 2 | a != 2 && a != 1 */
|
|
/* redundant none none */
|
|
return (value1 == value2) ? RC_OVERLAP_REDUNDANT : RC_OVERLAP_NONE;
|
|
case RC_OPERATOR_LT: /* a < 1 && a != 1 | a < 1 && a != 2 | a < 2 && a != 1 */
|
|
/* redundant redundant none */
|
|
return (value1 <= value2) ? RC_OVERLAP_REDUNDANT : RC_OVERLAP_NONE;
|
|
case RC_OPERATOR_GT: /* a > 1 && a != 1 | a > 1 && a != 2 | a > 2 && a != 1 */
|
|
/* redundant none redundant */
|
|
return (value1 >= value2) ? RC_OVERLAP_REDUNDANT : RC_OVERLAP_NONE;
|
|
}
|
|
break;
|
|
|
|
case RC_OPERATOR_LT:
|
|
switch (comparison1) /* comp1 comp2 comp1 comp2 comp1 comp2 */
|
|
{ /* value1 = value2 value1 < value2 value1 > value2 */
|
|
case RC_OPERATOR_EQ: /* a == 1 && a < 1 | a == 1 && a < 2 | a == 2 && a < 1 */
|
|
/* conflict redundant conflict */
|
|
return (value1 < value2) ? RC_OVERLAP_REDUNDANT : RC_OVERLAP_CONFLICTING;
|
|
case RC_OPERATOR_LE: /* a <= 1 && a < 1 | a <= 1 && a < 2 | a <= 2 && a < 1 */
|
|
/* defer redundant defer */
|
|
return (value1 < value2) ? RC_OVERLAP_REDUNDANT : RC_OVERLAP_DEFER;
|
|
case RC_OPERATOR_GE: /* a >= 1 && a < 1 | a >= 1 && a < 2 | a >= 2 && a < 1 */
|
|
/* conflict none conflict */
|
|
return (value1 >= value2) ? RC_OVERLAP_CONFLICTING : RC_OVERLAP_NONE;
|
|
case RC_OPERATOR_NE: /* a != 1 && a < 1 | a != 1 && a < 2 | a != 2 && a < 1 */
|
|
/* defer none defer */
|
|
return (value1 >= value2) ? RC_OVERLAP_DEFER : RC_OVERLAP_NONE;
|
|
case RC_OPERATOR_LT: /* a < 1 && a < 1 | a < 1 && a < 2 | a < 2 && a < 1 */
|
|
/* redundant redundant defer */
|
|
return (value1 <= value2) ? RC_OVERLAP_REDUNDANT : RC_OVERLAP_DEFER;
|
|
case RC_OPERATOR_GT: /* a > 1 && a < 1 | a > 1 && a < 2 | a > 2 && a < 1 */
|
|
/* conflict none conflict */
|
|
return (value1 >= value2) ? RC_OVERLAP_CONFLICTING : RC_OVERLAP_NONE;
|
|
}
|
|
break;
|
|
|
|
case RC_OPERATOR_LE:
|
|
switch (comparison1) /* comp1 comp2 comp1 comp2 comp1 comp2 */
|
|
{ /* value1 = value2 value1 < value2 value1 > value2 */
|
|
case RC_OPERATOR_EQ: /* a == 1 && a <= 1 | a == 1 && a <= 2 | a == 2 && a <= 1 */
|
|
/* redundant redundant conflict */
|
|
return (value1 <= value2) ? RC_OVERLAP_REDUNDANT : RC_OVERLAP_CONFLICTING;
|
|
case RC_OPERATOR_LE: /* a <= 1 && a <= 1 | a <= 1 && a <= 2 | a <= 2 && a <= 1 */
|
|
/* redundant redundant defer */
|
|
return (value1 <= value2) ? RC_OVERLAP_REDUNDANT : RC_OVERLAP_DEFER;
|
|
case RC_OPERATOR_GE: /* a >= 1 && a <= 1 | a >= 1 && a <= 2 | a >= 2 && a <= 1 */
|
|
/* none none conflict */
|
|
return (value1 > value2) ? RC_OVERLAP_CONFLICTING : RC_OVERLAP_NONE;
|
|
case RC_OPERATOR_NE: /* a != 1 && a <= 1 | a != 1 && a <= 2 | a != 2 && a <= 1 */
|
|
/* none none defer */
|
|
return (value1 > value2) ? RC_OVERLAP_DEFER : RC_OVERLAP_NONE;
|
|
case RC_OPERATOR_LT: /* a < 1 && a <= 1 | a < 1 && a <= 2 | a < 2 && a <= 1 */
|
|
/* redundant redundant defer */
|
|
return (value1 <= value2) ? RC_OVERLAP_REDUNDANT : RC_OVERLAP_DEFER;
|
|
case RC_OPERATOR_GT: /* a > 1 && a <= 1 | a > 1 && a <= 2 | a > 2 && a <= 1 */
|
|
/* conflict none conflict */
|
|
return (value1 >= value2) ? RC_OVERLAP_CONFLICTING : RC_OVERLAP_NONE;
|
|
}
|
|
break;
|
|
|
|
case RC_OPERATOR_GT:
|
|
switch (comparison1) /* comp1 comp2 comp1 comp2 comp1 comp2 */
|
|
{ /* value1 = value2 value1 < value2 value1 > value2 */
|
|
case RC_OPERATOR_EQ: /* a == 1 && a > 1 | a == 1 && a > 2 | a == 2 && a > 1 */
|
|
/* conflict conflict redundant */
|
|
return (value1 > value2) ? RC_OVERLAP_REDUNDANT : RC_OVERLAP_CONFLICTING;
|
|
case RC_OPERATOR_LE: /* a <= 1 && a > 1 | a <= 1 && a > 2 | a <= 2 && a > 1 */
|
|
/* conflict conflict defer */
|
|
return (value1 <= value2) ? RC_OVERLAP_CONFLICTING : RC_OVERLAP_DEFER;
|
|
case RC_OPERATOR_GE: /* a >= 1 && a > 1 | a >= 1 && a > 2 | a >= 2 && a > 1 */
|
|
/* defer defer redundant */
|
|
return (value1 > value2) ? RC_OVERLAP_REDUNDANT : RC_OVERLAP_DEFER;
|
|
case RC_OPERATOR_NE: /* a != 1 && a > 1 | a != 1 && a > 2 | a != 2 && a > 1 */
|
|
/* defer defer none */
|
|
return (value1 <= value2) ? RC_OVERLAP_DEFER : RC_OVERLAP_NONE;
|
|
case RC_OPERATOR_LT: /* a < 1 && a > 1 | a < 1 && a > 2 | a < 2 && a > 1 */
|
|
/* conflict conflict none */
|
|
return (value1 <= value2) ? RC_OVERLAP_CONFLICTING : RC_OVERLAP_NONE;
|
|
case RC_OPERATOR_GT: /* a > 1 && a > 1 | a > 1 && a > 2 | a > 2 && a > 1 */
|
|
/* redundant defer redundant */
|
|
return (value1 >= value2) ? RC_OVERLAP_REDUNDANT : RC_OVERLAP_DEFER;
|
|
}
|
|
break;
|
|
|
|
case RC_OPERATOR_GE:
|
|
switch (comparison1) /* comp1 comp2 comp1 comp2 comp1 comp2 */
|
|
{ /* value1 = value2 value1 < value2 value1 > value2 */
|
|
case RC_OPERATOR_EQ: /* a == 1 && a >= 1 | a == 1 && a >= 2 | a == 2 && a >= 1 */
|
|
/* redundant conflict redundant */
|
|
return (value1 >= value2) ? RC_OVERLAP_REDUNDANT : RC_OVERLAP_CONFLICTING;
|
|
case RC_OPERATOR_LE: /* a <= 1 && a >= 1 | a <= 1 && a >= 2 | a <= 2 && a >= 1 */
|
|
/* none conflict none */
|
|
return (value1 < value2) ? RC_OVERLAP_CONFLICTING : RC_OVERLAP_NONE;
|
|
case RC_OPERATOR_GE: /* a >= 1 && a >= 1 | a >= 1 && a >= 2 | a >= 2 && a >= 1 */
|
|
/* redundant redundant defer */
|
|
return (value1 <= value2) ? RC_OVERLAP_REDUNDANT : RC_OVERLAP_DEFER;
|
|
case RC_OPERATOR_NE: /* a != 1 && a >= 1 | a != 1 && a >= 2 | a != 2 && a >= 1 */
|
|
/* none defer none */
|
|
return (value1 < value2) ? RC_OVERLAP_DEFER : RC_OVERLAP_NONE;
|
|
case RC_OPERATOR_LT: /* a < 1 && a >= 1 | a < 1 && a >= 2 | a < 2 && a >= 1 */
|
|
/* conflict conflict none */
|
|
return (value1 <= value2) ? RC_OVERLAP_CONFLICTING : RC_OVERLAP_NONE;
|
|
case RC_OPERATOR_GT: /* a > 1 && a >= 1 | a > 1 && a >= 2 | a > 2 && a >= 1 */
|
|
/* redundant defer redundant */
|
|
return (value1 >= value2) ? RC_OVERLAP_REDUNDANT : RC_OVERLAP_DEFER;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return RC_OVERLAP_NONE;
|
|
}
|
|
|
|
static int rc_validate_conflicting_conditions(const rc_condset_t* conditions, const rc_condset_t* compare_conditions,
|
|
const char* prefix, const char* compare_prefix, char result[], const size_t result_size)
|
|
{
|
|
int comparison1, comparison2;
|
|
unsigned value1, value2;
|
|
const rc_operand_t* operand1;
|
|
const rc_operand_t* operand2;
|
|
const rc_condition_t* compare_condition;
|
|
const rc_condition_t* condition;
|
|
int overlap;
|
|
|
|
/* empty group */
|
|
if (conditions == NULL || compare_conditions == NULL)
|
|
return 1;
|
|
|
|
/* outer loop is the source conditions */
|
|
for (condition = conditions->conditions; condition != NULL;
|
|
condition = rc_validate_next_non_combining_condition(condition))
|
|
{
|
|
/* hits can be captured at any time, so any potential conflict will not be conflicting at another time */
|
|
if (condition->required_hits)
|
|
continue;
|
|
|
|
operand1 = rc_validate_get_comparison(condition, &comparison1, &value1);
|
|
if (!operand1)
|
|
continue;
|
|
|
|
switch (condition->type)
|
|
{
|
|
case RC_CONDITION_PAUSE_IF:
|
|
if (conditions != compare_conditions)
|
|
break;
|
|
/* fallthrough */
|
|
case RC_CONDITION_RESET_IF:
|
|
comparison1 = rc_validate_get_opposite_comparison(comparison1);
|
|
break;
|
|
default:
|
|
if (rc_validate_is_combining_condition(condition))
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
/* inner loop is the potentially conflicting conditions */
|
|
for (compare_condition = compare_conditions->conditions; compare_condition != NULL;
|
|
compare_condition = rc_validate_next_non_combining_condition(compare_condition))
|
|
{
|
|
if (compare_condition == condition)
|
|
continue;
|
|
|
|
if (compare_condition->required_hits)
|
|
continue;
|
|
|
|
operand2 = rc_validate_get_comparison(compare_condition, &comparison2, &value2);
|
|
if (!operand2 || operand2->value.memref->address != operand1->value.memref->address ||
|
|
operand2->size != operand1->size || operand2->type != operand1->type)
|
|
continue;
|
|
|
|
switch (compare_condition->type)
|
|
{
|
|
case RC_CONDITION_PAUSE_IF:
|
|
if (conditions != compare_conditions)
|
|
break;
|
|
/* fallthrough */
|
|
case RC_CONDITION_RESET_IF:
|
|
comparison2 = rc_validate_get_opposite_comparison(comparison2);
|
|
break;
|
|
default:
|
|
if (rc_validate_is_combining_condition(compare_condition))
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
overlap = rc_validate_comparison_overlap(comparison1, value1, comparison2, value2);
|
|
switch (overlap)
|
|
{
|
|
case RC_OVERLAP_CONFLICTING:
|
|
if (compare_condition->type == RC_CONDITION_PAUSE_IF || condition->type == RC_CONDITION_PAUSE_IF)
|
|
{
|
|
/* ignore PauseIf conflicts between groups, unless both conditions are PauseIfs */
|
|
if (conditions != compare_conditions && compare_condition->type != condition->type)
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case RC_OVERLAP_REDUNDANT:
|
|
if (compare_condition->type == RC_CONDITION_PAUSE_IF || condition->type == RC_CONDITION_PAUSE_IF)
|
|
{
|
|
/* ignore PauseIf redundancies between groups */
|
|
if (conditions != compare_conditions)
|
|
continue;
|
|
|
|
/* if the PauseIf is less restrictive than the other condition, it's just a guard. ignore it */
|
|
if (rc_validate_comparison_overlap(comparison2, value2, comparison1, value1) != RC_OVERLAP_REDUNDANT)
|
|
continue;
|
|
|
|
/* PauseIf redundant with ResetIf is a conflict (both are inverted comparisons) */
|
|
if (compare_condition->type == RC_CONDITION_RESET_IF || condition->type == RC_CONDITION_RESET_IF)
|
|
overlap = RC_OVERLAP_CONFLICTING;
|
|
}
|
|
else if (compare_condition->type == RC_CONDITION_RESET_IF && condition->type != RC_CONDITION_RESET_IF)
|
|
{
|
|
/* only ever report the redundancy on the non-ResetIf condition. The ResetIf is allowed to
|
|
* fire when the non-ResetIf condition is not true. */
|
|
continue;
|
|
}
|
|
else if (compare_condition->type == RC_CONDITION_MEASURED_IF || condition->type == RC_CONDITION_MEASURED_IF)
|
|
{
|
|
/* MeasuredIf is a meta tag and allowed to be redundant */
|
|
if (compare_condition->type != condition->type)
|
|
continue;
|
|
}
|
|
else if (compare_condition->type == RC_CONDITION_TRIGGER || condition->type == RC_CONDITION_TRIGGER)
|
|
{
|
|
/* Trigger is allowed to be redundant with non-trigger conditions as there may be limits that start a
|
|
* challenge that are furhter reduced for the completion of the challenge */
|
|
if (compare_condition->type != condition->type)
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
if (compare_prefix && *compare_prefix)
|
|
{
|
|
snprintf(result, result_size, "%s Condition %d: %s with %s Condition %d",
|
|
compare_prefix, rc_validate_get_condition_index(compare_conditions, compare_condition),
|
|
(overlap == RC_OVERLAP_REDUNDANT) ? "Redundant" : "Conflicts",
|
|
prefix, rc_validate_get_condition_index(conditions, condition));
|
|
}
|
|
else
|
|
{
|
|
snprintf(result, result_size, "Condition %d: %s with Condition %d",
|
|
rc_validate_get_condition_index(compare_conditions, compare_condition),
|
|
(overlap == RC_OVERLAP_REDUNDANT) ? "Redundant" : "Conflicts",
|
|
rc_validate_get_condition_index(conditions, condition));
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int rc_validate_trigger(const rc_trigger_t* trigger, char result[], const size_t result_size, unsigned max_address) {
|
|
const rc_condset_t* alt;
|
|
int index;
|
|
|
|
if (!trigger->alternative)
|
|
{
|
|
if (!rc_validate_condset(trigger->requirement, result, result_size, max_address))
|
|
return 0;
|
|
|
|
return rc_validate_conflicting_conditions(trigger->requirement, trigger->requirement, "", "", result, result_size);
|
|
}
|
|
|
|
snprintf(result, result_size, "Core ");
|
|
if (!rc_validate_condset(trigger->requirement, result + 5, result_size - 5, max_address))
|
|
return 0;
|
|
|
|
/* compare core to itself */
|
|
if (!rc_validate_conflicting_conditions(trigger->requirement, trigger->requirement, "Core", "Core", result, result_size))
|
|
return 0;
|
|
|
|
index = 1;
|
|
for (alt = trigger->alternative; alt; alt = alt->next, ++index) {
|
|
char altname[16];
|
|
const size_t prefix_length = snprintf(result, result_size, "Alt%d ", index);
|
|
if (!rc_validate_condset(alt, result + prefix_length, result_size - prefix_length, max_address))
|
|
return 0;
|
|
|
|
/* compare alt to itself */
|
|
snprintf(altname, sizeof(altname), "Alt%d", index);
|
|
if (!rc_validate_conflicting_conditions(alt, alt, altname, altname, result, result_size))
|
|
return 0;
|
|
|
|
/* compare alt to core */
|
|
if (!rc_validate_conflicting_conditions(trigger->requirement, alt, "Core", altname, result, result_size))
|
|
return 0;
|
|
|
|
/* compare core to alt */
|
|
if (!rc_validate_conflicting_conditions(alt, trigger->requirement, altname, "Core", result, result_size))
|
|
return 0;
|
|
}
|
|
|
|
*result = '\0';
|
|
return 1;
|
|
}
|
|
|