/* * Integer number functions. * * Copyright (C) 2001 Peter Johnson * * This file is part of YASM. * * YASM is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * YASM is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "util.h" /*@unused@*/ RCSID("$IdPath$"); #include #include "bitvect.h" #include "file.h" #include "errwarn.h" #include "intnum.h" #define BITVECT_ALLOC_SIZE 80 struct intnum { union val { unsigned long ul; /* integer value (for integers <=32 bits) */ intptr bv; /* bit vector (for integers >32 bits) */ } val; enum { INTNUM_UL, INTNUM_BV } type; unsigned char origsize; /* original (parsed) size, in bits */ }; /* static bitvect used for conversions */ static /*@only@*/ /*@null@*/ wordptr conv_bv = NULL; void intnum_shutdown(void) { if (conv_bv) { BitVector_Destroy(conv_bv); conv_bv = NULL; } BitVector_from_Dec_static_Shutdown(); } intnum * intnum_new_dec(char *str) { intnum *intn = xmalloc(sizeof(intnum)); intn->origsize = 0; /* no reliable way to figure this out */ if (!conv_bv) { conv_bv = BitVector_Create(BITVECT_ALLOC_SIZE, FALSE); BitVector_from_Dec_static_Boot(BITVECT_ALLOC_SIZE); } if (BitVector_from_Dec_static(conv_bv, (unsigned char *)str) == ErrCode_Ovfl) Warning(_("Numeric constant too large for internal format")); if (Set_Max(conv_bv) < 32) { intn->type = INTNUM_UL; intn->val.ul = BitVector_Chunk_Read(conv_bv, 32, 0); } else { intn->type = INTNUM_BV; intn->val.bv = BitVector_Clone(conv_bv); } return intn; } intnum * intnum_new_bin(char *str) { intnum *intn = xmalloc(sizeof(intnum)); intn->origsize = (unsigned char)strlen(str); if(intn->origsize > BITVECT_ALLOC_SIZE) Warning(_("Numeric constant too large for internal format")); if (!conv_bv) { conv_bv = BitVector_Create(BITVECT_ALLOC_SIZE, FALSE); BitVector_from_Dec_static_Boot(BITVECT_ALLOC_SIZE); } BitVector_from_Bin(conv_bv, (unsigned char *)str); if (Set_Max(conv_bv) < 32) { intn->type = INTNUM_UL; intn->val.ul = BitVector_Chunk_Read(conv_bv, 32, 0); } else { intn->type = INTNUM_BV; intn->val.bv = BitVector_Clone(conv_bv); } return intn; } intnum * intnum_new_oct(char *str) { intnum *intn = xmalloc(sizeof(intnum)); intn->origsize = strlen(str)*3; if(intn->origsize > BITVECT_ALLOC_SIZE) Warning(_("Numeric constant too large for internal format")); if (!conv_bv) { conv_bv = BitVector_Create(BITVECT_ALLOC_SIZE, FALSE); BitVector_from_Dec_static_Boot(BITVECT_ALLOC_SIZE); } BitVector_from_Oct(conv_bv, (unsigned char *)str); if (Set_Max(conv_bv) < 32) { intn->type = INTNUM_UL; intn->val.ul = BitVector_Chunk_Read(conv_bv, 32, 0); } else { intn->type = INTNUM_BV; intn->val.bv = BitVector_Clone(conv_bv); } return intn; } intnum * intnum_new_hex(char *str) { intnum *intn = xmalloc(sizeof(intnum)); intn->origsize = strlen(str)*4; if(intn->origsize > BITVECT_ALLOC_SIZE) Warning(_("Numeric constant too large for internal format")); if (!conv_bv) { conv_bv = BitVector_Create(BITVECT_ALLOC_SIZE, FALSE); BitVector_from_Dec_static_Boot(BITVECT_ALLOC_SIZE); } BitVector_from_Hex(conv_bv, (unsigned char *)str); if (Set_Max(conv_bv) < 32) { intn->type = INTNUM_UL; intn->val.ul = BitVector_Chunk_Read(conv_bv, 32, 0); } else { intn->type = INTNUM_BV; intn->val.bv = BitVector_Clone(conv_bv); } return intn; } /*@-usedef -compdef -uniondef@*/ intnum * intnum_new_charconst_nasm(const char *str) { intnum *intn = xmalloc(sizeof(intnum)); size_t len = strlen(str); if (len > 4) Warning(_("character constant too large, ignoring trailing characters")); intn->val.ul = 0; intn->type = INTNUM_UL; intn->origsize = len*8; switch (len) { case 4: intn->val.ul |= (unsigned long)str[3]; intn->val.ul <<= 8; /*@fallthrough@*/ case 3: intn->val.ul |= (unsigned long)str[2]; intn->val.ul <<= 8; /*@fallthrough@*/ case 2: intn->val.ul |= (unsigned long)str[1]; intn->val.ul <<= 8; /*@fallthrough@*/ case 1: intn->val.ul |= (unsigned long)str[0]; } return intn; } /*@=usedef =compdef =uniondef@*/ intnum * intnum_new_uint(unsigned long i) { intnum *intn = xmalloc(sizeof(intnum)); intn->val.ul = i; intn->type = INTNUM_UL; intn->origsize = 0; return intn; } intnum * intnum_new_int(long i) { /* FIXME: Better way of handling signed numbers? */ return intnum_new_uint((unsigned long)i); } intnum * intnum_copy(const intnum *intn) { intnum *n = xmalloc(sizeof(intnum)); switch (intn->type) { case INTNUM_UL: n->val.ul = intn->val.ul; break; case INTNUM_BV: n->val.bv = BitVector_Clone(intn->val.bv); break; } n->type = intn->type; n->origsize = intn->origsize; return n; } void intnum_delete(intnum *intn) { if (intn->type == INTNUM_BV) BitVector_Destroy(intn->val.bv); xfree(intn); } /*@-nullderef -nullpass -branchstate@*/ void intnum_calc(intnum *acc, ExprOp op, intnum *operand) { wordptr result = (wordptr)NULL, op1 = (wordptr)NULL, op2 = (wordptr)NULL; wordptr spare = (wordptr)NULL; boolean carry; /* upsize to bitvector op if one of two parameters is bitvector already. * BitVector results must be calculated through intermediate storage. */ if (acc->type == INTNUM_BV || (operand && operand->type == INTNUM_BV)) { result = BitVector_Create(BITVECT_ALLOC_SIZE, FALSE); spare = BitVector_Create(BITVECT_ALLOC_SIZE, FALSE); if (acc->type == INTNUM_BV) op1 = acc->val.bv; else { op1 = BitVector_Create(BITVECT_ALLOC_SIZE, TRUE); BitVector_Chunk_Store(op1, 32, 0, acc->val.ul); } if (operand) { if (operand->type == INTNUM_BV) op2 = acc->val.bv; else { op2 = BitVector_Create(BITVECT_ALLOC_SIZE, TRUE); BitVector_Chunk_Store(op2, 32, 0, operand->val.ul); } } } if (!operand && op != EXPR_NEG && op != EXPR_NOT && op != EXPR_LNOT) InternalError(_("Operation needs an operand")); /* A operation does a bitvector computation if result is allocated. */ switch (op) { case EXPR_ADD: if (result) BitVector_add(result, op1, op2, &carry); else acc->val.ul = acc->val.ul + operand->val.ul; break; case EXPR_SUB: if (result) BitVector_sub(result, op1, op2, &carry); else acc->val.ul = acc->val.ul - operand->val.ul; break; case EXPR_MUL: if (result) /* TODO: Make sure result size = op1+op2 */ BitVector_Multiply(result, op1, op2); else acc->val.ul = acc->val.ul * operand->val.ul; break; case EXPR_DIV: if (result) { /* TODO: make sure op1 and op2 are unsigned */ BitVector_Divide(result, op1, op2, spare); } else acc->val.ul = acc->val.ul / operand->val.ul; break; case EXPR_SIGNDIV: if (result) BitVector_Divide(result, op1, op2, spare); else acc->val.ul = (unsigned long)((signed long)acc->val.ul / (signed long)operand->val.ul); break; case EXPR_MOD: if (result) { /* TODO: make sure op1 and op2 are unsigned */ BitVector_Divide(spare, op1, op2, result); } else acc->val.ul = acc->val.ul % operand->val.ul; break; case EXPR_SIGNMOD: if (result) BitVector_Divide(spare, op1, op2, result); else acc->val.ul = (unsigned long)((signed long)acc->val.ul % (signed long)operand->val.ul); break; case EXPR_NEG: if (result) BitVector_Negate(result, op1); else acc->val.ul = -(acc->val.ul); break; case EXPR_NOT: if (result) Set_Complement(result, op1); else acc->val.ul = ~(acc->val.ul); break; case EXPR_OR: if (result) Set_Union(result, op1, op2); else acc->val.ul = acc->val.ul | operand->val.ul; break; case EXPR_AND: if (result) Set_Intersection(result, op1, op2); else acc->val.ul = acc->val.ul & operand->val.ul; break; case EXPR_XOR: if (result) Set_ExclusiveOr(result, op1, op2); else acc->val.ul = acc->val.ul ^ operand->val.ul; break; case EXPR_SHL: if (result) { if (operand->type == INTNUM_UL) { BitVector_Copy(result, op1); BitVector_Move_Left(result, (N_int)operand->val.ul); } else /* don't even bother, just zero result */ BitVector_Empty(result); } else acc->val.ul = acc->val.ul << operand->val.ul; break; case EXPR_SHR: if (result) { if (operand->type == INTNUM_UL) { BitVector_Copy(result, op1); BitVector_Move_Right(result, (N_int)operand->val.ul); } else /* don't even bother, just zero result */ BitVector_Empty(result); } else acc->val.ul = acc->val.ul >> operand->val.ul; break; case EXPR_LOR: if (result) { BitVector_Empty(result); BitVector_LSB(result, !BitVector_is_empty(op1) || !BitVector_is_empty(op2)); } else acc->val.ul = acc->val.ul || operand->val.ul; break; case EXPR_LAND: if (result) { BitVector_Empty(result); BitVector_LSB(result, !BitVector_is_empty(op1) && !BitVector_is_empty(op2)); } else acc->val.ul = acc->val.ul && operand->val.ul; break; case EXPR_LNOT: if (result) { BitVector_Empty(result); BitVector_LSB(result, BitVector_is_empty(op1)); } else acc->val.ul = !acc->val.ul; break; case EXPR_EQ: if (result) { BitVector_Empty(result); BitVector_LSB(result, BitVector_equal(op1, op2)); } else acc->val.ul = acc->val.ul == operand->val.ul; break; case EXPR_LT: if (result) { BitVector_Empty(result); BitVector_LSB(result, BitVector_Lexicompare(op1, op2) < 0); } else acc->val.ul = acc->val.ul < operand->val.ul; break; case EXPR_GT: if (result) { BitVector_Empty(result); BitVector_LSB(result, BitVector_Lexicompare(op1, op2) > 0); } else acc->val.ul = acc->val.ul > operand->val.ul; break; case EXPR_LE: if (result) { BitVector_Empty(result); BitVector_LSB(result, BitVector_Lexicompare(op1, op2) <= 0); } else acc->val.ul = acc->val.ul <= operand->val.ul; break; case EXPR_GE: if (result) { BitVector_Empty(result); BitVector_LSB(result, BitVector_Lexicompare(op1, op2) >= 0); } else acc->val.ul = acc->val.ul >= operand->val.ul; break; case EXPR_NE: if (result) { BitVector_Empty(result); BitVector_LSB(result, !BitVector_equal(op1, op2)); } else acc->val.ul = acc->val.ul != operand->val.ul; break; case EXPR_IDENT: if (result) BitVector_Copy(result, op1); break; default: InternalError(_("invalid operation in intnum calculation")); } /* If we were doing a bitvector computation... */ if (result) { BitVector_Destroy(spare); if (op1 && acc->type != INTNUM_BV) BitVector_Destroy(op1); if (op2 && operand && operand->type != INTNUM_BV) BitVector_Destroy(op2); /* Try to fit the result into 32 bits if possible */ if (Set_Max(result) < 32) { if (acc->type == INTNUM_BV) { BitVector_Destroy(acc->val.bv); acc->type = INTNUM_UL; } acc->val.ul = BitVector_Chunk_Read(result, 32, 0); BitVector_Destroy(result); } else { if (acc->type == INTNUM_BV) { BitVector_Copy(acc->val.bv, result); BitVector_Destroy(result); } else { acc->type = INTNUM_BV; acc->val.bv = result; } } } } /*@=nullderef =nullpass =branchstate@*/ int intnum_is_zero(intnum *intn) { return ((intn->type == INTNUM_UL && intn->val.ul == 0) || (intn->type == INTNUM_BV && BitVector_is_empty(intn->val.bv))); } int intnum_is_pos1(intnum *intn) { return ((intn->type == INTNUM_UL && intn->val.ul == 1) || (intn->type == INTNUM_BV && Set_Max(intn->val.bv) == 0)); } int intnum_is_neg1(intnum *intn) { return ((intn->type == INTNUM_UL && (long)intn->val.ul == -1) || (intn->type == INTNUM_BV && BitVector_is_full(intn->val.bv))); } unsigned long intnum_get_uint(const intnum *intn) { switch (intn->type) { case INTNUM_UL: return intn->val.ul; case INTNUM_BV: return BitVector_Chunk_Read(intn->val.bv, 32, 0); default: InternalError(_("unknown intnum type")); /*@notreached@*/ return 0; } } long intnum_get_int(const intnum *intn) { switch (intn->type) { case INTNUM_UL: return (long)intn->val.ul; case INTNUM_BV: if (BitVector_msb(intn->val.bv)) { /* it's negative: negate the bitvector to get a positive * number, then negate the positive number. */ intptr abs_bv = BitVector_Create(BITVECT_ALLOC_SIZE, FALSE); long retval; BitVector_Negate(abs_bv, intn->val.bv); retval = -((long)BitVector_Chunk_Read(abs_bv, 32, 0)); BitVector_Destroy(abs_bv); return retval; } else return (long)BitVector_Chunk_Read(intn->val.bv, 32, 0); default: InternalError(_("unknown intnum type")); /*@notreached@*/ return 0; } } void intnum_get_sized(const intnum *intn, unsigned char *ptr, size_t size) { unsigned long ul; unsigned char *buf; unsigned int len; switch (intn->type) { case INTNUM_UL: ul = intn->val.ul; while (size-- > 0) { WRITE_8(ptr, ul); if (ul != 0) ul >>= 8; } break; case INTNUM_BV: buf = BitVector_Block_Read(intn->val.bv, &len); if (len < (unsigned int)size) InternalError(_("Invalid size specified (too large)")); memcpy(ptr, buf, size); xfree(buf); break; } } /* Return 1 if okay size, 0 if not */ int intnum_check_size(const intnum *intn, size_t size, int is_signed) { if (is_signed) { long absl; switch (intn->type) { case INTNUM_UL: if (size >= 4) return 1; /* absl = absolute value of (long)intn->val.ul */ absl = (long)intn->val.ul; if (absl < 0) absl = -absl; switch (size) { case 3: return ((absl & 0x00FFFFFF) == absl); case 2: return ((absl & 0x0000FFFF) == absl); case 1: return ((absl & 0x000000FF) == absl); } break; case INTNUM_BV: if (size >= 10) return 1; if (BitVector_msb(intn->val.bv)) { /* it's negative */ intptr abs_bv = BitVector_Create(BITVECT_ALLOC_SIZE, FALSE); int retval; BitVector_Negate(abs_bv, intn->val.bv); retval = Set_Max(abs_bv) < size*8; BitVector_Destroy(abs_bv); return retval; } else return (Set_Max(intn->val.bv) < size*8); } } else { switch (intn->type) { case INTNUM_UL: if (size >= 4) return 1; switch (size) { case 3: return ((intn->val.ul & 0x00FFFFFF) == intn->val.ul); case 2: return ((intn->val.ul & 0x0000FFFF) == intn->val.ul); case 1: return ((intn->val.ul & 0x000000FF) == intn->val.ul); } break; case INTNUM_BV: if (size >= 10) return 1; else return (Set_Max(intn->val.bv) < size*8); } } return 0; } void intnum_print(FILE *f, const intnum *intn) { unsigned char *s; switch (intn->type) { case INTNUM_UL: fprintf(f, "0x%lx/%u", intn->val.ul, (unsigned int)intn->origsize); break; case INTNUM_BV: s = BitVector_to_Hex(intn->val.bv); fprintf(f, "0x%s/%u", (char *)s, (unsigned int)intn->origsize); xfree(s); break; } }