/* evaluates variables
Copyright 2002-2005 Craig Franklin
*/
#include "stdhdr.h"
#include "libgputils.h"
#include "gpasm.h"
#include "evaluate.h"
#include "directive.h"
#include "gpmsg.h"
#include "parse.h"
#include "coff.h"
/*------------------------------------------------------------------------------------------------*/
bool eval_enforce_arity(int Arity, int Must_be) {
if (Arity == Must_be) {
return true;
}
if (Arity < Must_be) {
gpmsg(GPE_MISSING_ARGU, NULL);
}
else {
gpmsg(GPE_TOO_MANY_ARGU, NULL);
}
return false;
}
/*------------------------------------------------------------------------------------------------*/
bool
eval_enforce_simple(const pnode_t *Pnode)
{
switch (Pnode->tag) {
case PTAG_SYMBOL:
return true;
break;
case PTAG_STRING:
gpmsg(GPE_ILLEGAL_ARGU, NULL, PnString(Pnode));
break;
default:
gpmsg0(GPE_ILLEGAL_ARGU, "Illegal argument.");
}
return false;
}
/*------------------------------------------------------------------------------------------------*/
int
eval_list_length(const pnode_t *List)
{
const pnode_t *p = List;
int n = 0;
while ((p) && PnIsList(p)) {
++n;
p = PnListTail(p);
}
return ((p) ? (n + 1) : n);
}
/*------------------------------------------------------------------------------------------------*/
bool
eval_can_evaluate(const pnode_t *Pnode)
{
char buf[BUFSIZ];
const symbol_t *sym;
const variable_t *var;
const char *name;
switch (Pnode->tag) {
case PTAG_CONSTANT:
return true;
break;
case PTAG_OFFSET: {
if (state.extended_pic16e == false) {
gpmsg(GPE_BADCHAR, NULL, '[');
}
return eval_can_evaluate(PnOffset(Pnode));
break;
}
case PTAG_SYMBOL: {
name = PnSymbol(Pnode);
/* '$' means current org, which we can always evaluate */
/*
clang-3.7.0, 3.8.0 bug.
if (strcmp(name, "$") == 0) {
*/
if ((name[0] == '$') && (name[1] == '\0')) {
return true;
}
/* Otherwise look it up. */
sym = gp_sym_get_symbol(state.stTop, name);
if (sym == NULL) {
var = NULL;
if (name[0] == '\0') {
gpmsg(GPE_MISSING_ARGU, NULL);
}
else {
gpmsg(GPE_SYM_NOT_DEFINED, NULL, name);
}
}
else {
var = (const variable_t *)gp_sym_get_symbol_annotation(sym);
if (var == NULL) {
snprintf(buf, sizeof(buf), "Symbol not assigned a value: \"%s\"", name);
gpmsg0(GPW_UNKNOWN, buf);
}
}
return ((sym) && (var));
break;
}
case PTAG_UNOP:
return eval_can_evaluate(PnUnOpP0(Pnode));
break;
case PTAG_BINOP:
return (eval_can_evaluate(PnBinOpP0(Pnode)) && eval_can_evaluate(PnBinOpP1(Pnode)));
break;
case PTAG_STRING:
gpmsg(GPE_ILLEGAL_ARGU, NULL, PnString(Pnode));
return false;
break;
default:
assert(0);
}
return false;
}
/*------------------------------------------------------------------------------------------------*/
bool
eval_can_evaluate_value(const pnode_t *Pnode)
{
const symbol_t *sym;
const variable_t *var;
const char *name;
switch (Pnode->tag) {
case PTAG_CONSTANT:
return true;
break;
case PTAG_OFFSET: {
if (state.extended_pic16e == false) {
gpmsg(GPE_BADCHAR, NULL, '[');
}
return eval_can_evaluate_value(PnOffset(Pnode));
break;
}
case PTAG_SYMBOL: {
name = PnSymbol(Pnode);
/* '$' means current org, which we can evaluate if section at absolute address. */
/*
clang-3.7.0, 3.8.0 bug.
if (strcmp(name, "$") == 0) {
*/
if ((name[0] == '$') && (name[1] == '\0')) {
return (FlagIsSet(state.obj.new_sect_flags, STYP_ABS) ? true : false);
}
/* Otherwise look it up. */
sym = gp_sym_get_symbol(state.stTop, name);
if (sym == NULL) {
return false;
}
var = (const variable*)gp_sym_get_symbol_annotation(sym);
if (var == NULL) {
return false;
}
if (FlagIsSet(var->flags, VATRR_HAS_NO_VALUE)) {
msg_has_no_value(NULL, name);
}
switch (var->type) {
case VAL_EXTERNAL:
case VAL_GLOBAL:
case VAL_STATIC:
case VAL_ABSOLUTE:
case VAL_DEBUG:
return false;
default:
return true;
}
break;
}
case PTAG_UNOP:
return eval_can_evaluate_value(PnUnOpP0(Pnode));
break;
case PTAG_BINOP:
return (eval_can_evaluate_value(PnBinOpP0(Pnode)) && eval_can_evaluate_value(PnBinOpP1(Pnode)));
break;
case PTAG_STRING:
gpmsg(GPE_ILLEGAL_ARGU, NULL, PnString(Pnode));
return false;
break;
default:
assert(0);
}
return false;
}
/*------------------------------------------------------------------------------------------------*/
static bool
_is_program_segment(const pnode_t *Pnode)
{
const symbol_t* sym;
const variable_t* var;
const char* str;
if (PnIsSymbol(Pnode)) {
str = PnSymbol(Pnode);
/*
clang-3.7.0, 3.8.0 bug.
if (strcmp(str, "$") != 0) {
*/
if ((str[0] != '$') || (str[1] != '\0')) {
sym = gp_sym_get_symbol(state.stTop, PnSymbol(Pnode));
assert(sym);
var = (const variable*)gp_sym_get_symbol_annotation(sym);
assert(var);
if (FlagIsSet(var->flags, VATRR_HAS_NO_VALUE)) {
msg_has_no_value(NULL, PnSymbol(Pnode));
}
return ((var->type == VAL_ADDRESS) ? true : false);
}
}
return false;
}
/*------------------------------------------------------------------------------------------------*/
gpasmVal
eval_evaluate(const pnode_t *Pnode)
{
const symbol_t *sym;
const variable_t *var;
const char *name;
gpasmVal p0;
gpasmVal p1;
gpasmVal val;
switch (Pnode->tag) {
case PTAG_CONSTANT:
return PnConstant(Pnode);
break;
case PTAG_OFFSET:
return eval_evaluate(PnOffset(Pnode));
break;
case PTAG_SYMBOL: {
name = PnSymbol(Pnode);
/*
clang-3.7.0, 3.8.0 bug.
if (strcmp(name, "$") == 0) {
*/
if ((name[0] == '$') && (name[1] == '\0')) {
return (IS_RAM_ORG ? state.byte_addr :
gp_processor_insn_from_byte_p(state.processor, state.byte_addr));
}
sym = gp_sym_get_symbol(state.stTop, name);
assert(sym);
var = (const variable*)gp_sym_get_symbol_annotation(sym);
assert(var);
if (FlagIsSet(var->flags, VATRR_HAS_NO_VALUE)) {
msg_has_no_value(NULL, name);
}
return var->value;
break;
}
case PTAG_UNOP: {
switch (PnUnOpOp(Pnode)) {
case '!':
return (!eval_evaluate(PnUnOpP0(Pnode)));
break;
case '+':
return eval_evaluate(PnUnOpP0(Pnode));
break;
case '-':
return (-eval_evaluate(PnUnOpP0(Pnode)));
break;
case '~':
return (~eval_evaluate(PnUnOpP0(Pnode)));
break;
case UPPER:
return ((eval_evaluate(PnUnOpP0(Pnode)) >> 16) & 0xff);
break;
case HIGH: {
val = (eval_evaluate(PnUnOpP0(Pnode)) >> 8) & 0xff;
/* Set 7th bit if in absolute mode and PROC_CLASS_PIC14E or PROC_CLASS_PIC14EX and
* address relative mode is handled by the linker. */
if ((state.mode == MODE_ABSOLUTE) && (IS_PIC14E_CORE || IS_PIC14EX_CORE) &&
_is_program_segment(PnUnOpP0(Pnode))) {
val |= PIC14E_FSRxH_FLASH_SEL;
}
return val;
break;
}
case LOW:
return (eval_evaluate(PnUnOpP0(Pnode)) & 0xff);
break;
case INCREMENT:
return (eval_evaluate(PnUnOpP0(Pnode)) + 1);
break;
case DECREMENT:
return (eval_evaluate(PnUnOpP0(Pnode)) - 1);
break;
default:
assert(0);
}
break;
}
case PTAG_BINOP: {
p0 = eval_evaluate(PnBinOpP0(Pnode));
p1 = eval_evaluate(PnBinOpP1(Pnode));
switch (PnBinOpOp(Pnode)) {
case '+':
return (p0 + p1);
break;
case '-':
return (p0 - p1);
break;
case '*':
return (p0 * p1);
break;
case '/': {
if (p1 == 0){
gpmsg(GPE_DIVBY0, NULL);
return 0;
}
else {
return (p0 / p1);
}
break;
}
case '%': {
if (p1 == 0){
gpmsg(GPE_DIVBY0, NULL);
return 0;
}
else {
return (p0 % p1);
}
break;
}
case '&':
return (p0 & p1);
break;
case '|':
return (p0 | p1);
break;
case '^':
return (p0 ^ p1);
break;
case LSH: {
if (state.mpasm_compatible) {
/* MPASM compatible:
* It seems that x << n is actually x << (n % (sizeof(int) * 8))
* on x86 architectures, so 0x1234 << 32 results 0x1234
* which is wrong but compatible with MPASM. */
return (p0 << p1);
}
else {
/* x << n results sign extension for n >= (sizeof(int) * 8) */
return ((p1 >= (sizeof(int) * 8)) ? ((p0 < 0) ? -1 : 0) : (p0 << p1));
}
break;
}
case RSH:
if (state.mpasm_compatible) {
/* MPASM compatible: see https://sourceforge.net/p/gputils/bugs/252/
* It seems that x >> n is actually x >> (n % (sizeof(int) * 8))
* on x86 architectures, so 0x1234 >> 32 results 0x1234
* which is wrong but compatible with MPASM. */
return (p0 >> p1);
}
else {
/* x >> n results sign extension for n >= (sizeof(int) * 8) */
return ((p1 >= (sizeof(int) * 8)) ? ((p0 < 0) ? -1 : 0) : (p0 >> p1));
}
break;
case EQUAL:
return (p0 == p1);
break;
case '<':
return (p0 < p1);
break;
case '>':
return (p0 > p1);
break;
case NOT_EQUAL:
return (p0 != p1);
break;
case GREATER_EQUAL:
return (p0 >= p1);
break;
case LESS_EQUAL:
return (p0 <= p1);
break;
case LOGICAL_AND:
return (p0 && p1);
break;
case LOGICAL_OR:
return (p0 || p1);
break;
case '=':
gpmsg(GPE_BADCHAR, NULL, '=');
return 0;
break;
default:
assert(0); /* Unhandled binary operator. */
}
break;
}
default:
assert(0); /* Unhandled parse node tag. */
}
return 0; /* Should never reach here. */
}
/*------------------------------------------------------------------------------------------------*/
/* Attempt to evaluate expression 'p'. Return its value if successful,
* otherwise generate an error message and return 0. */
gpasmVal
eval_maybe_evaluate(const pnode_t *Pnode)
{
if ((Pnode) && eval_can_evaluate(Pnode)) {
return eval_evaluate(Pnode);
}
return 0;
}
/*------------------------------------------------------------------------------------------------*/
/* Count the number of relocatable addesses in the expression. */
int
eval_count_reloc(const pnode_t *Pnode)
{
const symbol_t *sym;
const variable_t *var;
const char *name;
if (state.mode == MODE_ABSOLUTE) {
return 0;
}
switch (Pnode->tag) {
case PTAG_CONSTANT:
return 0;
break;
case PTAG_OFFSET:
return eval_count_reloc(PnOffset(Pnode));
break;
case PTAG_SYMBOL: {
name = PnSymbol(Pnode);
/*
clang-3.7.0, 3.8.0 bug.
if (strcmp(name, "$") == 0) {
*/
if ((name[0] == '$') && (name[1] == '\0')) {
return 1;
}
sym = gp_sym_get_symbol(state.stTop, name);
if (sym) {
var = (const variable*)gp_sym_get_symbol_annotation(sym);
if (var) {
if (FlagIsSet(var->flags, VATRR_HAS_NO_VALUE)) {
msg_has_no_value(NULL, name);
}
switch (var->type) {
case VAL_EXTERNAL:
case VAL_GLOBAL:
case VAL_STATIC:
case VAL_ADDRESS:
return 1;
default:
return 0;
}
}
}
return 0;
break;
}
case PTAG_UNOP:
return eval_count_reloc(PnUnOpP0(Pnode));
break;
case PTAG_BINOP:
return (eval_count_reloc(PnBinOpP0(Pnode)) + eval_count_reloc(PnBinOpP1(Pnode)));
break;
default:
assert(0);
break;
}
return 0;
}
/*------------------------------------------------------------------------------------------------*/
/* When generating object files, operands with relocatable addresses can only be
[UPPER|HIGH|LOW]([<relocatable symbol>] + [<offs>]) */
static gpasmVal _add_reloc(const pnode_t *Pnode, int Offset, uint16_t Type, bool Add_coff) {
const symbol_t *sym;
const variable_t *var;
const char *name;
char buffer[BUFSIZ];
int digits;
unsigned org;
enum gpasmValTypes type;
switch (Pnode->tag) {
case PTAG_OFFSET:
return _add_reloc(PnOffset(Pnode), Offset, Type, Add_coff);
break;
case PTAG_SYMBOL: {
name = PnSymbol(Pnode);
/*
clang-3.7.0, 3.8.0 bug.
if (strcmp(name, "$") == 0) {
*/
if ((name[0] == '$') && (name[1] == '\0')) {
if (IS_RAM_ORG) {
digits = state.device.pclass->word_digits;
org = state.byte_addr;
type = VAL_STATIC;
}
else {
digits = state.device.pclass->addr_digits;
org = gp_processor_insn_from_byte_p(state.processor, state.byte_addr);
type = VAL_ADDRESS;
}
snprintf(buffer, sizeof(buffer), "_%s_%0*X", state.obj.new_sect_name, digits, org);
/* RELOC_ACCESS has always also RELOC_F, which has already created this symbol. */
if (Type != RELOC_ACCESS) {
set_global(buffer, org, type, false, false);
}
sym = gp_sym_get_symbol(state.stTop, buffer);
}
else {
sym = gp_sym_get_symbol(state.stTop, name);
}
if (sym) {
var = (const variable*)gp_sym_get_symbol_annotation(sym);
if (var) {
if (FlagIsSet(var->flags, VATRR_HAS_NO_VALUE)) {
msg_has_no_value(NULL, name);
}
switch (var->type) {
case VAL_EXTERNAL:
case VAL_GLOBAL:
case VAL_STATIC:
case VAL_ADDRESS: {
if (Add_coff) {
coff_add_reloc(var->coff_symbol_num, Offset, Type);
}
return (FlagIsSet(var->coff_section_flags, STYP_ABS) ? var->value : -1);
break;
}
default:
return -1;
}
}
}
return -1;
break;
}
case PTAG_UNOP: {
switch (PnUnOpOp(Pnode)) {
case UPPER:
return _add_reloc(PnUnOpP0(Pnode), Offset, RELOC_UPPER, Add_coff);
case HIGH:
return _add_reloc(PnUnOpP0(Pnode), Offset, RELOC_HIGH, Add_coff);
case LOW:
return _add_reloc(PnUnOpP0(Pnode), Offset, RELOC_LOW, Add_coff);
case '!':
case '+':
case '-':
case '~':
case INCREMENT:
case DECREMENT:
gpmsg(GPE_UNRESOLVABLE, NULL);
return -1;
default:
assert(0);
}
break;
}
case PTAG_BINOP: {
switch (PnBinOpOp(Pnode)) {
case '+': {
/* The symbol can be in either position. */
if (eval_count_reloc(PnBinOpP0(Pnode)) == 1) {
return _add_reloc(PnBinOpP0(Pnode), Offset + eval_maybe_evaluate(PnBinOpP1(Pnode)), Type, Add_coff);
}
else {
return _add_reloc(PnBinOpP1(Pnode), eval_maybe_evaluate(PnBinOpP0(Pnode)) + Offset, Type, Add_coff);
}
break;
}
case '-': {
/* The symbol has to be first. */
if (eval_count_reloc(PnBinOpP0(Pnode)) == 1) {
return _add_reloc(PnBinOpP0(Pnode), Offset - eval_maybe_evaluate(PnBinOpP1(Pnode)), Type, Add_coff);
}
else {
gpmsg(GPE_UNRESOLVABLE, NULL);
return -1;
}
break;
}
case '*':
case '/':
case '%':
case '&':
case '|':
case '^':
case LSH:
case RSH:
case EQUAL:
case '<':
case '>':
case NOT_EQUAL:
case GREATER_EQUAL:
case LESS_EQUAL:
case LOGICAL_AND:
case LOGICAL_OR:
case '=':
gpmsg(GPE_UNRESOLVABLE, NULL);
return -1;
break;
default:
assert(0); /* Unhandled binary operator. */
}
return -1;
break;
}
case PTAG_CONSTANT:
default:
assert(0);
break;
}
return -1;
}
/*------------------------------------------------------------------------------------------------*/
/* Determine if the expression is the difference between two symbols in
the same section. If so, calculate the offset and don't generate a relocation.
[UPPER|HIGH|LOW]([<relocatable symbol>] - [<relocatable symbol>])
*/
static bool
_same_section(const pnode_t *Pnode)
{
const pnode_t *p0;
const pnode_t *p1;
const symbol_t *sym0;
const symbol_t *sym1;
const variable_t *var0;
const variable_t *var1;
const char *name0;
const char *name1;
unsigned section_num0;
unsigned section_num1;
if (!state.obj.enabled) {
return false;
}
if (PnIsUnOp(Pnode) &&
((PnUnOpOp(Pnode) == UPPER) || (PnUnOpOp(Pnode) == HIGH) || (PnUnOpOp(Pnode) == LOW))) {
Pnode = PnUnOpP0(Pnode);
}
if (!(PnIsBinOp(Pnode)) || (PnBinOpOp(Pnode) != '-') || (eval_count_reloc(PnBinOpP0(Pnode)) != 1)) {
return false;
}
p0 = PnBinOpP0(Pnode);
p1 = PnBinOpP1(Pnode);
if (!(PnIsSymbol(p0)) || !(PnIsSymbol(p1))) {
return false;
}
name0 = PnSymbol(p0);
name1 = PnSymbol(p1);
/*
clang-3.7.0, 3.8.0 bug.
if (strcmp(name0, "$") == 0) {
*/
if ((name0[0] == '$') && (name0[1] == '\0')) {
section_num0 = state.obj.section_num;
}
else {
sym0 = gp_sym_get_symbol(state.stTop, name0);
assert(sym0);
var0 = (const variable*)gp_sym_get_symbol_annotation(sym0);
section_num0 = var0->coff_section_num;
}
/*
clang-3.7.0, 3.8.0 bug.
if (strcmp(name1, "$") == 0) {
*/
if ((name1[0] == '$') && (name1[1] == '\0')) {
section_num1 = state.obj.section_num;
}
else {
sym1 = gp_sym_get_symbol(state.stTop, name1);
assert(sym1);
var1 = (const variable*)gp_sym_get_symbol_annotation(sym1);
section_num1 = var1->coff_section_num;
}
/* They must come from the same section. Debug symbols are not placed
in the global symbol table, so don't worry about symbol type.
Fail if sections are not known (== 0) or not the same. */
if ((section_num0 == 0) || (section_num1 == 0) || (section_num0 != section_num1)) {
return false;
}
return true;
}
/*------------------------------------------------------------------------------------------------*/
gpasmVal
eval_reloc_evaluate(const pnode_t *Pnode, uint16_t Type, bool *Is_reloc, gpasmVal *Reloc_value,
bool Add_coff)
{
int count;
gpasmVal value;
if (Is_reloc) {
*Is_reloc = false;
}
if (Reloc_value) {
*Reloc_value = -1;
}
if (state.mode == MODE_ABSOLUTE) {
return eval_maybe_evaluate(Pnode);
}
count = eval_count_reloc(Pnode);
if (count == 0) {
/* no relocatable addresses */
return eval_maybe_evaluate(Pnode);
}
if (count > 1) {
if ((count == 2) && (_same_section(Pnode))) {
/* It is valid to take the difference between two symbols in the same section.
Evaluate, but don't add a relocation. */
return eval_maybe_evaluate(Pnode);
}
/* too many relocatable addresses */
gpmsg(GPE_UNRESOLVABLE, NULL);
return 0;
}
/* add the coff relocation */
value = _add_reloc(Pnode, 0, Type, Add_coff);
if (Is_reloc) {
*Is_reloc = true;
}
if (Reloc_value) {
*Reloc_value = value;
}
return 0;
}
/*------------------------------------------------------------------------------------------------*/
/* Evaluate the number of passes for the "fill" directive. */
int
eval_fill_number(const pnode_t *Pnode)
{
int number;
number = eval_maybe_evaluate(Pnode);
if ((state.device.pclass->rom_width == 8) && ((number & 0x1) == 1)) {
gpmsg(GPE_FILL_ODD, NULL);
}
return (gp_processor_byte_from_insn_c(state.device.pclass, number) >> 1);
}
Detected encoding: ASCII (7 bit) | 2
|