diff options
Diffstat (limited to 'binutils-2.25/gas/testsuite/gas/all/test-gen.c')
-rw-r--r-- | binutils-2.25/gas/testsuite/gas/all/test-gen.c | 744 |
1 files changed, 744 insertions, 0 deletions
diff --git a/binutils-2.25/gas/testsuite/gas/all/test-gen.c b/binutils-2.25/gas/testsuite/gas/all/test-gen.c new file mode 100644 index 00000000..96aaeb81 --- /dev/null +++ b/binutils-2.25/gas/testsuite/gas/all/test-gen.c @@ -0,0 +1,744 @@ +#ifndef TEST_GEN_C +#define TEST_GEN_C 1 + +/* Copyright (C) 2000, 2003, 2005, 2007 Free Software Foundation + Contributed by Alexandre Oliva <aoliva@cygnus.com> + + This file 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 3 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., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* This is a source file with infra-structure to test generators for + assemblers and disassemblers. + + The strategy to generate testcases is as follows. We'll output to + two streams: one will get the assembly source, and the other will + get regexps that match the expected binary patterns. + + To generate each instruction, the functions of a func[] are called, + each with the corresponding func_arg. Each function should set + members of insn_data, to decide what it's going to output to the + assembly source, the corresponding output for the disassembler + tester, and the bits to be set in the instruction word. The + strings to be output must have been allocated with strdup() or + malloc(), so that they can be freed. A function may also modify + insn_size. More details in test-gen.c + + Because this would have generated too many tests, we have chosen to + define ``random'' sequences of numbers/registers, and simply + generate each instruction a couple of times, which should get us + enough coverage. + + In general, test generators should be compiled/run as follows: + + % gcc test.c -o test + % ./test > test.s 2 > test.d + + Please note that this file contains a couple of GCC-isms, such as + macro varargs (also available in C99, but with a difference syntax) + and labeled elements in initializers (so that insn definitions are + simpler and safer). + + It is assumed that the test generator #includes this file after + defining any of the preprocessor macros documented below. The test + generator is supposed to define instructions, at least one group of + instructions, optionally, a sequence of groups. + + It should also define a main() function that outputs the initial + lines of the assembler input and of the test control file, that + also contains the disassembler output. The main() funcion may + optionally set skip_list too, before calling output_groups() or + output_insns(). */ + +/* Define to 1 to avoid repeating instructions and to use a simpler + register/constant generation mechanism. This makes it much easier + to verify that the generated bit patterns are correct. */ +#ifndef SIMPLIFY_OUTPUT +#define SIMPLIFY_OUTPUT 0 +#endif + +/* Define to 0 to avoid generating disassembler tests. */ +#ifndef DISASSEMBLER_TEST +#define DISASSEMBLER_TEST 1 +#endif + +/* Define to the number of times to repeat the generation of each + insn. It's best to use prime numbers, to improve randomization. */ +#ifndef INSN_REPEAT +#define INSN_REPEAT 5 +#endif + +/* Define in order to get randomization_counter printed, as a comment, + in the disassembler output, after each insn is emitted. */ +#ifndef OUTPUT_RANDOMIZATION_COUNTER +#define OUTPUT_RANDOMIZATION_COUNTER 0 +#endif + +/* Other configuration macros are DEFINED_WORD and DEFINED_FUNC_ARG, + see below. */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +/* It is expected that the main program defines the type `word' before + includeing this. */ +#ifndef DEFINED_WORD +typedef unsigned long long word; +#endif + +/* This struct is used as the output area for each function. It + should store in as_in a pointer to the string to be output to the + assembler; in dis_out, the string to be expected in return from the + disassembler, and in bits the bits of the instruction word that are + enabled by the assembly fragment. */ +typedef struct +{ + char * as_in; + char * dis_out; + word bits; +} insn_data; + +#ifndef DEFINED_FUNC_ARG +/* This is the struct that feeds information to each function. You're + free to extend it, by `typedef'ing it before including this file, + and defining DEFINED_FUNC_ARG. You may even reorder the fields, + but do not remove any of the existing fields. */ +typedef struct +{ + int i1; + int i2; + int i3; + void * p1; + void * p2; + word w; +} func_arg; +#endif + +/* This is the struct whose arrays define insns. Each func in the + array will be called, in sequence, being given a pointer to the + associated arg and a pointer to a zero-initialized output area, + that it may fill in. */ +typedef struct +{ + int (* func) (func_arg *, insn_data *); + func_arg arg; +} func; + +/* Use this to group insns under a name. */ +typedef struct +{ + const char * name; + func ** insns; +} group_t; + +/* This is the size of each instruction. Use `insn_size_bits' instead + of `insn_bits' in an insn defition to modify it. */ +int insn_size = 4; + +/* The offset of the next insn, as expected in the disassembler + output. */ +int current_offset = 0; + +/* The offset and name of the last label to be emitted. */ +int last_label_offset = 0; +const char * last_label_name = 0; + +/* This variable may be initialized in main() to `argv+1', if + `argc>1', so that tests are emitted only for instructions that + match exactly one of the given command-line arguments. If it is + NULL, tests for all instructions are emitted. It must be a + NULL-terminated array of pointers to strings (just like + `argv+1'). */ +char ** skip_list = 0; + +/* This is a counter used to walk the various arrays of ``random'' + operand generation. In simplified output mode, it is zeroed after + each insn, otherwise it just keeps growing. */ +unsigned randomization_counter = 0; + +/* Use `define_insn' to create an array of funcs to define an insn, + then `insn' to refer to that insn when defining an insn group. */ +#define define_insn(insname, funcs...) \ + func i_ ## insname[] = { funcs, { 0 } } +#define insn(insname) (i_ ## insname) + +/* Use these to output a comma followed by an optional space, a single + space, a plus sign, left and right square brackets and parentheses, + all of them properly quoted. */ +#define comma literal_q (", ", ", ?") +#define space literal (" ") +#define tab literal ("\t") +#define plus literal_q ("+", "\\+") +#define lsqbkt literal_q ("[", "\\[") +#define rsqbkt literal_q ("]", "\\]") +#define lparen literal_q ("(", "\\(") +#define rparen literal_q (")", "\\)") + +/* Use this as a placeholder when you define a macro that expects an + argument, but you don't have anything to output there. */ +int +nothing (func_arg *arg, insn_data *data) +#define nothing { nothing } +{ + return 0; +} + +/* This is to be used in the argument list of define_insn, causing a + string to be copied into both the assembly and the expected + disassembler output. It is assumed not to modify the binary + encoding of the insn. */ +int +literal (func_arg *arg, insn_data *data) +#define literal(s) { literal, { p1: (s) } } +{ + data->as_in = data->dis_out = strdup ((char *) arg->p1); + return 0; +} + +/* The characters `[', `]', `\\' and `^' must be quoted in the + disassembler-output matcher. If a literal string contains any of + these characters, use literal_q instead of literal, and specify the + unquoted version (for as input) as the first argument, and the + quoted version (for expected disassembler output) as the second + one. */ +int +literal_q (func_arg *arg, insn_data *data) +#define literal_q(s,q) { literal_q, { p1: (s), p2: (q) } } +{ + data->as_in = strdup ((char *) arg->p1); + data->dis_out = strdup ((char *) arg->p2); + return 0; +} + +/* Given an insn name, check whether it should be skipped or not, + depending on skip_list. Return non-zero if the insn is to be + skipped. */ +int +skip_insn (char *name) +{ + char **test; + + if (! skip_list) + return 0; + + for (test = skip_list; * test; ++ test) + if (strcmp (name, * test) == 0) + return 0; + + return 1; +} + +/* Use this to emit the actual insn name, with its opcode, in + architectures with fixed-length instructions. */ +int +insn_bits (func_arg *arg, insn_data *data) +#define insn_bits(name,bits) \ + { insn_bits, { p1: # name, w: bits } } +{ + if (skip_insn ((char *) arg->p1)) + return 1; + data->as_in = data->dis_out = strdup ((char *) arg->p1); + data->bits = arg->w; + return 0; +} + +/* Use this to emit the insn name and its opcode in architectures + without a variable instruction length. */ +int +insn_size_bits (func_arg *arg, insn_data *data) +#define insn_size_bits(name,size,bits) \ + { insn_size_bits, { p1: # name, i1: size, w: bits } } +{ + if (skip_insn ((char *) arg->p1)) + return 1; + data->as_in = data->dis_out = strdup ((char *) arg->p1); + data->bits = arg->w; + insn_size = arg->i1; + return 0; +} + +/* Use this to advance the random generator by one, in case it is + generating repetitive patterns. It is usually good to arrange that + each insn consumes a prime number of ``random'' numbers, or, at + least, that it does not consume an exact power of two ``random'' + numbers. */ +int +tick_random (func_arg *arg, insn_data *data) +#define tick_random { tick_random } +{ + ++ randomization_counter; + return 0; +} + +/* Select the next ``random'' number from the array V of size S, and + advance the counter. */ +#define get_bits_from_size(V,S) \ + ((V)[randomization_counter ++ % (S)]) + +/* Utility macros. `_get_bits_var', used in some macros below, assume + the names of the arrays used to define the ``random'' orders start + with `random_order_'. */ +#define _get_bits_var(N) (random_order_ ## N) +#define _get_bits_size(V) (sizeof (V) / sizeof * (V)) + +/* Use this within a `func_arg' to select one of the arrays below (or + any other array that starts with random_order_N. */ +#define mk_get_bits(N) \ + p2: _get_bits_var (N), i3: _get_bits_size (_get_bits_var (N)) + +/* Simplified versions of get_bits_from_size for when you have access + to the array, so that its size can be implicitly calculated. */ +#define get_bits_from(V) get_bits_from_size ((V),_get_bits_size ((V))) +#define get_bits(N) get_bits_from (_get_bits_var (N)) + + +/* Use `2u' to generate 2-bit unsigned values. Good for selecting + registers randomly from a set of 4 registers. */ +unsigned random_order_2u[] = + { + /* This sequence was generated by hand so that no digit appers more + than once in any horizontal or vertical line. */ + 0, 1, 3, 2, + 2, 0, 1, 3, + 1, 3, 2, 0, + 3, 2, 0, 1 + }; + +/* Use `3u' to generate 3-bit unsigned values. Good for selecting + registers randomly from a set of 8 registers. */ +unsigned random_order_3u[] = + { + /* This sequence was generated by: + f(k) = 3k mod 8 + except that the middle pairs were swapped. */ + 0, 6, 3, 1, 4, 2, 7, 5, + /* This sequence was generated by: + f(k) = 5k mod 8 + except that the middle pairs were swapped. */ + 0, 2, 5, 7, 4, 6, 1, 3, + }; + +/* Use `4u' to generate 4-bit unsigned values. Good for selecting + registers randomly from a set of 16 registers. */ +unsigned random_order_4u[] = + { + /* This sequence was generated by: + f(k) = 5k mod 16 + except that the middle pairs were swapped. */ + 0, 5, 15, 10, 9, 4, 14, 3, + 8, 13, 7, 2, 1, 12, 6, 11, + /* This sequence was generated by: + f(k) = 7k mod 16 + except that the middle pairs were swapped. */ + 0, 7, 5, 14, 3, 12, 10, 1, + 8, 15, 13, 6, 11, 4, 2, 9, + }; + +/* Use `5u' to generate 5-bit unsigned values. Good for selecting + registers randomly from a set of 32 registers. */ +unsigned random_order_5u[] = + { + /* This sequence was generated by: + f(k) = (13k) mod 32 + except that the middle pairs were swapped. */ + 0, 26, 13, 7, 20, 14, 1, 27, + 8, 2, 21, 15, 28, 22, 9, 3, + 16, 10, 29, 23, 4, 30, 17, 11, + 24, 18, 5, 31, 12, 6, 25, 19 + }; + +/* Use `7s' to generate 7-bit signed values. Good for selecting + ``interesting'' constants from -64 to +63. */ +int random_order_7s[] = + { + /* Sequence generated by hand, to explore limit values and a few + intermediate values selected by chance. Keep the number of + intermediate values low, to ensure that the limit values are + generated often enough. */ + 0, -1, -64, 63, -32, 32, 24, -20, + 9, -27, -31, 33, 40, -2, -5, 1 + }; + +/* Use `8s' to generate 8-bit signed values. Good for selecting + ``interesting'' constants from -128 to +127. */ +int random_order_8s[] = + { + /* Sequence generated by hand, to explore limit values and a few + intermediate values selected by chance. Keep the number of + intermediate values low, to ensure that the limit values are + generated often enough. */ + 0, -1, -128, 127, -32, 32, 24, -20, + 73, -27, -95, 33, 104, -2, -69, 1 + }; + +/* Use `9s' to generate 9-bit signed values. Good for selecting + ``interesting'' constants from -256 to +255. */ +int random_order_9s[] = + { + /* Sequence generated by hand, to explore limit values and a few + intermediate values selected by chance. Keep the number of + intermediate values low, to ensure that the limit values are + generated often enough. */ + 0, -1, -256, 255, -64, 64, 72, -40, + 73, -137, -158, 37, 104, -240, -69, 1 + }; + +/* Use `16s' to generate 16-bit signed values. Good for selecting + ``interesting'' constants from -32768 to +32767. */ +int random_order_16s[] = + { + /* Sequence generated by hand, to explore limit values and a few + intermediate values selected by chance. Keep the number of + intermediate values low, to ensure that the limit values are + generated often enough. */ + -32768, + 32767, + (-1 << 15) | (64 << 8) | 32, + (64 << 8) | 32, + 0x1234, + (-1 << 15) | 0x8765, + 0x0180, + (-1 << 15) | 0x8001 +}; + +/* Use `24s' to generate 24-bit signed values. Good for selecting + ``interesting'' constants from -2^23 to 2^23-1. */ +int random_order_24s[] = + { + /* Sequence generated by hand, to explore limit values and a few + intermediate values selected by chance. Keep the number of + intermediate values low, to ensure that the limit values are + generated often enough. */ + -1 << 23, + 1 << 23 -1, + (-1 << 23) | (((64 << 8) | 32) << 8) | 16, + (((64 << 8) | 32) << 8) | 16, + 0x123456, + (-1 << 23) | 0x876543, + 0x01ff80, + (-1 << 23) | 0x80ff01 +}; + +/* Use `32s' to generate 32-bit signed values. Good for selecting + ``interesting'' constants from -2^31 to 2^31-1. */ +int random_order_32s[] = + { + /* Sequence generated by hand, to explore limit values and a few + intermediate values selected by chance. Keep the number of + intermediate values low, to ensure that the limit values are + generated often enough. */ + -1 << 31, + 1 << 31 - 1, + (-1 << 31) | (((((64 << 8) | 32) << 8) | 16) << 8) | 8, + (((((64 << 8) | 32) << 8) | 16) << 8) | 8, + 0x12345678, + (-1 << 31) | 0x87654321, + 0x01ffff80, + (-1 << 31) | 0x80ffff01 + }; + +/* This function computes the number of digits needed to represent a + given number. */ +unsigned long +ulen (unsigned long i, unsigned base) +{ + int count = 0; + + if (i == 0) + return 1; + for (; i > 0; ++ count) + i /= base; + return count; +} + +/* Use this to generate a signed constant of the given size, shifted + by the given amount, with the specified endianness. */ +int +signed_constant (func_arg * arg, insn_data * data) +#define signed_constant(bits, shift, revert) \ + { signed_constant, { i1: shift, i2: bits * (revert ? -1 : 1), \ + mk_get_bits (bits ## s) } } +{ + long val = get_bits_from_size ((unsigned *) arg->p2, arg->i3); + int len = (val >= 0 ? ulen (val, 10) : (1 + ulen (-val, 10))); + int nbits = (arg->i2 >= 0 ? arg->i2 : -arg->i2); + word bits = ((word) val) & (((((word) 1) << (nbits - 1)) << 1) - 1); + + data->as_in = data->dis_out = malloc (len + 1); + sprintf (data->as_in, "%ld", val); + if (arg->i2 < 0) + { + word rbits = 0; + + do + { + rbits <<= 8; + rbits |= bits & 0xff; + bits >>= 8; + nbits -= 8; + } + while (nbits > 0); + + bits = rbits; + } + data->bits = bits << arg->i1; + + return 0; +} + +/* Use this to generate a unsigned constant of the given size, shifted + by the given amount, with the specified endianness. */ +int +unsigned_constant (func_arg * arg, insn_data * data) +#define unsigned_constant(bits, shift, revert) \ + { unsigned_constant, { i1: shift, i2: bits * (revert ? -1 : 1), \ + mk_get_bits (bits ## s) } } +{ + int nbits = (arg->i2 >= 0 ? arg->i2 : -arg->i2); + unsigned long val = + get_bits_from_size ((unsigned *) arg->p2, arg->i3) + & (((((word) 1) << (nbits - 1)) << 1) - 1); + int len = ulen (val, 10); + word bits = val; + + data->as_in = data->dis_out = malloc (len + 1); + sprintf (data->as_in, "%lu", val); + if (arg->i2 < 0) + { + word rbits = 0; + + do + { + rbits <<= 8; + rbits |= bits & 0xff; + bits >>= 8; + nbits -= 8; + } + while (nbits > 0); + + bits = rbits; + } + data->bits = bits << arg->i1; + + return 0; +} + +/* Use this to generate an absolute address of the given size, shifted + by the given amount, with the specified endianness. */ +int +absolute_address (func_arg *arg, insn_data *data) +#define absolute_address (bits, shift, revert) \ + { absolute_address, { i1: shift, i2: bits * (revert ? -1 : 1), \ + mk_get_bits (bits ## s) } } +{ + int nbits = (arg->i2 >= 0 ? arg->i2 : -arg->i2); + unsigned long val = + get_bits_from_size ((unsigned *) arg->p2, arg->i3) + & (((((word) 1) << (nbits - 1)) << 1) - 1); + word bits = val; + + data->as_in = malloc (ulen (val, 10) + 1); + sprintf (data->as_in, "%lu", val); + data->dis_out = malloc (nbits / 4 + 11); + sprintf (data->dis_out, "0*%0*lx <[^>]*>", nbits / 4, val); + if (arg->i2 < 0) + { + word rbits = 0; + + do + { + rbits <<= 8; + rbits |= bits & 0xff; + bits >>= 8; + nbits -= 8; + } + while (nbits > 0); + + bits = rbits; + } + data->bits = bits << arg->i1; + + return 0; +} + +/* Use this to generate a register name that starts with a given + prefix, and is followed by a number generated by `gen' (see + mk_get_bits below). The register number is shifted `shift' bits + left before being stored in the binary insn. */ +int +reg_p (func_arg *arg, insn_data *data) +#define reg_p(prefix,shift,gen) \ + { reg_p, { i1: (shift), p1: (prefix), gen } } +{ + unsigned reg = get_bits_from_size ((unsigned *) arg->p2, arg->i3); + char *regname = (char *) arg->p1; + + data->as_in = data->dis_out = malloc (strlen (regname) + ulen (reg, 10) + 1); + sprintf (data->as_in, "%s%u", regname, reg); + data->bits = reg; + data->bits <<= arg->i1; + return 0; +} + +/* Use this to generate a register name taken from an array. The + index into the array `names' is to be produced by `gen', but `mask' + may be used to filter out some of the bits before choosing the + disassembler output and the bits for the binary insn, shifted left + by `shift'. For example, if registers have canonical names, but + can also be referred to by aliases, the array can be n times larger + than the actual number of registers, and the mask is then used to + pick the canonical name for the disassembler output, and to + eliminate the extra bits from the binary output. */ +int +reg_r (func_arg *arg, insn_data *data) +#define reg_r(names,shift,mask,gen) \ + { reg_r, { i1: (shift), i2: (mask), p1: (names), gen } } +{ + unsigned reg = get_bits_from_size ((unsigned *) arg->p2, arg->i3); + + data->as_in = strdup (((const char **) arg->p1)[reg]); + reg &= arg->i2; + data->dis_out = strdup (((const char **) arg->p1)[reg]); + data->bits = reg; + data->bits <<= arg->i1; + return 0; +} + +/* Given a NULL-terminated array of insns-definitions (pointers to + arrays of funcs), output test code for the insns to as_in (assembly + input) and dis_out (expected disassembler output). */ +void +output_insns (func **insn, FILE *as_in, FILE *dis_out) +{ + for (; *insn; ++insn) + { + insn_data *data; + func *parts = *insn; + int part_count = 0, r; + + /* Figure out how many funcs have to be called. */ + while (parts[part_count].func) + ++part_count; + + /* Allocate storage for the output area of each func. */ + data = (insn_data*) malloc (part_count * sizeof (insn_data)); + +#if SIMPLIFY_OUTPUT + randomization_counter = 0; +#else + /* Repeat each insn several times. */ + for (r = 0; r < INSN_REPEAT; ++r) +#endif + { + unsigned saved_rc = randomization_counter; + int part; + word bits = 0; + + for (part = 0; part < part_count; ++part) + { + /* Zero-initialize the storage. */ + data[part].as_in = data[part].dis_out = 0; + data[part].bits = 0; + /* If a func returns non-zero, skip this line. */ + if (parts[part].func (&parts[part].arg, &data[part])) + goto skip; + /* Otherwise, get its output bit pattern into the total + bit pattern. */ + bits |= data[part].bits; + } + + if (as_in) + { + /* Output the whole assembly line. */ + fputc ('\t', as_in); + for (part = 0; part < part_count; ++part) + if (data[part].as_in) + fputs (data[part].as_in, as_in); + fputc ('\n', as_in); + } + + if (dis_out) + { + /* Output the disassembler expected output line, + starting with the offset and the insn binary pattern, + just like objdump outputs. Because objdump sometimes + inserts spaces between each byte in the insn binary + pattern, make the space optional. */ + fprintf (dis_out, "0*%x <", current_offset); + if (last_label_name) + if (current_offset == last_label_offset) + fputs (last_label_name, dis_out); + else + fprintf (dis_out, "%s\\+0x%x", last_label_name, + current_offset - last_label_offset); + else + fputs ("[^>]*", dis_out); + fputs ("> ", dis_out); + for (part = insn_size; part-- > 0; ) + fprintf (dis_out, "%02x ?", (int)(bits >> (part * 8)) & 0xff); + fputs (" *\t", dis_out); + +#if DISASSEMBLER_TEST + for (part = 0; part < part_count; ++part) + if (data[part].dis_out) + fputs (data[part].dis_out, dis_out); +#else + /* If we're not testing the DISASSEMBLER, just match + anything. */ + fputs (".*", dis_out); +#endif + fputc ('\n', dis_out); +#if OUTPUT_RANDOMIZATION_COUNTER + fprintf (dis_out, "# %i\n", randomization_counter); +#endif + } + + /* Account for the insn_size bytes we've just output. */ + current_offset += insn_size; + + /* Release the memory that each func may have allocated. */ + for (; part-- > 0;) + { + skip: + if (data[part].as_in) + free (data[part].as_in); + if (data[part].dis_out + && data[part].dis_out != data[part].as_in) + free (data[part].dis_out); + } + + /* There's nothing random here, don't repeat this insn. */ + if (randomization_counter == saved_rc) + break; + } + + free (data); + } +} + +/* For each group, output an asm label and the insns of the group. */ +void +output_groups (group_t group[], FILE *as_in, FILE *dis_out) +{ + for (; group->name; ++group) + { + fprintf (as_in, "%s:\n", group->name); + fprintf (dis_out, "# %s:\n", group->name); + last_label_offset = current_offset; + last_label_name = group->name; + output_insns (group->insns, as_in, dis_out); + } +} + +#endif |