| /* -*- mode: C; c-basic-offset: 3; -*- */ |
| |
| /* |
| This file is part of MemCheck, a heavyweight Valgrind tool for |
| detecting memory errors. |
| |
| Copyright (C) 2012-2015 Florian Krohm |
| |
| This program 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. |
| |
| This program 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. |
| |
| The GNU General Public License is contained in the file COPYING. |
| */ |
| |
| #include <stdio.h> // fprintf |
| #include <stdlib.h> // exit |
| #include <assert.h> // assert |
| #if defined(__APPLE__) |
| #include <machine/endian.h> |
| #define __BYTE_ORDER BYTE_ORDER |
| #define __LITTLE_ENDIAN LITTLE_ENDIAN |
| #elif defined(__sun) |
| #define __LITTLE_ENDIAN 1234 |
| #define __BIG_ENDIAN 4321 |
| # if defined(_LITTLE_ENDIAN) |
| # define __BYTE_ORDER __LITTLE_ENDIAN |
| # else |
| # define __BYTE_ORDER __BIG_ENDIAN |
| # endif |
| #else |
| #include <endian.h> |
| #endif |
| #include <inttypes.h> |
| #include "vtest.h" |
| |
| |
| /* Something bad happened. Cannot continue. */ |
| void __attribute__((noreturn)) |
| panic(const char *string) |
| { |
| fprintf(stderr, "*** OOPS: %s\n", string); |
| exit(1); |
| } |
| |
| |
| /* Issue a complaint because the V-bits of the result of an operation |
| differ from what was expected. */ |
| void |
| complain(const irop_t *op, const test_data_t *data, vbits_t expected) |
| { |
| fprintf(stderr, "*** Incorrect result for operator %s\n", op->name); |
| |
| int num_operands = get_num_operands(op->op); |
| |
| for (unsigned i = 0; i < num_operands; ++i) { |
| fprintf(stderr, " opnd %u: ", i); |
| print_opnd(stderr, &data->opnds[i]); |
| fprintf(stderr, "\n"); |
| } |
| fprintf(stderr, " result: "); |
| print_opnd(stderr, &data->result); |
| fprintf(stderr, "\n"); |
| fprintf(stderr, " expect: vbits = "); |
| print_vbits(stderr, expected); |
| fprintf(stderr, "\n"); |
| } |
| |
| |
| static void |
| print_value(FILE *fp, value_t val, unsigned num_bits) |
| { |
| switch (num_bits) { |
| case 1: fprintf(fp, "%02x", val.u8); break; |
| case 8: fprintf(fp, "%02x", val.u8); break; |
| case 16: fprintf(fp, "%04x", val.u16); break; |
| case 32: fprintf(fp, "%08x", val.u32); break; |
| case 64: fprintf(fp, "%016"PRIx64, val.u64); break; |
| case 128: |
| if (__BYTE_ORDER == __LITTLE_ENDIAN) { |
| fprintf(fp, "%016"PRIx64, val.u128[1]); |
| fprintf(fp, "%016"PRIx64, val.u128[0]); |
| } else { |
| fprintf(fp, "%016"PRIx64, val.u128[0]); |
| fprintf(fp, "%016"PRIx64, val.u128[1]); |
| } |
| break; |
| case 256: |
| if (__BYTE_ORDER == __LITTLE_ENDIAN) { |
| fprintf(fp, "%016"PRIx64, val.u256[3]); |
| fprintf(fp, "%016"PRIx64, val.u256[2]); |
| fprintf(fp, "%016"PRIx64, val.u256[1]); |
| fprintf(fp, "%016"PRIx64, val.u256[0]); |
| } else { |
| fprintf(fp, "%016"PRIx64, val.u256[0]); |
| fprintf(fp, "%016"PRIx64, val.u256[1]); |
| fprintf(fp, "%016"PRIx64, val.u256[2]); |
| fprintf(fp, "%016"PRIx64, val.u256[3]); |
| } |
| break; |
| default: |
| panic(__func__); |
| } |
| } |
| |
| |
| void |
| print_opnd(FILE *fp, const opnd_t *opnd) |
| { |
| fprintf(fp, "vbits = "); |
| print_vbits(fp, opnd->vbits); |
| /* Write the value only if it is defined. Otherwise, there will be error |
| messages about it being undefined */ |
| if (equal_vbits(opnd->vbits, defined_vbits(opnd->vbits.num_bits))) { |
| fprintf(fp, " value = "); |
| print_value(fp, opnd->value, opnd->vbits.num_bits); |
| } |
| } |
| |
| |
| static int |
| is_floating_point_type(IRType type) |
| { |
| switch (type) { |
| case Ity_F32: |
| case Ity_F64: |
| case Ity_F128: |
| case Ity_D32: |
| case Ity_D64: |
| case Ity_D128: |
| return 1; |
| |
| default: |
| return 0; |
| } |
| } |
| |
| |
| int |
| is_floating_point_op_with_rounding_mode(IROp op) |
| { |
| IRType t_dst, t_arg1, t_arg2, t_arg3, t_arg4; |
| |
| typeof_primop(op, &t_dst, &t_arg1, &t_arg2, &t_arg3, &t_arg4); |
| |
| // A unary operator cannot have a rounding mode |
| if (t_arg2 == Ity_INVALID) return 0; |
| |
| if (is_floating_point_type(t_dst) || |
| is_floating_point_type(t_arg1) || |
| is_floating_point_type(t_arg2) || |
| is_floating_point_type(t_arg3) || |
| is_floating_point_type(t_arg4)) { |
| // Rounding mode, if present, is the 1st operand |
| return t_arg1 == Ity_I32; |
| } |
| return 0; |
| } |
| |
| |
| /* Return the number of operands for which input values can |
| be freely chosen. For floating point ops, the rounding mode |
| is not counted here, as it is restricted. */ |
| int |
| get_num_operands(IROp op) |
| { |
| IRType unused, t1, t2, t3, t4; |
| |
| typeof_primop(op, &unused, &t1, &t2, &t3, &t4); |
| |
| int num_operands = 4; |
| if (t4 == Ity_INVALID) num_operands = 3; |
| if (t3 == Ity_INVALID) num_operands = 2; |
| if (t2 == Ity_INVALID) num_operands = 1; |
| |
| if (is_floating_point_op_with_rounding_mode(op)) |
| -- num_operands; |
| |
| return num_operands; |
| } |
| |
| |
| unsigned |
| sizeof_irtype(IRType ty) |
| { |
| return sizeofIRType(ty); |
| } |
| |
| |
| void |
| typeof_primop(IROp op, IRType *t_dst, IRType *t_arg1, IRType *t_arg2, |
| IRType *t_arg3, IRType *t_arg4) |
| { |
| return typeOfPrimop(op, t_dst, t_arg1, t_arg2, t_arg3, t_arg4); |
| } |