]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Add a new performance benchmark: tinycc.
authorNicholas Nethercote <njn@valgrind.org>
Tue, 20 Dec 2005 23:02:43 +0000 (23:02 +0000)
committerNicholas Nethercote <njn@valgrind.org>
Tue, 20 Dec 2005 23:02:43 +0000 (23:02 +0000)
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@5394

perf/Makefile.am
perf/README
perf/stdarg.h [new file with mode: 0644]
perf/stddef.h [new file with mode: 0644]
perf/tinycc.c [new file with mode: 0644]
perf/tinycc.vgperf [new file with mode: 0644]

index cd31e5f0b4537aa7b6f413375a70986bf765c9b8..9b40b99586f43cd36802ce6eafd23bafcf1b1efd 100644 (file)
@@ -1,22 +1,27 @@
 
 noinst_SCRIPTS = vg_perf
 
+noinst_HEADERS = stdarg.h stddef.h
+
 EXTRA_DIST = $(noinst_SCRIPTS) \
        bigcode.vgperf \
        bz2.vgperf \
        fbench.vgperf \
        ffbench.vgperf \
        heap.vgperf \
-       sarp.vgperf
+       sarp.vgperf \
+       tinycc.vgperf
 
 check_PROGRAMS = \
-       bigcode bz2 fbench ffbench heap sarp
+       bigcode bz2 fbench ffbench heap sarp tinycc
 
 AM_CFLAGS   = $(WERROR) -Winline -Wall -Wshadow -g -O
 AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/include -I$(top_builddir)/include
 AM_CXXFLAGS = $(AM_CFLAGS)
 
 # Extra stuff
-fbench_LDADD           = -lm
-ffbench_LDADD          = -lm
+fbench_LDADD   = -lm
+ffbench_LDADD  = -lm
+tinycc_CFLAGS  =
+tinycc_LDADD   = -ldl
 
index 0dd8c16ccb7febf904e9ccc47e5922eec8c673cc..c092105c9140c37f4a5359f5248ca6a7ff68f6de 100644 (file)
@@ -53,4 +53,11 @@ ffbench:
 - Weaknesses:  Dominated by the inner loop, which is quite long and flatters
                Valgrind due to the small dispatcher overhead.
 
+tinycc:
+- Description: A very small and fast C compiler.  A munged version of
+               Fabrice Bellard's TinyCC compiling itself multiple times.
+- Strengths:   A real program, lots of code (top 100 blocks only account for
+               47% of execution), involves large irregular data structures
+               (presumably, since it's a compiler).
+- Weaknesses   None, really, it's a good benchmark.
 
diff --git a/perf/stdarg.h b/perf/stdarg.h
new file mode 100644 (file)
index 0000000..46993bd
--- /dev/null
@@ -0,0 +1,17 @@
+// njn: used by tinycc
+
+#ifndef _STDARG_H
+#define _STDARG_H
+
+typedef char *va_list;
+
+/* only correct for i386 */
+#define va_start(ap,last) ap = ((char *)&(last)) + ((sizeof(last)+3)&~3)
+#define va_arg(ap,type) (ap += (sizeof(type)+3)&~3, *(type *)(ap - ((sizeof(type)+3)&~3)))
+#define va_end(ap)
+
+/* fix a buggy dependency on GCC in libio.h */
+typedef va_list __gnuc_va_list;
+#define _VA_LIST_DEFINED
+
+#endif
diff --git a/perf/stddef.h b/perf/stddef.h
new file mode 100644 (file)
index 0000000..16d09b9
--- /dev/null
@@ -0,0 +1,23 @@
+// njn: used by tinycc
+
+#ifndef _STDDEF_H
+#define _STDDEF_H
+
+#define NULL ((void *)0)
+typedef __SIZE_TYPE__ size_t;
+typedef __WCHAR_TYPE__ wchar_t;
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+#define offsetof(type, field) ((size_t) &((type *)0)->field)
+
+/* need to do that because of glibc 2.1 bug (should have a way to test
+   presence of 'long long' without __GNUC__, or TCC should define
+   __GNUC__ ? */
+#if !defined(__int8_t_defined) && !defined(__dietlibc__)
+#define __int8_t_defined
+typedef        char int8_t;
+typedef        short int int16_t;
+typedef        int int32_t;
+typedef long long int int64_t;
+#endif
+
+#endif
diff --git a/perf/tinycc.c b/perf/tinycc.c
new file mode 100644 (file)
index 0000000..1fb0a2c
--- /dev/null
@@ -0,0 +1,21850 @@
+/*
+ *  TCC - Tiny C Compiler
+ * 
+ *  Copyright (c) 2001-2004 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#define _GNU_SOURCE
+
+// njn: inlined config.h
+//#include "config.h"
+//---------------------------------------------------------------------------
+/* Automatically generated by configure - do not modify */
+#define CONFIG_TCCDIR "tinycc-extras"
+#define GCC_MAJOR 3
+#define HOST_I386 1
+#define TCC_VERSION "0.9.23"
+//---------------------------------------------------------------------------
+
+// njn: comment out CONFIG_TCCBOOT branch
+//#ifdef CONFIG_TCCBOOT
+//
+//#include "tccboot.h"
+//#define CONFIG_TCC_STATIC
+//
+//#else
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <time.h>
+#ifdef WIN32
+#include <sys/timeb.h>
+#endif
+#ifndef WIN32
+#include <sys/time.h>
+#include <sys/ucontext.h>
+#endif
+
+//#endif /* !CONFIG_TCCBOOT */
+
+// njn: inlined elf.h
+//#include "elf.h"
+//---------------------------------------------------------------------------
+/* This file defines standard ELF types, structures, and macros.
+   Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ian Lance Taylor <ian@cygnus.com>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef _ELF_H
+#define        _ELF_H 1
+
+#ifndef WIN32
+#include <inttypes.h>
+#else
+#ifndef __int8_t_defined
+#define __int8_t_defined
+typedef signed char int8_t;
+typedef        short int int16_t;
+typedef        int int32_t;
+typedef long long int int64_t;
+#endif
+
+typedef unsigned char          uint8_t;
+typedef unsigned short int     uint16_t;
+typedef unsigned int           uint32_t;
+typedef unsigned long long int uint64_t;
+#endif
+
+/* Standard ELF types.  */
+
+/* Type for a 16-bit quantity.  */
+typedef uint16_t Elf32_Half;
+typedef uint16_t Elf64_Half;
+
+/* Types for signed and unsigned 32-bit quantities.  */
+typedef uint32_t Elf32_Word;
+typedef        int32_t  Elf32_Sword;
+typedef uint32_t Elf64_Word;
+typedef        int32_t  Elf64_Sword;
+
+/* Types for signed and unsigned 64-bit quantities.  */
+typedef uint64_t Elf32_Xword;
+typedef        int64_t  Elf32_Sxword;
+typedef uint64_t Elf64_Xword;
+typedef        int64_t  Elf64_Sxword;
+
+/* Type of addresses.  */
+typedef uint32_t Elf32_Addr;
+typedef uint64_t Elf64_Addr;
+
+/* Type of file offsets.  */
+typedef uint32_t Elf32_Off;
+typedef uint64_t Elf64_Off;
+
+/* Type for section indices, which are 16-bit quantities.  */
+typedef uint16_t Elf32_Section;
+typedef uint16_t Elf64_Section;
+
+/* Type of symbol indices.  */
+typedef uint32_t Elf32_Symndx;
+typedef uint64_t Elf64_Symndx;
+
+
+/* The ELF file header.  This appears at the start of every ELF file.  */
+
+#define EI_NIDENT (16)
+
+typedef struct
+{
+  unsigned char        e_ident[EI_NIDENT];     /* Magic number and other info */
+  Elf32_Half   e_type;                 /* Object file type */
+  Elf32_Half   e_machine;              /* Architecture */
+  Elf32_Word   e_version;              /* Object file version */
+  Elf32_Addr   e_entry;                /* Entry point virtual address */
+  Elf32_Off    e_phoff;                /* Program header table file offset */
+  Elf32_Off    e_shoff;                /* Section header table file offset */
+  Elf32_Word   e_flags;                /* Processor-specific flags */
+  Elf32_Half   e_ehsize;               /* ELF header size in bytes */
+  Elf32_Half   e_phentsize;            /* Program header table entry size */
+  Elf32_Half   e_phnum;                /* Program header table entry count */
+  Elf32_Half   e_shentsize;            /* Section header table entry size */
+  Elf32_Half   e_shnum;                /* Section header table entry count */
+  Elf32_Half   e_shstrndx;             /* Section header string table index */
+} Elf32_Ehdr;
+
+typedef struct
+{
+  unsigned char        e_ident[EI_NIDENT];     /* Magic number and other info */
+  Elf64_Half   e_type;                 /* Object file type */
+  Elf64_Half   e_machine;              /* Architecture */
+  Elf64_Word   e_version;              /* Object file version */
+  Elf64_Addr   e_entry;                /* Entry point virtual address */
+  Elf64_Off    e_phoff;                /* Program header table file offset */
+  Elf64_Off    e_shoff;                /* Section header table file offset */
+  Elf64_Word   e_flags;                /* Processor-specific flags */
+  Elf64_Half   e_ehsize;               /* ELF header size in bytes */
+  Elf64_Half   e_phentsize;            /* Program header table entry size */
+  Elf64_Half   e_phnum;                /* Program header table entry count */
+  Elf64_Half   e_shentsize;            /* Section header table entry size */
+  Elf64_Half   e_shnum;                /* Section header table entry count */
+  Elf64_Half   e_shstrndx;             /* Section header string table index */
+} Elf64_Ehdr;
+
+/* Fields in the e_ident array.  The EI_* macros are indices into the
+   array.  The macros under each EI_* macro are the values the byte
+   may have.  */
+
+#define EI_MAG0                0               /* File identification byte 0 index */
+#define ELFMAG0                0x7f            /* Magic number byte 0 */
+
+#define EI_MAG1                1               /* File identification byte 1 index */
+#define ELFMAG1                'E'             /* Magic number byte 1 */
+
+#define EI_MAG2                2               /* File identification byte 2 index */
+#define ELFMAG2                'L'             /* Magic number byte 2 */
+
+#define EI_MAG3                3               /* File identification byte 3 index */
+#define ELFMAG3                'F'             /* Magic number byte 3 */
+
+/* Conglomeration of the identification bytes, for easy testing as a word.  */
+#define        ELFMAG          "\177ELF"
+#define        SELFMAG         4
+
+#define EI_CLASS       4               /* File class byte index */
+#define ELFCLASSNONE   0               /* Invalid class */
+#define ELFCLASS32     1               /* 32-bit objects */
+#define ELFCLASS64     2               /* 64-bit objects */
+#define ELFCLASSNUM    3
+
+#define EI_DATA                5               /* Data encoding byte index */
+#define ELFDATANONE    0               /* Invalid data encoding */
+#define ELFDATA2LSB    1               /* 2's complement, little endian */
+#define ELFDATA2MSB    2               /* 2's complement, big endian */
+#define ELFDATANUM     3
+
+#define EI_VERSION     6               /* File version byte index */
+                                       /* Value must be EV_CURRENT */
+
+#define EI_OSABI       7               /* OS ABI identification */
+#define ELFOSABI_SYSV          0       /* UNIX System V ABI */
+#define ELFOSABI_HPUX          1       /* HP-UX */
+#define ELFOSABI_FREEBSD        9       /* Free BSD */
+#define ELFOSABI_ARM           97      /* ARM */
+#define ELFOSABI_STANDALONE    255     /* Standalone (embedded) application */
+
+#define EI_ABIVERSION  8               /* ABI version */
+
+#define EI_PAD         9               /* Byte index of padding bytes */
+
+/* Legal values for e_type (object file type).  */
+
+#define ET_NONE                0               /* No file type */
+#define ET_REL         1               /* Relocatable file */
+#define ET_EXEC                2               /* Executable file */
+#define ET_DYN         3               /* Shared object file */
+#define ET_CORE                4               /* Core file */
+#define        ET_NUM          5               /* Number of defined types */
+#define ET_LOPROC      0xff00          /* Processor-specific */
+#define ET_HIPROC      0xffff          /* Processor-specific */
+
+/* Legal values for e_machine (architecture).  */
+
+#define EM_NONE                 0              /* No machine */
+#define EM_M32          1              /* AT&T WE 32100 */
+#define EM_SPARC        2              /* SUN SPARC */
+#define EM_386          3              /* Intel 80386 */
+#define EM_68K          4              /* Motorola m68k family */
+#define EM_88K          5              /* Motorola m88k family */
+#define EM_486          6              /* Intel 80486 */
+#define EM_860          7              /* Intel 80860 */
+#define EM_MIPS                 8              /* MIPS R3000 big-endian */
+#define EM_S370                 9              /* Amdahl */
+#define EM_MIPS_RS4_BE 10              /* MIPS R4000 big-endian */
+#define EM_RS6000      11              /* RS6000 */
+
+#define EM_PARISC      15              /* HPPA */
+#define EM_nCUBE       16              /* nCUBE */
+#define EM_VPP500      17              /* Fujitsu VPP500 */
+#define EM_SPARC32PLUS 18              /* Sun's "v8plus" */
+#define EM_960         19              /* Intel 80960 */
+#define EM_PPC         20              /* PowerPC */
+
+#define EM_V800                36              /* NEC V800 series */
+#define EM_FR20                37              /* Fujitsu FR20 */
+#define EM_RH32                38              /* TRW RH32 */
+#define EM_MMA         39              /* Fujitsu MMA */
+#define EM_ARM         40              /* ARM */
+#define EM_FAKE_ALPHA  41              /* Digital Alpha */
+#define EM_SH          42              /* Hitachi SH */
+#define EM_SPARCV9     43              /* SPARC v9 64-bit */
+#define EM_TRICORE     44              /* Siemens Tricore */
+#define EM_ARC         45              /* Argonaut RISC Core */
+#define EM_H8_300      46              /* Hitachi H8/300 */
+#define EM_H8_300H     47              /* Hitachi H8/300H */
+#define EM_H8S         48              /* Hitachi H8S */
+#define EM_H8_500      49              /* Hitachi H8/500 */
+#define EM_IA_64       50              /* Intel Merced */
+#define EM_MIPS_X      51              /* Stanford MIPS-X */
+#define EM_COLDFIRE    52              /* Motorola Coldfire */
+#define EM_68HC12      53              /* Motorola M68HC12 */
+#define EM_NUM         54
+
+/* If it is necessary to assign new unofficial EM_* values, please
+   pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the
+   chances of collision with official or non-GNU unofficial values.  */
+
+#define EM_ALPHA       0x9026
+#define EM_C60          0x9c60
+
+/* Legal values for e_version (version).  */
+
+#define EV_NONE                0               /* Invalid ELF version */
+#define EV_CURRENT     1               /* Current version */
+#define EV_NUM         2
+
+/* Section header.  */
+
+typedef struct
+{
+  Elf32_Word   sh_name;                /* Section name (string tbl index) */
+  Elf32_Word   sh_type;                /* Section type */
+  Elf32_Word   sh_flags;               /* Section flags */
+  Elf32_Addr   sh_addr;                /* Section virtual addr at execution */
+  Elf32_Off    sh_offset;              /* Section file offset */
+  Elf32_Word   sh_size;                /* Section size in bytes */
+  Elf32_Word   sh_link;                /* Link to another section */
+  Elf32_Word   sh_info;                /* Additional section information */
+  Elf32_Word   sh_addralign;           /* Section alignment */
+  Elf32_Word   sh_entsize;             /* Entry size if section holds table */
+} Elf32_Shdr;
+
+typedef struct
+{
+  Elf64_Word   sh_name;                /* Section name (string tbl index) */
+  Elf64_Word   sh_type;                /* Section type */
+  Elf64_Xword  sh_flags;               /* Section flags */
+  Elf64_Addr   sh_addr;                /* Section virtual addr at execution */
+  Elf64_Off    sh_offset;              /* Section file offset */
+  Elf64_Xword  sh_size;                /* Section size in bytes */
+  Elf64_Word   sh_link;                /* Link to another section */
+  Elf64_Word   sh_info;                /* Additional section information */
+  Elf64_Xword  sh_addralign;           /* Section alignment */
+  Elf64_Xword  sh_entsize;             /* Entry size if section holds table */
+} Elf64_Shdr;
+
+/* Special section indices.  */
+
+#define SHN_UNDEF      0               /* Undefined section */
+#define SHN_LORESERVE  0xff00          /* Start of reserved indices */
+#define SHN_LOPROC     0xff00          /* Start of processor-specific */
+#define SHN_HIPROC     0xff1f          /* End of processor-specific */
+#define SHN_ABS                0xfff1          /* Associated symbol is absolute */
+#define SHN_COMMON     0xfff2          /* Associated symbol is common */
+#define SHN_HIRESERVE  0xffff          /* End of reserved indices */
+
+/* Legal values for sh_type (section type).  */
+
+#define SHT_NULL        0              /* Section header table entry unused */
+#define SHT_PROGBITS    1              /* Program data */
+#define SHT_SYMTAB      2              /* Symbol table */
+#define SHT_STRTAB      3              /* String table */
+#define SHT_RELA        4              /* Relocation entries with addends */
+#define SHT_HASH        5              /* Symbol hash table */
+#define SHT_DYNAMIC     6              /* Dynamic linking information */
+#define SHT_NOTE        7              /* Notes */
+#define SHT_NOBITS      8              /* Program space with no data (bss) */
+#define SHT_REL                 9              /* Relocation entries, no addends */
+#define SHT_SHLIB       10             /* Reserved */
+#define SHT_DYNSYM      11             /* Dynamic linker symbol table */
+#define        SHT_NUM          12             /* Number of defined types.  */
+#define SHT_LOOS        0x60000000     /* Start OS-specific */
+#define SHT_LOSUNW      0x6ffffffb     /* Sun-specific low bound.  */
+#define SHT_SUNW_COMDAT  0x6ffffffb
+#define SHT_SUNW_syminfo 0x6ffffffc
+#define SHT_GNU_verdef  0x6ffffffd     /* Version definition section.  */
+#define SHT_GNU_verneed         0x6ffffffe     /* Version needs section.  */
+#define SHT_GNU_versym  0x6fffffff     /* Version symbol table.  */
+#define SHT_HISUNW      0x6fffffff     /* Sun-specific high bound.  */
+#define SHT_HIOS        0x6fffffff     /* End OS-specific type */
+#define SHT_LOPROC      0x70000000     /* Start of processor-specific */
+#define SHT_HIPROC      0x7fffffff     /* End of processor-specific */
+#define SHT_LOUSER      0x80000000     /* Start of application-specific */
+#define SHT_HIUSER      0x8fffffff     /* End of application-specific */
+
+/* Legal values for sh_flags (section flags).  */
+
+#define SHF_WRITE      (1 << 0)        /* Writable */
+#define SHF_ALLOC      (1 << 1)        /* Occupies memory during execution */
+#define SHF_EXECINSTR  (1 << 2)        /* Executable */
+#define SHF_MASKPROC   0xf0000000      /* Processor-specific */
+
+/* Symbol table entry.  */
+
+typedef struct
+{
+  Elf32_Word   st_name;                /* Symbol name (string tbl index) */
+  Elf32_Addr   st_value;               /* Symbol value */
+  Elf32_Word   st_size;                /* Symbol size */
+  unsigned char        st_info;                /* Symbol type and binding */
+  unsigned char        st_other;               /* No defined meaning, 0 */
+  Elf32_Section        st_shndx;               /* Section index */
+} Elf32_Sym;
+
+typedef struct
+{
+  Elf64_Word   st_name;                /* Symbol name (string tbl index) */
+  unsigned char        st_info;                /* Symbol type and binding */
+  unsigned char st_other;              /* No defined meaning, 0 */
+  Elf64_Section        st_shndx;               /* Section index */
+  Elf64_Addr   st_value;               /* Symbol value */
+  Elf64_Xword  st_size;                /* Symbol size */
+} Elf64_Sym;
+
+/* The syminfo section if available contains additional information about
+   every dynamic symbol.  */
+
+typedef struct
+{
+  Elf32_Half si_boundto;               /* Direct bindings, symbol bound to */
+  Elf32_Half si_flags;                 /* Per symbol flags */
+} Elf32_Syminfo;
+
+typedef struct
+{
+  Elf64_Half si_boundto;               /* Direct bindings, symbol bound to */
+  Elf64_Half si_flags;                 /* Per symbol flags */
+} Elf64_Syminfo;
+
+/* Possible values for si_boundto.  */
+#define SYMINFO_BT_SELF                0xffff  /* Symbol bound to self */
+#define SYMINFO_BT_PARENT      0xfffe  /* Symbol bound to parent */
+#define SYMINFO_BT_LOWRESERVE  0xff00  /* Beginning of reserved entries */
+
+/* Possible bitmasks for si_flags.  */
+#define SYMINFO_FLG_DIRECT     0x0001  /* Direct bound symbol */
+#define SYMINFO_FLG_PASSTHRU   0x0002  /* Pass-thru symbol for translator */
+#define SYMINFO_FLG_COPY       0x0004  /* Symbol is a copy-reloc */
+#define SYMINFO_FLG_LAZYLOAD   0x0008  /* Symbol bound to object to be lazy
+                                          loaded */
+/* Syminfo version values.  */
+#define SYMINFO_NONE           0
+#define SYMINFO_CURRENT                1
+#define SYMINFO_NUM            2
+
+
+/* Special section index.  */
+
+#define SHN_UNDEF      0               /* No section, undefined symbol.  */
+
+/* How to extract and insert information held in the st_info field.  */
+
+#define ELF32_ST_BIND(val)             (((unsigned char) (val)) >> 4)
+#define ELF32_ST_TYPE(val)             ((val) & 0xf)
+#define ELF32_ST_INFO(bind, type)      (((bind) << 4) + ((type) & 0xf))
+
+/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field.  */
+#define ELF64_ST_BIND(val)             ELF32_ST_BIND (val)
+#define ELF64_ST_TYPE(val)             ELF32_ST_TYPE (val)
+#define ELF64_ST_INFO(bind, type)      ELF32_ST_INFO ((bind), (type))
+
+/* Legal values for ST_BIND subfield of st_info (symbol binding).  */
+
+#define STB_LOCAL      0               /* Local symbol */
+#define STB_GLOBAL     1               /* Global symbol */
+#define STB_WEAK       2               /* Weak symbol */
+#define        STB_NUM         3               /* Number of defined types.  */
+#define STB_LOOS       10              /* Start of OS-specific */
+#define STB_HIOS       12              /* End of OS-specific */
+#define STB_LOPROC     13              /* Start of processor-specific */
+#define STB_HIPROC     15              /* End of processor-specific */
+
+/* Legal values for ST_TYPE subfield of st_info (symbol type).  */
+
+#define STT_NOTYPE     0               /* Symbol type is unspecified */
+#define STT_OBJECT     1               /* Symbol is a data object */
+#define STT_FUNC       2               /* Symbol is a code object */
+#define STT_SECTION    3               /* Symbol associated with a section */
+#define STT_FILE       4               /* Symbol's name is file name */
+#define        STT_NUM         5               /* Number of defined types.  */
+#define STT_LOOS       11              /* Start of OS-specific */
+#define STT_HIOS       12              /* End of OS-specific */
+#define STT_LOPROC     13              /* Start of processor-specific */
+#define STT_HIPROC     15              /* End of processor-specific */
+
+
+/* Symbol table indices are found in the hash buckets and chain table
+   of a symbol hash table section.  This special index value indicates
+   the end of a chain, meaning no further symbols are found in that bucket.  */
+
+#define STN_UNDEF      0               /* End of a chain.  */
+
+
+/* How to extract and insert information held in the st_other field.  */
+
+#define ELF32_ST_VISIBILITY(o) ((o) & 0x03)
+
+/* For ELF64 the definitions are the same.  */
+#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o)
+
+/* Symbol visibility specification encoded in the st_other field.  */
+#define STV_DEFAULT    0               /* Default symbol visibility rules */
+#define STV_INTERNAL   1               /* Processor specific hidden class */
+#define STV_HIDDEN     2               /* Sym unavailable in other modules */
+#define STV_PROTECTED  3               /* Not preemptible, not exported */
+
+
+/* Relocation table entry without addend (in section of type SHT_REL).  */
+
+typedef struct
+{
+  Elf32_Addr   r_offset;               /* Address */
+  Elf32_Word   r_info;                 /* Relocation type and symbol index */
+} Elf32_Rel;
+
+/* I have seen two different definitions of the Elf64_Rel and
+   Elf64_Rela structures, so we'll leave them out until Novell (or
+   whoever) gets their act together.  */
+/* The following, at least, is used on Sparc v9, MIPS, and Alpha.  */
+
+typedef struct
+{
+  Elf64_Addr   r_offset;               /* Address */
+  Elf64_Xword  r_info;                 /* Relocation type and symbol index */
+} Elf64_Rel;
+
+/* Relocation table entry with addend (in section of type SHT_RELA).  */
+
+typedef struct
+{
+  Elf32_Addr   r_offset;               /* Address */
+  Elf32_Word   r_info;                 /* Relocation type and symbol index */
+  Elf32_Sword  r_addend;               /* Addend */
+} Elf32_Rela;
+
+typedef struct
+{
+  Elf64_Addr   r_offset;               /* Address */
+  Elf64_Xword  r_info;                 /* Relocation type and symbol index */
+  Elf64_Sxword r_addend;               /* Addend */
+} Elf64_Rela;
+
+/* How to extract and insert information held in the r_info field.  */
+
+#define ELF32_R_SYM(val)               ((val) >> 8)
+#define ELF32_R_TYPE(val)              ((val) & 0xff)
+#define ELF32_R_INFO(sym, type)                (((sym) << 8) + ((type) & 0xff))
+
+#define ELF64_R_SYM(i)                 ((i) >> 32)
+#define ELF64_R_TYPE(i)                        ((i) & 0xffffffff)
+#define ELF64_R_INFO(sym,type)         (((sym) << 32) + (type))
+
+/* Program segment header.  */
+
+typedef struct
+{
+  Elf32_Word   p_type;                 /* Segment type */
+  Elf32_Off    p_offset;               /* Segment file offset */
+  Elf32_Addr   p_vaddr;                /* Segment virtual address */
+  Elf32_Addr   p_paddr;                /* Segment physical address */
+  Elf32_Word   p_filesz;               /* Segment size in file */
+  Elf32_Word   p_memsz;                /* Segment size in memory */
+  Elf32_Word   p_flags;                /* Segment flags */
+  Elf32_Word   p_align;                /* Segment alignment */
+} Elf32_Phdr;
+
+typedef struct
+{
+  Elf64_Word   p_type;                 /* Segment type */
+  Elf64_Word   p_flags;                /* Segment flags */
+  Elf64_Off    p_offset;               /* Segment file offset */
+  Elf64_Addr   p_vaddr;                /* Segment virtual address */
+  Elf64_Addr   p_paddr;                /* Segment physical address */
+  Elf64_Xword  p_filesz;               /* Segment size in file */
+  Elf64_Xword  p_memsz;                /* Segment size in memory */
+  Elf64_Xword  p_align;                /* Segment alignment */
+} Elf64_Phdr;
+
+/* Legal values for p_type (segment type).  */
+
+#define        PT_NULL         0               /* Program header table entry unused */
+#define PT_LOAD                1               /* Loadable program segment */
+#define PT_DYNAMIC     2               /* Dynamic linking information */
+#define PT_INTERP      3               /* Program interpreter */
+#define PT_NOTE                4               /* Auxiliary information */
+#define PT_SHLIB       5               /* Reserved */
+#define PT_PHDR                6               /* Entry for header table itself */
+#define        PT_NUM          7               /* Number of defined types.  */
+#define PT_LOOS                0x60000000      /* Start of OS-specific */
+#define PT_HIOS                0x6fffffff      /* End of OS-specific */
+#define PT_LOPROC      0x70000000      /* Start of processor-specific */
+#define PT_HIPROC      0x7fffffff      /* End of processor-specific */
+
+/* Legal values for p_flags (segment flags).  */
+
+#define PF_X           (1 << 0)        /* Segment is executable */
+#define PF_W           (1 << 1)        /* Segment is writable */
+#define PF_R           (1 << 2)        /* Segment is readable */
+#define PF_MASKPROC    0xf0000000      /* Processor-specific */
+
+/* Legal values for note segment descriptor types for core files. */
+
+#define NT_PRSTATUS    1               /* Contains copy of prstatus struct */
+#define NT_FPREGSET    2               /* Contains copy of fpregset struct */
+#define NT_PRPSINFO    3               /* Contains copy of prpsinfo struct */
+#define NT_PRXREG      4               /* Contains copy of prxregset struct */
+#define NT_PLATFORM    5               /* String from sysinfo(SI_PLATFORM) */
+#define NT_AUXV                6               /* Contains copy of auxv array */
+#define NT_GWINDOWS    7               /* Contains copy of gwindows struct */
+#define NT_PSTATUS     10              /* Contains copy of pstatus struct */
+#define NT_PSINFO      13              /* Contains copy of psinfo struct */
+#define NT_PRCRED      14              /* Contains copy of prcred struct */
+#define NT_UTSNAME     15              /* Contains copy of utsname struct */
+#define NT_LWPSTATUS   16              /* Contains copy of lwpstatus struct */
+#define NT_LWPSINFO    17              /* Contains copy of lwpinfo struct */
+
+/* Legal values for the  note segment descriptor types for object files.  */
+
+#define NT_VERSION     1               /* Contains a version string.  */
+
+
+/* Dynamic section entry.  */
+
+typedef struct
+{
+  Elf32_Sword  d_tag;                  /* Dynamic entry type */
+  union
+    {
+      Elf32_Word d_val;                        /* Integer value */
+      Elf32_Addr d_ptr;                        /* Address value */
+    } d_un;
+} Elf32_Dyn;
+
+typedef struct
+{
+  Elf64_Sxword d_tag;                  /* Dynamic entry type */
+  union
+    {
+      Elf64_Xword d_val;               /* Integer value */
+      Elf64_Addr d_ptr;                        /* Address value */
+    } d_un;
+} Elf64_Dyn;
+
+/* Legal values for d_tag (dynamic entry type).  */
+
+#define DT_NULL                0               /* Marks end of dynamic section */
+#define DT_NEEDED      1               /* Name of needed library */
+#define DT_PLTRELSZ    2               /* Size in bytes of PLT relocs */
+#define DT_PLTGOT      3               /* Processor defined value */
+#define DT_HASH                4               /* Address of symbol hash table */
+#define DT_STRTAB      5               /* Address of string table */
+#define DT_SYMTAB      6               /* Address of symbol table */
+#define DT_RELA                7               /* Address of Rela relocs */
+#define DT_RELASZ      8               /* Total size of Rela relocs */
+#define DT_RELAENT     9               /* Size of one Rela reloc */
+#define DT_STRSZ       10              /* Size of string table */
+#define DT_SYMENT      11              /* Size of one symbol table entry */
+#define DT_INIT                12              /* Address of init function */
+#define DT_FINI                13              /* Address of termination function */
+#define DT_SONAME      14              /* Name of shared object */
+#define DT_RPATH       15              /* Library search path */
+#define DT_SYMBOLIC    16              /* Start symbol search here */
+#define DT_REL         17              /* Address of Rel relocs */
+#define DT_RELSZ       18              /* Total size of Rel relocs */
+#define DT_RELENT      19              /* Size of one Rel reloc */
+#define DT_PLTREL      20              /* Type of reloc in PLT */
+#define DT_DEBUG       21              /* For debugging; unspecified */
+#define DT_TEXTREL     22              /* Reloc might modify .text */
+#define DT_JMPREL      23              /* Address of PLT relocs */
+#define        DT_BIND_NOW     24              /* Process relocations of object */
+#define        DT_INIT_ARRAY   25              /* Array with addresses of init fct */
+#define        DT_FINI_ARRAY   26              /* Array with addresses of fini fct */
+#define        DT_INIT_ARRAYSZ 27              /* Size in bytes of DT_INIT_ARRAY */
+#define        DT_FINI_ARRAYSZ 28              /* Size in bytes of DT_FINI_ARRAY */
+#define        DT_NUM          29              /* Number used */
+#define DT_LOOS                0x60000000      /* Start of OS-specific */
+#define DT_HIOS                0x6fffffff      /* End of OS-specific */
+#define DT_LOPROC      0x70000000      /* Start of processor-specific */
+#define DT_HIPROC      0x7fffffff      /* End of processor-specific */
+#define        DT_PROCNUM      DT_MIPS_NUM     /* Most used by any processor */
+
+/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the
+   Dyn.d_un.d_val field of the Elf*_Dyn structure.  This follows Sun's
+   approach.  */
+#define DT_VALRNGLO    0x6ffffd00
+#define DT_POSFLAG_1   0x6ffffdfd      /* Flags for DT_* entries, effecting
+                                          the following DT_* entry.  */
+#define DT_SYMINSZ     0x6ffffdfe      /* Size of syminfo table (in bytes) */
+#define DT_SYMINENT    0x6ffffdff      /* Entry size of syminfo */
+#define DT_VALRNGHI    0x6ffffdff
+
+/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the
+   Dyn.d_un.d_ptr field of the Elf*_Dyn structure.
+
+   If any adjustment is made to the ELF object after it has been
+   built these entries will need to be adjusted.  */
+#define DT_ADDRRNGLO   0x6ffffe00
+#define DT_SYMINFO     0x6ffffeff      /* syminfo table */
+#define DT_ADDRRNGHI   0x6ffffeff
+
+/* The versioning entry types.  The next are defined as part of the
+   GNU extension.  */
+#define DT_VERSYM      0x6ffffff0
+
+/* These were chosen by Sun.  */
+#define DT_FLAGS_1     0x6ffffffb      /* State flags, see DF_1_* below.  */
+#define        DT_VERDEF       0x6ffffffc      /* Address of version definition
+                                          table */
+#define        DT_VERDEFNUM    0x6ffffffd      /* Number of version definitions */
+#define        DT_VERNEED      0x6ffffffe      /* Address of table with needed
+                                          versions */
+#define        DT_VERNEEDNUM   0x6fffffff      /* Number of needed versions */
+#define DT_VERSIONTAGIDX(tag)  (DT_VERNEEDNUM - (tag)) /* Reverse order! */
+#define DT_VERSIONTAGNUM 16
+
+/* Sun added these machine-independent extensions in the "processor-specific"
+   range.  Be compatible.  */
+#define DT_AUXILIARY    0x7ffffffd      /* Shared object to load before self */
+#define DT_FILTER       0x7fffffff      /* Shared object to get values from */
+#define DT_EXTRATAGIDX(tag)    ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1)
+#define DT_EXTRANUM    3
+
+/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1
+   entry in the dynamic section.  */
+#define DF_1_NOW       0x00000001      /* Set RTLD_NOW for this object.  */
+#define DF_1_GLOBAL    0x00000002      /* Set RTLD_GLOBAL for this object.  */
+#define DF_1_GROUP     0x00000004      /* Set RTLD_GROUP for this object.  */
+#define DF_1_NODELETE  0x00000008      /* Set RTLD_NODELETE for this object.*/
+#define DF_1_LOADFLTR  0x00000010      /* Trigger filtee loading at runtime.*/
+#define DF_1_INITFIRST 0x00000020      /* Set RTLD_INITFIRST for this object*/
+#define DF_1_NOOPEN    0x00000040      /* Set RTLD_NOOPEN for this object.  */
+
+/* Version definition sections.  */
+
+typedef struct
+{
+  Elf32_Half   vd_version;             /* Version revision */
+  Elf32_Half   vd_flags;               /* Version information */
+  Elf32_Half   vd_ndx;                 /* Version Index */
+  Elf32_Half   vd_cnt;                 /* Number of associated aux entries */
+  Elf32_Word   vd_hash;                /* Version name hash value */
+  Elf32_Word   vd_aux;                 /* Offset in bytes to verdaux array */
+  Elf32_Word   vd_next;                /* Offset in bytes to next verdef
+                                          entry */
+} Elf32_Verdef;
+
+typedef struct
+{
+  Elf64_Half   vd_version;             /* Version revision */
+  Elf64_Half   vd_flags;               /* Version information */
+  Elf64_Half   vd_ndx;                 /* Version Index */
+  Elf64_Half   vd_cnt;                 /* Number of associated aux entries */
+  Elf64_Word   vd_hash;                /* Version name hash value */
+  Elf64_Word   vd_aux;                 /* Offset in bytes to verdaux array */
+  Elf64_Word   vd_next;                /* Offset in bytes to next verdef
+                                          entry */
+} Elf64_Verdef;
+
+
+/* Legal values for vd_version (version revision).  */
+#define VER_DEF_NONE   0               /* No version */
+#define VER_DEF_CURRENT        1               /* Current version */
+#define VER_DEF_NUM    2               /* Given version number */
+
+/* Legal values for vd_flags (version information flags).  */
+#define VER_FLG_BASE   0x1             /* Version definition of file itself */
+#define VER_FLG_WEAK   0x2             /* Weak version identifier */
+
+/* Auxialiary version information.  */
+
+typedef struct
+{
+  Elf32_Word   vda_name;               /* Version or dependency names */
+  Elf32_Word   vda_next;               /* Offset in bytes to next verdaux
+                                          entry */
+} Elf32_Verdaux;
+
+typedef struct
+{
+  Elf64_Word   vda_name;               /* Version or dependency names */
+  Elf64_Word   vda_next;               /* Offset in bytes to next verdaux
+                                          entry */
+} Elf64_Verdaux;
+
+
+/* Version dependency section.  */
+
+typedef struct
+{
+  Elf32_Half   vn_version;             /* Version of structure */
+  Elf32_Half   vn_cnt;                 /* Number of associated aux entries */
+  Elf32_Word   vn_file;                /* Offset of filename for this
+                                          dependency */
+  Elf32_Word   vn_aux;                 /* Offset in bytes to vernaux array */
+  Elf32_Word   vn_next;                /* Offset in bytes to next verneed
+                                          entry */
+} Elf32_Verneed;
+
+typedef struct
+{
+  Elf64_Half   vn_version;             /* Version of structure */
+  Elf64_Half   vn_cnt;                 /* Number of associated aux entries */
+  Elf64_Word   vn_file;                /* Offset of filename for this
+                                          dependency */
+  Elf64_Word   vn_aux;                 /* Offset in bytes to vernaux array */
+  Elf64_Word   vn_next;                /* Offset in bytes to next verneed
+                                          entry */
+} Elf64_Verneed;
+
+
+/* Legal values for vn_version (version revision).  */
+#define VER_NEED_NONE   0              /* No version */
+#define VER_NEED_CURRENT 1             /* Current version */
+#define VER_NEED_NUM    2              /* Given version number */
+
+/* Auxiliary needed version information.  */
+
+typedef struct
+{
+  Elf32_Word   vna_hash;               /* Hash value of dependency name */
+  Elf32_Half   vna_flags;              /* Dependency specific information */
+  Elf32_Half   vna_other;              /* Unused */
+  Elf32_Word   vna_name;               /* Dependency name string offset */
+  Elf32_Word   vna_next;               /* Offset in bytes to next vernaux
+                                          entry */
+} Elf32_Vernaux;
+
+typedef struct
+{
+  Elf64_Word   vna_hash;               /* Hash value of dependency name */
+  Elf64_Half   vna_flags;              /* Dependency specific information */
+  Elf64_Half   vna_other;              /* Unused */
+  Elf64_Word   vna_name;               /* Dependency name string offset */
+  Elf64_Word   vna_next;               /* Offset in bytes to next vernaux
+                                          entry */
+} Elf64_Vernaux;
+
+
+/* Legal values for vna_flags.  */
+#define VER_FLG_WEAK   0x2             /* Weak version identifier */
+
+
+/* Auxiliary vector.  */
+
+/* This vector is normally only used by the program interpreter.  The
+   usual definition in an ABI supplement uses the name auxv_t.  The
+   vector is not usually defined in a standard <elf.h> file, but it
+   can't hurt.  We rename it to avoid conflicts.  The sizes of these
+   types are an arrangement between the exec server and the program
+   interpreter, so we don't fully specify them here.  */
+
+typedef struct
+{
+  int a_type;                  /* Entry type */
+  union
+    {
+      long int a_val;          /* Integer value */
+      void *a_ptr;             /* Pointer value */
+      void (*a_fcn) (void);    /* Function pointer value */
+    } a_un;
+} Elf32_auxv_t;
+
+typedef struct
+{
+  long int a_type;             /* Entry type */
+  union
+    {
+      long int a_val;          /* Integer value */
+      void *a_ptr;             /* Pointer value */
+      void (*a_fcn) (void);    /* Function pointer value */
+    } a_un;
+} Elf64_auxv_t;
+
+/* Legal values for a_type (entry type).  */
+
+#define AT_NULL                0               /* End of vector */
+#define AT_IGNORE      1               /* Entry should be ignored */
+#define AT_EXECFD      2               /* File descriptor of program */
+#define AT_PHDR                3               /* Program headers for program */
+#define AT_PHENT       4               /* Size of program header entry */
+#define AT_PHNUM       5               /* Number of program headers */
+#define AT_PAGESZ      6               /* System page size */
+#define AT_BASE                7               /* Base address of interpreter */
+#define AT_FLAGS       8               /* Flags */
+#define AT_ENTRY       9               /* Entry point of program */
+#define AT_NOTELF      10              /* Program is not ELF */
+#define AT_UID         11              /* Real uid */
+#define AT_EUID                12              /* Effective uid */
+#define AT_GID         13              /* Real gid */
+#define AT_EGID                14              /* Effective gid */
+
+/* Some more special a_type values describing the hardware.  */
+#define AT_PLATFORM    15              /* String identifying platform.  */
+#define AT_HWCAP       16              /* Machine dependent hints about
+                                          processor capabilities.  */
+
+/* This entry gives some information about the FPU initialization
+   performed by the kernel.  */
+#define AT_FPUCW       17              /* Used FPU control word.  */
+
+
+/* Note section contents.  Each entry in the note section begins with
+   a header of a fixed form.  */
+
+typedef struct
+{
+  Elf32_Word n_namesz;                 /* Length of the note's name.  */
+  Elf32_Word n_descsz;                 /* Length of the note's descriptor.  */
+  Elf32_Word n_type;                   /* Type of the note.  */
+} Elf32_Nhdr;
+
+typedef struct
+{
+  Elf64_Word n_namesz;                 /* Length of the note's name.  */
+  Elf64_Word n_descsz;                 /* Length of the note's descriptor.  */
+  Elf64_Word n_type;                   /* Type of the note.  */
+} Elf64_Nhdr;
+
+/* Known names of notes.  */
+
+/* Solaris entries in the note section have this name.  */
+#define ELF_NOTE_SOLARIS       "SUNW Solaris"
+
+/* Note entries for GNU systems have this name.  */
+#define ELF_NOTE_GNU           "GNU"
+
+
+/* Defined types of notes for Solaris.  */
+
+/* Value of descriptor (one word) is desired pagesize for the binary.  */
+#define ELF_NOTE_PAGESIZE_HINT 1
+
+
+/* Defined note types for GNU systems.  */
+
+/* ABI information.  The descriptor consists of words:
+   word 0: OS descriptor
+   word 1: major version of the ABI
+   word 2: minor version of the ABI
+   word 3: subminor version of the ABI
+*/
+#define ELF_NOTE_ABI           1
+
+/* Known OSes.  These value can appear in word 0 of an ELF_NOTE_ABI
+   note section entry.  */
+#define ELF_NOTE_OS_LINUX      0
+#define ELF_NOTE_OS_GNU                1
+#define ELF_NOTE_OS_SOLARIS2   2
+
+
+/* Motorola 68k specific definitions.  */
+
+/* m68k relocs.  */
+
+#define R_68K_NONE     0               /* No reloc */
+#define R_68K_32       1               /* Direct 32 bit  */
+#define R_68K_16       2               /* Direct 16 bit  */
+#define R_68K_8                3               /* Direct 8 bit  */
+#define R_68K_PC32     4               /* PC relative 32 bit */
+#define R_68K_PC16     5               /* PC relative 16 bit */
+#define R_68K_PC8      6               /* PC relative 8 bit */
+#define R_68K_GOT32    7               /* 32 bit PC relative GOT entry */
+#define R_68K_GOT16    8               /* 16 bit PC relative GOT entry */
+#define R_68K_GOT8     9               /* 8 bit PC relative GOT entry */
+#define R_68K_GOT32O   10              /* 32 bit GOT offset */
+#define R_68K_GOT16O   11              /* 16 bit GOT offset */
+#define R_68K_GOT8O    12              /* 8 bit GOT offset */
+#define R_68K_PLT32    13              /* 32 bit PC relative PLT address */
+#define R_68K_PLT16    14              /* 16 bit PC relative PLT address */
+#define R_68K_PLT8     15              /* 8 bit PC relative PLT address */
+#define R_68K_PLT32O   16              /* 32 bit PLT offset */
+#define R_68K_PLT16O   17              /* 16 bit PLT offset */
+#define R_68K_PLT8O    18              /* 8 bit PLT offset */
+#define R_68K_COPY     19              /* Copy symbol at runtime */
+#define R_68K_GLOB_DAT 20              /* Create GOT entry */
+#define R_68K_JMP_SLOT 21              /* Create PLT entry */
+#define R_68K_RELATIVE 22              /* Adjust by program base */
+/* Keep this the last entry.  */
+#define R_68K_NUM      23
+
+/* Intel 80386 specific definitions.  */
+
+/* i386 relocs.  */
+
+#define R_386_NONE     0               /* No reloc */
+#define R_386_32       1               /* Direct 32 bit  */
+#define R_386_PC32     2               /* PC relative 32 bit */
+#define R_386_GOT32    3               /* 32 bit GOT entry */
+#define R_386_PLT32    4               /* 32 bit PLT address */
+#define R_386_COPY     5               /* Copy symbol at runtime */
+#define R_386_GLOB_DAT 6               /* Create GOT entry */
+#define R_386_JMP_SLOT 7               /* Create PLT entry */
+#define R_386_RELATIVE 8               /* Adjust by program base */
+#define R_386_GOTOFF   9               /* 32 bit offset to GOT */
+#define R_386_GOTPC    10              /* 32 bit PC relative offset to GOT */
+/* Keep this the last entry.  */
+#define R_386_NUM      11
+
+/* SUN SPARC specific definitions.  */
+
+/* Values for Elf64_Ehdr.e_flags.  */
+
+#define EF_SPARCV9_MM          3
+#define EF_SPARCV9_TSO         0
+#define EF_SPARCV9_PSO         1
+#define EF_SPARCV9_RMO         2
+#define EF_SPARC_EXT_MASK      0xFFFF00
+#define EF_SPARC_SUN_US1       0x000200
+#define EF_SPARC_HAL_R1                0x000400
+
+/* SPARC relocs.  */
+
+#define R_SPARC_NONE   0               /* No reloc */
+#define R_SPARC_8      1               /* Direct 8 bit */
+#define R_SPARC_16     2               /* Direct 16 bit */
+#define R_SPARC_32     3               /* Direct 32 bit */
+#define R_SPARC_DISP8  4               /* PC relative 8 bit */
+#define R_SPARC_DISP16 5               /* PC relative 16 bit */
+#define R_SPARC_DISP32 6               /* PC relative 32 bit */
+#define R_SPARC_WDISP30        7               /* PC relative 30 bit shifted */
+#define R_SPARC_WDISP22        8               /* PC relative 22 bit shifted */
+#define R_SPARC_HI22   9               /* High 22 bit */
+#define R_SPARC_22     10              /* Direct 22 bit */
+#define R_SPARC_13     11              /* Direct 13 bit */
+#define R_SPARC_LO10   12              /* Truncated 10 bit */
+#define R_SPARC_GOT10  13              /* Truncated 10 bit GOT entry */
+#define R_SPARC_GOT13  14              /* 13 bit GOT entry */
+#define R_SPARC_GOT22  15              /* 22 bit GOT entry shifted */
+#define R_SPARC_PC10   16              /* PC relative 10 bit truncated */
+#define R_SPARC_PC22   17              /* PC relative 22 bit shifted */
+#define R_SPARC_WPLT30 18              /* 30 bit PC relative PLT address */
+#define R_SPARC_COPY   19              /* Copy symbol at runtime */
+#define R_SPARC_GLOB_DAT 20            /* Create GOT entry */
+#define R_SPARC_JMP_SLOT 21            /* Create PLT entry */
+#define R_SPARC_RELATIVE 22            /* Adjust by program base */
+#define R_SPARC_UA32   23              /* Direct 32 bit unaligned */
+
+/* Additional Sparc64 relocs.  */
+
+#define R_SPARC_PLT32  24              /* Direct 32 bit ref to PLT entry */
+#define R_SPARC_HIPLT22        25              /* High 22 bit PLT entry */
+#define R_SPARC_LOPLT10        26              /* Truncated 10 bit PLT entry */
+#define R_SPARC_PCPLT32        27              /* PC rel 32 bit ref to PLT entry */
+#define R_SPARC_PCPLT22        28              /* PC rel high 22 bit PLT entry */
+#define R_SPARC_PCPLT10        29              /* PC rel trunc 10 bit PLT entry */
+#define R_SPARC_10     30              /* Direct 10 bit */
+#define R_SPARC_11     31              /* Direct 11 bit */
+#define R_SPARC_64     32              /* Direct 64 bit */
+#define R_SPARC_OLO10  33              /* ?? */
+#define R_SPARC_HH22   34              /* Top 22 bits of direct 64 bit */
+#define R_SPARC_HM10   35              /* High middle 10 bits of ... */
+#define R_SPARC_LM22   36              /* Low middle 22 bits of ... */
+#define R_SPARC_PC_HH22        37              /* Top 22 bits of pc rel 64 bit */
+#define R_SPARC_PC_HM10        38              /* High middle 10 bit of ... */
+#define R_SPARC_PC_LM22        39              /* Low miggle 22 bits of ... */
+#define R_SPARC_WDISP16        40              /* PC relative 16 bit shifted */
+#define R_SPARC_WDISP19        41              /* PC relative 19 bit shifted */
+#define R_SPARC_7      43              /* Direct 7 bit */
+#define R_SPARC_5      44              /* Direct 5 bit */
+#define R_SPARC_6      45              /* Direct 6 bit */
+#define R_SPARC_DISP64 46              /* PC relative 64 bit */
+#define R_SPARC_PLT64  47              /* Direct 64 bit ref to PLT entry */
+#define R_SPARC_HIX22  48              /* High 22 bit complemented */
+#define R_SPARC_LOX10  49              /* Truncated 11 bit complemented */
+#define R_SPARC_H44    50              /* Direct high 12 of 44 bit */
+#define R_SPARC_M44    51              /* Direct mid 22 of 44 bit */
+#define R_SPARC_L44    52              /* Direct low 10 of 44 bit */
+#define R_SPARC_REGISTER 53            /* Global register usage */
+#define R_SPARC_UA64   54              /* Direct 64 bit unaligned */
+#define R_SPARC_UA16   55              /* Direct 16 bit unaligned */
+/* Keep this the last entry.  */
+#define R_SPARC_NUM    56
+
+/* For Sparc64, legal values for d_tag of Elf64_Dyn.  */
+
+#define DT_SPARC_REGISTER 0x70000001
+#define DT_SPARC_NUM   2
+
+/* Bits present in AT_HWCAP, primarily for Sparc32.  */
+
+#define HWCAP_SPARC_FLUSH      1       /* The cpu supports flush insn.  */
+#define HWCAP_SPARC_STBAR      2
+#define HWCAP_SPARC_SWAP       4
+#define HWCAP_SPARC_MULDIV     8
+#define HWCAP_SPARC_V9         16      /* The cpu is v9, so v8plus is ok.  */
+
+/* MIPS R3000 specific definitions.  */
+
+/* Legal values for e_flags field of Elf32_Ehdr.  */
+
+#define EF_MIPS_NOREORDER   1          /* A .noreorder directive was used */
+#define EF_MIPS_PIC        2           /* Contains PIC code */
+#define EF_MIPS_CPIC       4           /* Uses PIC calling sequence */
+#define EF_MIPS_XGOT       8
+#define EF_MIPS_64BIT_WHIRL 16
+#define EF_MIPS_ABI2       32
+#define EF_MIPS_ABI_ON32    64
+#define EF_MIPS_ARCH       0xf0000000  /* MIPS architecture level */
+
+/* Legal values for MIPS architecture level.  */
+
+#define EF_MIPS_ARCH_1     0x00000000  /* -mips1 code.  */
+#define EF_MIPS_ARCH_2     0x10000000  /* -mips2 code.  */
+#define EF_MIPS_ARCH_3     0x20000000  /* -mips3 code.  */
+#define EF_MIPS_ARCH_4     0x30000000  /* -mips4 code.  */
+#define EF_MIPS_ARCH_5     0x40000000  /* -mips5 code.  */
+
+/* The following are non-official names and should not be used.  */
+
+#define E_MIPS_ARCH_1    0x00000000    /* -mips1 code.  */
+#define E_MIPS_ARCH_2    0x10000000    /* -mips2 code.  */
+#define E_MIPS_ARCH_3    0x20000000    /* -mips3 code.  */
+#define E_MIPS_ARCH_4    0x30000000    /* -mips4 code.  */
+#define E_MIPS_ARCH_5    0x40000000    /* -mips5 code.  */
+
+/* Special section indices.  */
+
+#define SHN_MIPS_ACOMMON 0xff00                /* Allocated common symbols */
+#define SHN_MIPS_TEXT   0xff01         /* Allocated test symbols.  */
+#define SHN_MIPS_DATA   0xff02         /* Allocated data symbols.  */
+#define SHN_MIPS_SCOMMON 0xff03                /* Small common symbols */
+#define SHN_MIPS_SUNDEFINED 0xff04     /* Small undefined symbols */
+
+/* Legal values for sh_type field of Elf32_Shdr.  */
+
+#define SHT_MIPS_LIBLIST       0x70000000 /* Shared objects used in link */
+#define SHT_MIPS_MSYM         0x70000001
+#define SHT_MIPS_CONFLICT      0x70000002 /* Conflicting symbols */
+#define SHT_MIPS_GPTAB        0x70000003 /* Global data area sizes */
+#define SHT_MIPS_UCODE        0x70000004 /* Reserved for SGI/MIPS compilers */
+#define SHT_MIPS_DEBUG        0x70000005 /* MIPS ECOFF debugging information*/
+#define SHT_MIPS_REGINFO       0x70000006 /* Register usage information */
+#define SHT_MIPS_PACKAGE       0x70000007
+#define SHT_MIPS_PACKSYM       0x70000008
+#define SHT_MIPS_RELD         0x70000009
+#define SHT_MIPS_IFACE         0x7000000b
+#define SHT_MIPS_CONTENT       0x7000000c
+#define SHT_MIPS_OPTIONS       0x7000000d /* Miscellaneous options.  */
+#define SHT_MIPS_SHDR         0x70000010
+#define SHT_MIPS_FDESC        0x70000011
+#define SHT_MIPS_EXTSYM               0x70000012
+#define SHT_MIPS_DENSE        0x70000013
+#define SHT_MIPS_PDESC        0x70000014
+#define SHT_MIPS_LOCSYM               0x70000015
+#define SHT_MIPS_AUXSYM               0x70000016
+#define SHT_MIPS_OPTSYM               0x70000017
+#define SHT_MIPS_LOCSTR               0x70000018
+#define SHT_MIPS_LINE         0x70000019
+#define SHT_MIPS_RFDESC               0x7000001a
+#define SHT_MIPS_DELTASYM      0x7000001b
+#define SHT_MIPS_DELTAINST     0x7000001c
+#define SHT_MIPS_DELTACLASS    0x7000001d
+#define SHT_MIPS_DWARF         0x7000001e /* DWARF debugging information.  */
+#define SHT_MIPS_DELTADECL     0x7000001f
+#define SHT_MIPS_SYMBOL_LIB    0x70000020
+#define SHT_MIPS_EVENTS               0x70000021 /* Event section.  */
+#define SHT_MIPS_TRANSLATE     0x70000022
+#define SHT_MIPS_PIXIE        0x70000023
+#define SHT_MIPS_XLATE        0x70000024
+#define SHT_MIPS_XLATE_DEBUG   0x70000025
+#define SHT_MIPS_WHIRL        0x70000026
+#define SHT_MIPS_EH_REGION     0x70000027
+#define SHT_MIPS_XLATE_OLD     0x70000028
+#define SHT_MIPS_PDR_EXCEPTION 0x70000029
+
+/* Legal values for sh_flags field of Elf32_Shdr.  */
+
+#define SHF_MIPS_GPREL  0x10000000     /* Must be part of global data area */
+#define SHF_MIPS_MERGE  0x20000000
+#define SHF_MIPS_ADDR   0x40000000
+#define SHF_MIPS_STRINGS 0x80000000
+#define SHF_MIPS_NOSTRIP 0x08000000
+#define SHF_MIPS_LOCAL  0x04000000
+#define SHF_MIPS_NAMES  0x02000000
+#define SHF_MIPS_NODUPE         0x01000000
+
+
+/* Symbol tables.  */
+
+/* MIPS specific values for `st_other'.  */
+#define STO_MIPS_DEFAULT               0x0
+#define STO_MIPS_INTERNAL              0x1
+#define STO_MIPS_HIDDEN                        0x2
+#define STO_MIPS_PROTECTED             0x3
+#define STO_MIPS_SC_ALIGN_UNUSED       0xff
+
+/* MIPS specific values for `st_info'.  */
+#define STB_MIPS_SPLIT_COMMON          13
+
+/* Entries found in sections of type SHT_MIPS_GPTAB.  */
+
+typedef union
+{
+  struct
+    {
+      Elf32_Word gt_current_g_value;   /* -G value used for compilation */
+      Elf32_Word gt_unused;            /* Not used */
+    } gt_header;                       /* First entry in section */
+  struct
+    {
+      Elf32_Word gt_g_value;           /* If this value were used for -G */
+      Elf32_Word gt_bytes;             /* This many bytes would be used */
+    } gt_entry;                                /* Subsequent entries in section */
+} Elf32_gptab;
+
+/* Entry found in sections of type SHT_MIPS_REGINFO.  */
+
+typedef struct
+{
+  Elf32_Word   ri_gprmask;             /* General registers used */
+  Elf32_Word   ri_cprmask[4];          /* Coprocessor registers used */
+  Elf32_Sword  ri_gp_value;            /* $gp register value */
+} Elf32_RegInfo;
+
+/* Entries found in sections of type SHT_MIPS_OPTIONS.  */
+
+typedef struct
+{
+  unsigned char kind;          /* Determines interpretation of the
+                                  variable part of descriptor.  */
+  unsigned char size;          /* Size of descriptor, including header.  */
+  Elf32_Section section;       /* Section header index of section affected,
+                                  0 for global options.  */
+  Elf32_Word info;             /* Kind-specific information.  */
+} Elf_Options;
+
+/* Values for `kind' field in Elf_Options.  */
+
+#define ODK_NULL       0       /* Undefined.  */
+#define ODK_REGINFO    1       /* Register usage information.  */
+#define ODK_EXCEPTIONS 2       /* Exception processing options.  */
+#define ODK_PAD                3       /* Section padding options.  */
+#define ODK_HWPATCH    4       /* Hardware workarounds performed */
+#define ODK_FILL       5       /* record the fill value used by the linker. */
+#define ODK_TAGS       6       /* reserve space for desktop tools to write. */
+#define ODK_HWAND      7       /* HW workarounds.  'AND' bits when merging. */
+#define ODK_HWOR       8       /* HW workarounds.  'OR' bits when merging.  */
+
+/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries.  */
+
+#define OEX_FPU_MIN    0x1f    /* FPE's which MUST be enabled.  */
+#define OEX_FPU_MAX    0x1f00  /* FPE's which MAY be enabled.  */
+#define OEX_PAGE0      0x10000 /* page zero must be mapped.  */
+#define OEX_SMM                0x20000 /* Force sequential memory mode?  */
+#define OEX_FPDBUG     0x40000 /* Force floating point debug mode?  */
+#define OEX_PRECISEFP  OEX_FPDBUG
+#define OEX_DISMISS    0x80000 /* Dismiss invalid address faults?  */
+
+#define OEX_FPU_INVAL  0x10
+#define OEX_FPU_DIV0   0x08
+#define OEX_FPU_OFLO   0x04
+#define OEX_FPU_UFLO   0x02
+#define OEX_FPU_INEX   0x01
+
+/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry.  */
+
+#define OHW_R4KEOP     0x1     /* R4000 end-of-page patch.  */
+#define OHW_R8KPFETCH  0x2     /* may need R8000 prefetch patch.  */
+#define OHW_R5KEOP     0x4     /* R5000 end-of-page patch.  */
+#define OHW_R5KCVTL    0x8     /* R5000 cvt.[ds].l bug.  clean=1.  */
+
+#define OPAD_PREFIX    0x1
+#define OPAD_POSTFIX   0x2
+#define OPAD_SYMBOL    0x4
+
+/* Entry found in `.options' section.  */
+
+typedef struct
+{
+  Elf32_Word hwp_flags1;       /* Extra flags.  */
+  Elf32_Word hwp_flags2;       /* Extra flags.  */
+} Elf_Options_Hw;
+
+/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries.  */
+
+#define OHWA0_R4KEOP_CHECKED   0x00000001
+#define OHWA1_R4KEOP_CLEAN     0x00000002
+
+/* MIPS relocs.  */
+
+#define R_MIPS_NONE            0       /* No reloc */
+#define R_MIPS_16              1       /* Direct 16 bit */
+#define R_MIPS_32              2       /* Direct 32 bit */
+#define R_MIPS_REL32           3       /* PC relative 32 bit */
+#define R_MIPS_26              4       /* Direct 26 bit shifted */
+#define R_MIPS_HI16            5       /* High 16 bit */
+#define R_MIPS_LO16            6       /* Low 16 bit */
+#define R_MIPS_GPREL16         7       /* GP relative 16 bit */
+#define R_MIPS_LITERAL         8       /* 16 bit literal entry */
+#define R_MIPS_GOT16           9       /* 16 bit GOT entry */
+#define R_MIPS_PC16            10      /* PC relative 16 bit */
+#define R_MIPS_CALL16          11      /* 16 bit GOT entry for function */
+#define R_MIPS_GPREL32         12      /* GP relative 32 bit */
+
+#define R_MIPS_SHIFT5          16
+#define R_MIPS_SHIFT6          17
+#define R_MIPS_64              18
+#define R_MIPS_GOT_DISP                19
+#define R_MIPS_GOT_PAGE                20
+#define R_MIPS_GOT_OFST                21
+#define R_MIPS_GOT_HI16                22
+#define R_MIPS_GOT_LO16                23
+#define R_MIPS_SUB             24
+#define R_MIPS_INSERT_A                25
+#define R_MIPS_INSERT_B                26
+#define R_MIPS_DELETE          27
+#define R_MIPS_HIGHER          28
+#define R_MIPS_HIGHEST         29
+#define R_MIPS_CALL_HI16       30
+#define R_MIPS_CALL_LO16       31
+#define R_MIPS_SCN_DISP                32
+#define R_MIPS_REL16           33
+#define R_MIPS_ADD_IMMEDIATE   34
+#define R_MIPS_PJUMP           35
+#define R_MIPS_RELGOT          36
+#define R_MIPS_JALR            37
+/* Keep this the last entry.  */
+#define R_MIPS_NUM             38
+
+/* Legal values for p_type field of Elf32_Phdr.  */
+
+#define PT_MIPS_REGINFO        0x70000000      /* Register usage information */
+#define PT_MIPS_RTPROC  0x70000001     /* Runtime procedure table. */
+#define PT_MIPS_OPTIONS 0x70000002
+
+/* Special program header types.  */
+
+#define PF_MIPS_LOCAL  0x10000000
+
+/* Legal values for d_tag field of Elf32_Dyn.  */
+
+#define DT_MIPS_RLD_VERSION  0x70000001        /* Runtime linker interface version */
+#define DT_MIPS_TIME_STAMP   0x70000002        /* Timestamp */
+#define DT_MIPS_ICHECKSUM    0x70000003        /* Checksum */
+#define DT_MIPS_IVERSION     0x70000004        /* Version string (string tbl index) */
+#define DT_MIPS_FLAGS       0x70000005 /* Flags */
+#define DT_MIPS_BASE_ADDRESS 0x70000006        /* Base address */
+#define DT_MIPS_MSYM        0x70000007
+#define DT_MIPS_CONFLICT     0x70000008        /* Address of CONFLICT section */
+#define DT_MIPS_LIBLIST             0x70000009 /* Address of LIBLIST section */
+#define DT_MIPS_LOCAL_GOTNO  0x7000000a        /* Number of local GOT entries */
+#define DT_MIPS_CONFLICTNO   0x7000000b        /* Number of CONFLICT entries */
+#define DT_MIPS_LIBLISTNO    0x70000010        /* Number of LIBLIST entries */
+#define DT_MIPS_SYMTABNO     0x70000011        /* Number of DYNSYM entries */
+#define DT_MIPS_UNREFEXTNO   0x70000012        /* First external DYNSYM */
+#define DT_MIPS_GOTSYM      0x70000013 /* First GOT entry in DYNSYM */
+#define DT_MIPS_HIPAGENO     0x70000014        /* Number of GOT page table entries */
+#define DT_MIPS_RLD_MAP             0x70000016 /* Address of run time loader map.  */
+#define DT_MIPS_DELTA_CLASS  0x70000017        /* Delta C++ class definition.  */
+#define DT_MIPS_DELTA_CLASS_NO    0x70000018 /* Number of entries in
+                                               DT_MIPS_DELTA_CLASS.  */
+#define DT_MIPS_DELTA_INSTANCE    0x70000019 /* Delta C++ class instances.  */
+#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in
+                                               DT_MIPS_DELTA_INSTANCE.  */
+#define DT_MIPS_DELTA_RELOC  0x7000001b /* Delta relocations.  */
+#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in
+                                            DT_MIPS_DELTA_RELOC.  */
+#define DT_MIPS_DELTA_SYM    0x7000001d /* Delta symbols that Delta
+                                          relocations refer to.  */
+#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in
+                                          DT_MIPS_DELTA_SYM.  */
+#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the
+                                            class declaration.  */
+#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in
+                                               DT_MIPS_DELTA_CLASSSYM.  */
+#define DT_MIPS_CXX_FLAGS    0x70000022 /* Flags indicating for C++ flavor.  */
+#define DT_MIPS_PIXIE_INIT   0x70000023
+#define DT_MIPS_SYMBOL_LIB   0x70000024
+#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025
+#define DT_MIPS_LOCAL_GOTIDX 0x70000026
+#define DT_MIPS_HIDDEN_GOTIDX 0x70000027
+#define DT_MIPS_PROTECTED_GOTIDX 0x70000028
+#define DT_MIPS_OPTIONS             0x70000029 /* Address of .options.  */
+#define DT_MIPS_INTERFACE    0x7000002a /* Address of .interface.  */
+#define DT_MIPS_DYNSTR_ALIGN 0x7000002b
+#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */
+#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve
+                                                   function stored in GOT.  */
+#define DT_MIPS_PERF_SUFFIX  0x7000002e /* Default suffix of dso to be added
+                                          by rld on dlopen() calls.  */
+#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */
+#define DT_MIPS_GP_VALUE     0x70000030 /* GP value for aux GOTs.  */
+#define DT_MIPS_AUX_DYNAMIC  0x70000031 /* Address of aux .dynamic.  */
+#define DT_MIPS_NUM         0x32
+
+/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry.  */
+
+#define RHF_NONE                  0            /* No flags */
+#define RHF_QUICKSTART            (1 << 0)     /* Use quickstart */
+#define RHF_NOTPOT                (1 << 1)     /* Hash size not power of 2 */
+#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2)    /* Ignore LD_LIBRARY_PATH */
+#define RHF_NO_MOVE               (1 << 3)
+#define RHF_SGI_ONLY              (1 << 4)
+#define RHF_GUARANTEE_INIT        (1 << 5)
+#define RHF_DELTA_C_PLUS_PLUS     (1 << 6)
+#define RHF_GUARANTEE_START_INIT   (1 << 7)
+#define RHF_PIXIE                 (1 << 8)
+#define RHF_DEFAULT_DELAY_LOAD    (1 << 9)
+#define RHF_REQUICKSTART          (1 << 10)
+#define RHF_REQUICKSTARTED        (1 << 11)
+#define RHF_CORD                  (1 << 12)
+#define RHF_NO_UNRES_UNDEF        (1 << 13)
+#define RHF_RLD_ORDER_SAFE        (1 << 14)
+
+/* Entries found in sections of type SHT_MIPS_LIBLIST.  */
+
+typedef struct
+{
+  Elf32_Word l_name;           /* Name (string table index) */
+  Elf32_Word l_time_stamp;     /* Timestamp */
+  Elf32_Word l_checksum;       /* Checksum */
+  Elf32_Word l_version;                /* Interface version */
+  Elf32_Word l_flags;          /* Flags */
+} Elf32_Lib;
+
+typedef struct
+{
+  Elf64_Word l_name;           /* Name (string table index) */
+  Elf64_Word l_time_stamp;     /* Timestamp */
+  Elf64_Word l_checksum;       /* Checksum */
+  Elf64_Word l_version;                /* Interface version */
+  Elf64_Word l_flags;          /* Flags */
+} Elf64_Lib;
+
+
+/* Legal values for l_flags.  */
+
+#define LL_NONE                  0
+#define LL_EXACT_MATCH   (1 << 0)      /* Require exact match */
+#define LL_IGNORE_INT_VER (1 << 1)     /* Ignore interface version */
+#define LL_REQUIRE_MINOR  (1 << 2)
+#define LL_EXPORTS       (1 << 3)
+#define LL_DELAY_LOAD    (1 << 4)
+#define LL_DELTA         (1 << 5)
+
+/* Entries found in sections of type SHT_MIPS_CONFLICT.  */
+
+typedef Elf32_Addr Elf32_Conflict;
+
+
+/* HPPA specific definitions.  */
+
+/* Legal values for e_flags field of Elf32_Ehdr.  */
+
+#define EF_PARISC_TRAPNL       1       /* Trap nil pointer dereference.  */
+#define EF_PARISC_EXT          2       /* Program uses arch. extensions.  */
+#define EF_PARISC_ARCH         0xffff0000 /* Architecture version.  */
+/* Defined values are:
+                               0x020b  PA-RISC 1.0 big-endian
+                               0x0210  PA-RISC 1.1 big-endian
+                               0x028b  PA-RISC 1.0 little-endian
+                               0x0290  PA-RISC 1.1 little-endian
+*/
+
+/* Legal values for sh_type field of Elf32_Shdr.  */
+
+#define SHT_PARISC_GOT         0x70000000 /* GOT for external data.  */
+#define SHT_PARISC_ARCH                0x70000001 /* Architecture extensions.  */
+#define SHT_PARISC_GLOBAL      0x70000002 /* Definition of $global$.  */
+#define SHT_PARISC_MILLI       0x70000003 /* Millicode routines.  */
+#define SHT_PARISC_UNWIND      0x70000004 /* Unwind information.  */
+#define SHT_PARISC_PLT         0x70000005 /* Procedure linkage table.  */
+#define SHT_PARISC_SDATA       0x70000006 /* Short initialized data.  */
+#define SHT_PARISC_SBSS                0x70000007 /* Short uninitialized data.  */
+#define SHT_PARISC_SYMEXTN     0x70000008 /* Argument/relocation info.  */
+#define SHT_PARISC_STUBS       0x70000009 /* Linker stubs.  */
+
+/* Legal values for sh_flags field of Elf32_Shdr.  */
+
+#define SHF_PARISC_GLOBAL      0x10000000 /* Section defines dp.  */
+#define SHF_PARISC_SHORT       0x20000000 /* Section with short addressing. */
+
+/* Legal values for ST_TYPE subfield of st_info (symbol type).  */
+
+#define STT_PARISC_MILLICODE   13      /* Millicode function entry point.  */
+
+/* HPPA relocs.  */
+
+#define R_PARISC_NONE          0       /* No reloc.  */
+#define R_PARISC_DIR32         1       /* Direct 32-bit reference.  */
+#define R_PARISC_DIR21L                2       /* Left 21 bits of eff. address.  */
+#define R_PARISC_DIR17R                3       /* Right 17 bits of eff. address.  */
+#define R_PARISC_DIR14R                4       /* Right 14 bits of eff. address.  */
+#define R_PARISC_PCREL21L      5       /* PC-relative, left 21 bits.  */
+#define R_PARISC_PCREL14R      6       /* PC-relative, right 14 bits.  */
+#define R_PARISC_PCREL17C      7       /* Conditional PC-relative, ignore
+                                          if displacement > 17bits.  */
+#define R_PARISC_PCREL17F      8       /* Conditional PC-relative, must
+                                          fit in 17bits.  */
+#define R_PARISC_DPREL21L      9       /* DP-relative, left 21 bits.  */
+#define R_PARISC_DPREL14R      10      /* DP-relative, right 14 bits.  */
+#define R_PARISC_DPREL14F      11      /* DP-relative, must bit in 14 bits. */
+#define R_PARISC_DLTREL21L     12      /* DLT-relative, left 21 bits.  */
+#define R_PARISC_DLTREL14R     13      /* DLT-relative, right 14 bits.  */
+#define R_PARISC_DLTREL14F     14      /* DLT-relative, must fit in 14 bits.*/
+#define R_PARISC_DLTIND21L     15      /* DLT-relative indirect, left
+                                          21 bits.  */
+#define R_PARISC_DLTIND14R     16      /* DLT-relative indirect, right
+                                          14 bits.  */
+#define R_PARISC_DLTIND14F     17      /* DLT-relative indirect, must fit
+                                          int 14 bits.  */
+#define R_PARISC_PLABEL32      18      /* Direct 32-bit reference to proc.  */
+
+/* Alpha specific definitions.  */
+
+/* Legal values for e_flags field of Elf64_Ehdr.  */
+
+#define EF_ALPHA_32BIT         1       /* All addresses must be < 2GB.  */
+#define EF_ALPHA_CANRELAX      2       /* Relocations for relaxing exist.  */
+
+/* Legal values for sh_type field of Elf64_Shdr.  */
+
+/* These two are primerily concerned with ECOFF debugging info.  */
+#define SHT_ALPHA_DEBUG                0x70000001
+#define SHT_ALPHA_REGINFO      0x70000002
+
+/* Legal values for sh_flags field of Elf64_Shdr.  */
+
+#define SHF_ALPHA_GPREL                0x10000000
+
+/* Legal values for st_other field of Elf64_Sym.  */
+#define STO_ALPHA_NOPV         0x80    /* No PV required.  */
+#define STO_ALPHA_STD_GPLOAD   0x88    /* PV only used for initial ldgp.  */
+
+/* Alpha relocs.  */
+
+#define R_ALPHA_NONE           0       /* No reloc */
+#define R_ALPHA_REFLONG                1       /* Direct 32 bit */
+#define R_ALPHA_REFQUAD                2       /* Direct 64 bit */
+#define R_ALPHA_GPREL32                3       /* GP relative 32 bit */
+#define R_ALPHA_LITERAL                4       /* GP relative 16 bit w/optimization */
+#define R_ALPHA_LITUSE         5       /* Optimization hint for LITERAL */
+#define R_ALPHA_GPDISP         6       /* Add displacement to GP */
+#define R_ALPHA_BRADDR         7       /* PC+4 relative 23 bit shifted */
+#define R_ALPHA_HINT           8       /* PC+4 relative 16 bit shifted */
+#define R_ALPHA_SREL16         9       /* PC relative 16 bit */
+#define R_ALPHA_SREL32         10      /* PC relative 32 bit */
+#define R_ALPHA_SREL64         11      /* PC relative 64 bit */
+#define R_ALPHA_OP_PUSH                12      /* OP stack push */
+#define R_ALPHA_OP_STORE       13      /* OP stack pop and store */
+#define R_ALPHA_OP_PSUB                14      /* OP stack subtract */
+#define R_ALPHA_OP_PRSHIFT     15      /* OP stack right shift */
+#define R_ALPHA_GPVALUE                16
+#define R_ALPHA_GPRELHIGH      17
+#define R_ALPHA_GPRELLOW       18
+#define R_ALPHA_IMMED_GP_16    19
+#define R_ALPHA_IMMED_GP_HI32  20
+#define R_ALPHA_IMMED_SCN_HI32 21
+#define R_ALPHA_IMMED_BR_HI32  22
+#define R_ALPHA_IMMED_LO32     23
+#define R_ALPHA_COPY           24      /* Copy symbol at runtime */
+#define R_ALPHA_GLOB_DAT       25      /* Create GOT entry */
+#define R_ALPHA_JMP_SLOT       26      /* Create PLT entry */
+#define R_ALPHA_RELATIVE       27      /* Adjust by program base */
+/* Keep this the last entry.  */
+#define R_ALPHA_NUM            28
+
+
+/* PowerPC specific declarations */
+
+/* PowerPC relocations defined by the ABIs */
+#define R_PPC_NONE             0
+#define R_PPC_ADDR32           1       /* 32bit absolute address */
+#define R_PPC_ADDR24           2       /* 26bit address, 2 bits ignored.  */
+#define R_PPC_ADDR16           3       /* 16bit absolute address */
+#define R_PPC_ADDR16_LO                4       /* lower 16bit of absolute address */
+#define R_PPC_ADDR16_HI                5       /* high 16bit of absolute address */
+#define R_PPC_ADDR16_HA                6       /* adjusted high 16bit */
+#define R_PPC_ADDR14           7       /* 16bit address, 2 bits ignored */
+#define R_PPC_ADDR14_BRTAKEN   8
+#define R_PPC_ADDR14_BRNTAKEN  9
+#define R_PPC_REL24            10      /* PC relative 26 bit */
+#define R_PPC_REL14            11      /* PC relative 16 bit */
+#define R_PPC_REL14_BRTAKEN    12
+#define R_PPC_REL14_BRNTAKEN   13
+#define R_PPC_GOT16            14
+#define R_PPC_GOT16_LO         15
+#define R_PPC_GOT16_HI         16
+#define R_PPC_GOT16_HA         17
+#define R_PPC_PLTREL24         18
+#define R_PPC_COPY             19
+#define R_PPC_GLOB_DAT         20
+#define R_PPC_JMP_SLOT         21
+#define R_PPC_RELATIVE         22
+#define R_PPC_LOCAL24PC                23
+#define R_PPC_UADDR32          24
+#define R_PPC_UADDR16          25
+#define R_PPC_REL32            26
+#define R_PPC_PLT32            27
+#define R_PPC_PLTREL32         28
+#define R_PPC_PLT16_LO         29
+#define R_PPC_PLT16_HI         30
+#define R_PPC_PLT16_HA         31
+#define R_PPC_SDAREL16         32
+#define R_PPC_SECTOFF          33
+#define R_PPC_SECTOFF_LO       34
+#define R_PPC_SECTOFF_HI       35
+#define R_PPC_SECTOFF_HA       36
+/* Keep this the last entry.  */
+#define R_PPC_NUM              37
+
+/* The remaining relocs are from the Embedded ELF ABI, and are not
+   in the SVR4 ELF ABI.  */
+#define R_PPC_EMB_NADDR32      101
+#define R_PPC_EMB_NADDR16      102
+#define R_PPC_EMB_NADDR16_LO   103
+#define R_PPC_EMB_NADDR16_HI   104
+#define R_PPC_EMB_NADDR16_HA   105
+#define R_PPC_EMB_SDAI16       106
+#define R_PPC_EMB_SDA2I16      107
+#define R_PPC_EMB_SDA2REL      108
+#define R_PPC_EMB_SDA21                109     /* 16 bit offset in SDA */
+#define R_PPC_EMB_MRKREF       110
+#define R_PPC_EMB_RELSEC16     111
+#define R_PPC_EMB_RELST_LO     112
+#define R_PPC_EMB_RELST_HI     113
+#define R_PPC_EMB_RELST_HA     114
+#define R_PPC_EMB_BIT_FLD      115
+#define R_PPC_EMB_RELSDA       116     /* 16 bit relative offset in SDA */
+
+/* Diab tool relocations.  */
+#define R_PPC_DIAB_SDA21_LO    180     /* like EMB_SDA21, but lower 16 bit */
+#define R_PPC_DIAB_SDA21_HI    181     /* like EMB_SDA21, but high 16 bit */
+#define R_PPC_DIAB_SDA21_HA    182     /* like EMB_SDA21, adjusted high 16 */
+#define R_PPC_DIAB_RELSDA_LO   183     /* like EMB_RELSDA, but lower 16 bit */
+#define R_PPC_DIAB_RELSDA_HI   184     /* like EMB_RELSDA, but high 16 bit */
+#define R_PPC_DIAB_RELSDA_HA   185     /* like EMB_RELSDA, adjusted high 16 */
+
+/* This is a phony reloc to handle any old fashioned TOC16 references
+   that may still be in object files.  */
+#define R_PPC_TOC16            255
+
+
+/* ARM specific declarations */
+
+/* Processor specific flags for the ELF header e_flags field.  */
+#define EF_ARM_RELEXEC     0x01
+#define EF_ARM_HASENTRY    0x02
+#define EF_ARM_INTERWORK   0x04
+#define EF_ARM_APCS_26     0x08
+#define EF_ARM_APCS_FLOAT  0x10
+#define EF_ARM_PIC         0x20
+#define EF_ALIGN8          0x40                /* 8-bit structure alignment is in use */
+#define EF_NEW_ABI         0x80
+#define EF_OLD_ABI         0x100
+
+/* Additional symbol types for Thumb */
+#define STT_ARM_TFUNC      0xd
+
+/* ARM-specific values for sh_flags */
+#define SHF_ARM_ENTRYSECT  0x10000000   /* Section contains an entry point */
+#define SHF_ARM_COMDEF     0x80000000   /* Section may be multiply defined
+                                          in the input to a link step */
+
+/* ARM-specific program header flags */
+#define PF_ARM_SB          0x10000000   /* Segment contains the location
+                                          addressed by the static base */
+
+/* ARM relocs.  */
+#define R_ARM_NONE             0       /* No reloc */
+#define R_ARM_PC24             1       /* PC relative 26 bit branch */
+#define R_ARM_ABS32            2       /* Direct 32 bit  */
+#define R_ARM_REL32            3       /* PC relative 32 bit */
+#define R_ARM_PC13             4
+#define R_ARM_ABS16            5       /* Direct 16 bit */
+#define R_ARM_ABS12            6       /* Direct 12 bit */
+#define R_ARM_THM_ABS5         7
+#define R_ARM_ABS8             8       /* Direct 8 bit */
+#define R_ARM_SBREL32          9
+#define R_ARM_THM_PC22         10
+#define R_ARM_THM_PC8          11
+#define R_ARM_AMP_VCALL9       12
+#define R_ARM_SWI24            13
+#define R_ARM_THM_SWI8         14
+#define R_ARM_XPC25            15
+#define R_ARM_THM_XPC22                16
+#define R_ARM_COPY             20      /* Copy symbol at runtime */
+#define R_ARM_GLOB_DAT         21      /* Create GOT entry */
+#define R_ARM_JUMP_SLOT                22      /* Create PLT entry */
+#define R_ARM_RELATIVE         23      /* Adjust by program base */
+#define R_ARM_GOTOFF           24      /* 32 bit offset to GOT */
+#define R_ARM_GOTPC            25      /* 32 bit PC relative offset to GOT */
+#define R_ARM_GOT32            26      /* 32 bit GOT entry */
+#define R_ARM_PLT32            27      /* 32 bit PLT address */
+#define R_ARM_GNU_VTENTRY      100
+#define R_ARM_GNU_VTINHERIT    101
+#define R_ARM_THM_PC11         102     /* thumb unconditional branch */
+#define R_ARM_THM_PC9          103     /* thumb conditional branch */
+#define R_ARM_RXPC25           249
+#define R_ARM_RSBREL32         250
+#define R_ARM_THM_RPC22                251
+#define R_ARM_RREL32           252
+#define R_ARM_RABS22           253
+#define R_ARM_RPC24            254
+#define R_ARM_RBASE            255
+/* Keep this the last entry.  */
+#define R_ARM_NUM              256
+
+/* TMS320C67xx specific declarations */
+/* XXX: no ELF standard yet */
+
+/* TMS320C67xx relocs. */
+#define R_C60_32       1
+#define R_C60_GOT32    3               /* 32 bit GOT entry */
+#define R_C60_PLT32    4               /* 32 bit PLT address */
+#define R_C60_COPY     5               /* Copy symbol at runtime */
+#define R_C60_GLOB_DAT 6               /* Create GOT entry */
+#define R_C60_JMP_SLOT 7               /* Create PLT entry */
+#define R_C60_RELATIVE 8               /* Adjust by program base */
+#define R_C60_GOTOFF   9               /* 32 bit offset to GOT */
+#define R_C60_GOTPC    10              /* 32 bit PC relative offset to GOT */
+
+#define R_C60HI16      0x55       // high 16 bit MVKH embedded
+#define R_C60LO16      0x54       // low 16 bit MVKL embedded
+
+#endif /* elf.h */
+//---------------------------------------------------------------------------
+
+
+// njn: inlined stab.h
+//#include "stab.h"
+//---------------------------------------------------------------------------
+#ifndef __GNU_STAB__
+
+/* Indicate the GNU stab.h is in use.  */
+
+#define __GNU_STAB__
+
+#define __define_stab(NAME, CODE, STRING) NAME=CODE,
+
+enum __stab_debug_code
+{
+// njn: inlined stab.def
+//#include "stab.def"
+//---------------------------------------------------------------------------
+/* Table of DBX symbol codes for the GNU system.
+   Copyright (C) 1988, 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* This contains contribution from Cygnus Support.  */
+\f
+/* Global variable.  Only the name is significant.
+   To find the address, look in the corresponding external symbol.  */
+__define_stab (N_GSYM, 0x20, "GSYM")
+
+/* Function name for BSD Fortran.  Only the name is significant.
+   To find the address, look in the corresponding external symbol.  */
+__define_stab (N_FNAME, 0x22, "FNAME")
+
+/* Function name or text-segment variable for C.  Value is its address.
+   Desc is supposedly starting line number, but GCC doesn't set it
+   and DBX seems not to miss it.  */
+__define_stab (N_FUN, 0x24, "FUN")
+
+/* Data-segment variable with internal linkage.  Value is its address.
+   "Static Sym".  */
+__define_stab (N_STSYM, 0x26, "STSYM")
+
+/* BSS-segment variable with internal linkage.  Value is its address.  */
+__define_stab (N_LCSYM, 0x28, "LCSYM")
+
+/* Name of main routine.  Only the name is significant.
+   This is not used in C.  */
+__define_stab (N_MAIN, 0x2a, "MAIN")
+
+/* Global symbol in Pascal.
+   Supposedly the value is its line number; I'm skeptical.  */
+__define_stab (N_PC, 0x30, "PC")
+
+/* Number of symbols:  0, files,,funcs,lines according to Ultrix V4.0. */
+__define_stab (N_NSYMS, 0x32, "NSYMS")
+
+/* "No DST map for sym: name, ,0,type,ignored"  according to Ultrix V4.0. */
+__define_stab (N_NOMAP, 0x34, "NOMAP")
+
+/* New stab from Solaris.  I don't know what it means, but it
+   don't seem to contain useful information.  */
+__define_stab (N_OBJ, 0x38, "OBJ")
+
+/* New stab from Solaris.  I don't know what it means, but it
+   don't seem to contain useful information.  Possibly related to the
+   optimization flags used in this module.  */
+__define_stab (N_OPT, 0x3c, "OPT")
+
+/* Register variable.  Value is number of register.  */
+__define_stab (N_RSYM, 0x40, "RSYM")
+
+/* Modula-2 compilation unit.  Can someone say what info it contains?  */
+__define_stab (N_M2C, 0x42, "M2C")
+
+/* Line number in text segment.  Desc is the line number;
+   value is corresponding address.  */
+__define_stab (N_SLINE, 0x44, "SLINE")
+
+/* Similar, for data segment.  */
+__define_stab (N_DSLINE, 0x46, "DSLINE")
+
+/* Similar, for bss segment.  */
+__define_stab (N_BSLINE, 0x48, "BSLINE")
+
+/* Sun's source-code browser stabs.  ?? Don't know what the fields are.
+   Supposedly the field is "path to associated .cb file".  THIS VALUE
+   OVERLAPS WITH N_BSLINE!  */
+__define_stab (N_BROWS, 0x48, "BROWS")
+
+/* GNU Modula-2 definition module dependency.  Value is the modification time
+   of the definition file.  Other is non-zero if it is imported with the
+   GNU M2 keyword %INITIALIZE.  Perhaps N_M2C can be used if there
+   are enough empty fields? */
+__define_stab(N_DEFD, 0x4a, "DEFD")
+
+/* THE FOLLOWING TWO STAB VALUES CONFLICT.  Happily, one is for Modula-2
+   and one is for C++.   Still,... */
+/* GNU C++ exception variable.  Name is variable name.  */
+__define_stab (N_EHDECL, 0x50, "EHDECL")
+/* Modula2 info "for imc":  name,,0,0,0  according to Ultrix V4.0.  */
+__define_stab (N_MOD2, 0x50, "MOD2")
+
+/* GNU C++ `catch' clause.  Value is its address.  Desc is nonzero if
+   this entry is immediately followed by a CAUGHT stab saying what exception
+   was caught.  Multiple CAUGHT stabs means that multiple exceptions
+   can be caught here.  If Desc is 0, it means all exceptions are caught
+   here.  */
+__define_stab (N_CATCH, 0x54, "CATCH")
+
+/* Structure or union element.  Value is offset in the structure.  */
+__define_stab (N_SSYM, 0x60, "SSYM")
+
+/* Name of main source file.
+   Value is starting text address of the compilation.  */
+__define_stab (N_SO, 0x64, "SO")
+
+/* Automatic variable in the stack.  Value is offset from frame pointer.
+   Also used for type descriptions.  */
+__define_stab (N_LSYM, 0x80, "LSYM")
+
+/* Beginning of an include file.  Only Sun uses this.
+   In an object file, only the name is significant.
+   The Sun linker puts data into some of the other fields.  */
+__define_stab (N_BINCL, 0x82, "BINCL")
+
+/* Name of sub-source file (#include file).
+   Value is starting text address of the compilation.  */
+__define_stab (N_SOL, 0x84, "SOL")
+
+/* Parameter variable.  Value is offset from argument pointer.
+   (On most machines the argument pointer is the same as the frame pointer.  */
+__define_stab (N_PSYM, 0xa0, "PSYM")
+
+/* End of an include file.  No name.
+   This and N_BINCL act as brackets around the file's output.
+   In an object file, there is no significant data in this entry.
+   The Sun linker puts data into some of the fields.  */
+__define_stab (N_EINCL, 0xa2, "EINCL")
+
+/* Alternate entry point.  Value is its address.  */
+__define_stab (N_ENTRY, 0xa4, "ENTRY")
+
+/* Beginning of lexical block.
+   The desc is the nesting level in lexical blocks.
+   The value is the address of the start of the text for the block.
+   The variables declared inside the block *precede* the N_LBRAC symbol.  */
+__define_stab (N_LBRAC, 0xc0, "LBRAC")
+
+/* Place holder for deleted include file.  Replaces a N_BINCL and everything
+   up to the corresponding N_EINCL.  The Sun linker generates these when
+   it finds multiple identical copies of the symbols from an include file.
+   This appears only in output from the Sun linker.  */
+__define_stab (N_EXCL, 0xc2, "EXCL")
+
+/* Modula-2 scope information.  Can someone say what info it contains?  */
+__define_stab (N_SCOPE, 0xc4, "SCOPE")
+
+/* End of a lexical block.  Desc matches the N_LBRAC's desc.
+   The value is the address of the end of the text for the block.  */
+__define_stab (N_RBRAC, 0xe0, "RBRAC")
+
+/* Begin named common block.  Only the name is significant.  */
+__define_stab (N_BCOMM, 0xe2, "BCOMM")
+
+/* End named common block.  Only the name is significant
+   (and it should match the N_BCOMM).  */
+__define_stab (N_ECOMM, 0xe4, "ECOMM")
+
+/* End common (local name): value is address.
+   I'm not sure how this is used.  */
+__define_stab (N_ECOML, 0xe8, "ECOML")
+
+/* These STAB's are used on Gould systems for Non-Base register symbols
+   or something like that.  FIXME.  I have assigned the values at random
+   since I don't have a Gould here.  Fixups from Gould folk welcome... */
+__define_stab (N_NBTEXT, 0xF0, "NBTEXT")
+__define_stab (N_NBDATA, 0xF2, "NBDATA")
+__define_stab (N_NBBSS,  0xF4, "NBBSS")
+__define_stab (N_NBSTS,  0xF6, "NBSTS")
+__define_stab (N_NBLCS,  0xF8, "NBLCS")
+
+/* Second symbol entry containing a length-value for the preceding entry.
+   The value is the length.  */
+__define_stab (N_LENG, 0xfe, "LENG")
+\f
+/* The above information, in matrix format.
+
+                       STAB MATRIX
+       _________________________________________________
+       | 00 - 1F are not dbx stab symbols              |
+       | In most cases, the low bit is the EXTernal bit|
+
+       | 00 UNDEF  | 02 ABS    | 04 TEXT   | 06 DATA   |
+       | 01  |EXT  | 03  |EXT  | 05  |EXT  | 07  |EXT  |
+
+       | 08 BSS    | 0A INDR   | 0C FN_SEQ | 0E        |
+       | 09  |EXT  | 0B        | 0D        | 0F        |
+
+       | 10        | 12 COMM   | 14 SETA   | 16 SETT   |
+       | 11        | 13        | 15        | 17        |
+
+       | 18 SETD   | 1A SETB   | 1C SETV   | 1E WARNING|
+       | 19        | 1B        | 1D        | 1F FN     |
+
+       |_______________________________________________|
+       | Debug entries with bit 01 set are unused.     |
+       | 20 GSYM   | 22 FNAME  | 24 FUN    | 26 STSYM  |
+       | 28 LCSYM  | 2A MAIN   | 2C        | 2E        |
+       | 30 PC     | 32 NSYMS  | 34 NOMAP  | 36        |
+       | 38 OBJ    | 3A        | 3C OPT    | 3E        |
+       | 40 RSYM   | 42 M2C    | 44 SLINE  | 46 DSLINE |
+       | 48 BSLINE*| 4A DEFD   | 4C        | 4E        |
+       | 50 EHDECL*| 52        | 54 CATCH  | 56        |
+       | 58        | 5A        | 5C        | 5E        |
+       | 60 SSYM   | 62        | 64 SO     | 66        |
+       | 68        | 6A        | 6C        | 6E        |
+       | 70        | 72        | 74        | 76        |
+       | 78        | 7A        | 7C        | 7E        |
+       | 80 LSYM   | 82 BINCL  | 84 SOL    | 86        |
+       | 88        | 8A        | 8C        | 8E        |
+       | 90        | 92        | 94        | 96        |
+       | 98        | 9A        | 9C        | 9E        |
+       | A0 PSYM   | A2 EINCL  | A4 ENTRY  | A6        |
+       | A8        | AA        | AC        | AE        |
+       | B0        | B2        | B4        | B6        |
+       | B8        | BA        | BC        | BE        |
+       | C0 LBRAC  | C2 EXCL   | C4 SCOPE  | C6        |
+       | C8        | CA        | CC        | CE        |
+       | D0        | D2        | D4        | D6        |
+       | D8        | DA        | DC        | DE        |
+       | E0 RBRAC  | E2 BCOMM  | E4 ECOMM  | E6        |
+       | E8 ECOML  | EA        | EC        | EE        |
+       | F0        | F2        | F4        | F6        |
+       | F8        | FA        | FC        | FE LENG   |
+       +-----------------------------------------------+
+ * 50 EHDECL is also MOD2.
+ * 48 BSLINE is also BROWS.
+ */
+//---------------------------------------------------------------------------
+LAST_UNUSED_STAB_CODE
+};
+
+#undef __define_stab
+
+#endif /* __GNU_STAB_ */
+//---------------------------------------------------------------------------
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+// njn: inlined libtcc.h
+//#include "libtcc.h"
+//---------------------------------------------------------------------------
+#ifndef LIBTCC_H
+#define LIBTCC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct TCCState;
+
+typedef struct TCCState TCCState;
+
+/* create a new TCC compilation context */
+TCCState *tcc_new(void);
+
+/* free a TCC compilation context */
+void tcc_delete(TCCState *s);
+
+/* add debug information in the generated code */
+void tcc_enable_debug(TCCState *s);
+
+/* set error/warning display callback */
+void tcc_set_error_func(TCCState *s, void *error_opaque,
+                        void (*error_func)(void *opaque, const char *msg));
+
+/* set/reset a warning */
+int tcc_set_warning(TCCState *s, const char *warning_name, int value);
+
+/*****************************/
+/* preprocessor */
+
+/* add include path */
+int tcc_add_include_path(TCCState *s, const char *pathname);
+
+/* add in system include path */
+int tcc_add_sysinclude_path(TCCState *s, const char *pathname);
+
+/* define preprocessor symbol 'sym'. Can put optional value */
+void tcc_define_symbol(TCCState *s, const char *sym, const char *value);
+
+/* undefine preprocess symbol 'sym' */
+void tcc_undefine_symbol(TCCState *s, const char *sym);
+
+/*****************************/
+/* compiling */
+
+/* add a file (either a C file, dll, an object, a library or an ld
+   script). Return -1 if error. */
+int tcc_add_file(TCCState *s, const char *filename);
+
+/* compile a string containing a C source. Return non zero if
+   error. */
+int tcc_compile_string(TCCState *s, const char *buf);
+
+/*****************************/
+/* linking commands */
+
+/* set output type. MUST BE CALLED before any compilation */
+#define TCC_OUTPUT_MEMORY   0 /* output will be ran in memory (no
+                                 output file) (default) */
+#define TCC_OUTPUT_EXE      1 /* executable file */
+#define TCC_OUTPUT_DLL      2 /* dynamic library */
+#define TCC_OUTPUT_OBJ      3 /* object file */
+int tcc_set_output_type(TCCState *s, int output_type);
+
+#define TCC_OUTPUT_FORMAT_ELF    0 /* default output format: ELF */
+#define TCC_OUTPUT_FORMAT_BINARY 1 /* binary image output */
+#define TCC_OUTPUT_FORMAT_COFF   2 /* COFF */
+
+/* equivalent to -Lpath option */
+int tcc_add_library_path(TCCState *s, const char *pathname);
+
+/* the library name is the same as the argument of the '-l' option */
+int tcc_add_library(TCCState *s, const char *libraryname);
+
+/* add a symbol to the compiled program */
+int tcc_add_symbol(TCCState *s, const char *name, unsigned long val);
+
+/* output an executable, library or object file. DO NOT call
+   tcc_relocate() before. */
+int tcc_output_file(TCCState *s, const char *filename);
+
+/* link and run main() function and return its value. DO NOT call
+   tcc_relocate() before. */
+int tcc_run(TCCState *s, int argc, char **argv);
+
+/* do all relocations (needed before using tcc_get_symbol()). Return
+   non zero if link error. */
+int tcc_relocate(TCCState *s);
+
+/* return symbol value. return 0 if OK, -1 if symbol not found */
+int tcc_get_symbol(TCCState *s, unsigned long *pval, const char *name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+//---------------------------------------------------------------------------
+
+/* parser debug */
+//#define PARSE_DEBUG
+/* preprocessor debug */
+//#define PP_DEBUG
+/* include file debug */
+//#define INC_DEBUG
+
+//#define MEM_DEBUG
+
+/* assembler debug */
+//#define ASM_DEBUG
+
+/* target selection */
+//#define TCC_TARGET_I386   /* i386 code generator */
+//#define TCC_TARGET_ARM    /* ARMv4 code generator */
+//#define TCC_TARGET_C67    /* TMS320C67xx code generator */
+
+/* default target is I386 */
+#if !defined(TCC_TARGET_I386) && !defined(TCC_TARGET_ARM) && \
+    !defined(TCC_TARGET_C67)
+#define TCC_TARGET_I386
+#endif
+
+#if !defined(WIN32) && !defined(TCC_UCLIBC) && !defined(TCC_TARGET_ARM) && \
+    !defined(TCC_TARGET_C67)
+#define CONFIG_TCC_BCHECK /* enable bound checking code */
+#endif
+
+#if defined(WIN32) && !defined(TCC_TARGET_PE)
+#define CONFIG_TCC_STATIC
+#endif
+
+/* define it to include assembler support */
+#if !defined(TCC_TARGET_ARM) && !defined(TCC_TARGET_C67)
+#define CONFIG_TCC_ASM
+#endif
+
+/* object format selection */
+#if defined(TCC_TARGET_C67)
+#define TCC_TARGET_COFF
+#endif
+
+#define FALSE 0
+#define false 0
+#define TRUE 1
+#define true 1
+typedef int BOOL;
+
+/* path to find crt1.o, crti.o and crtn.o. Only needed when generating
+   executables or dlls */
+#define CONFIG_TCC_CRT_PREFIX "/usr/lib"
+
+#define INCLUDE_STACK_SIZE  32
+#define IFDEF_STACK_SIZE    64
+#define VSTACK_SIZE         256
+#define STRING_MAX_SIZE     1024
+#define PACK_STACK_SIZE     8
+
+#define TOK_HASH_SIZE       8192 /* must be a power of two */
+#define TOK_ALLOC_INCR      512  /* must be a power of two */
+#define TOK_MAX_SIZE        4 /* token max size in int unit when stored in string */
+
+/* token symbol management */
+typedef struct TokenSym {
+    struct TokenSym *hash_next;
+    struct Sym *sym_define; /* direct pointer to define */
+    struct Sym *sym_label; /* direct pointer to label */
+    struct Sym *sym_struct; /* direct pointer to structure */
+    struct Sym *sym_identifier; /* direct pointer to identifier */
+    int tok; /* token number */
+    int len;
+    char str[1];
+} TokenSym;
+
+typedef struct CString {
+    int size; /* size in bytes */
+    void *data; /* either 'char *' or 'int *' */
+    int size_allocated;
+    void *data_allocated; /* if non NULL, data has been malloced */
+} CString;
+
+/* type definition */
+typedef struct CType {
+    int t;
+    struct Sym *ref;
+} CType;
+
+/* constant value */
+typedef union CValue {
+    long double ld;
+    double d;
+    float f;
+    int i;
+    unsigned int ui;
+    unsigned int ul; /* address (should be unsigned long on 64 bit cpu) */
+    long long ll;
+    unsigned long long ull;
+    struct CString *cstr;
+    void *ptr;
+    int tab[1];
+} CValue;
+
+/* value on stack */
+typedef struct SValue {
+    CType type;      /* type */
+    unsigned short r;      /* register + flags */
+    unsigned short r2;     /* second register, used for 'long long'
+                              type. If not used, set to VT_CONST */
+    CValue c;              /* constant, if VT_CONST */
+    struct Sym *sym;       /* symbol, if (VT_SYM | VT_CONST) */
+} SValue;
+
+/* symbol management */
+typedef struct Sym {
+    int v;    /* symbol token */
+    int r;    /* associated register */
+    int c;    /* associated number */
+    CType type;    /* associated type */
+    struct Sym *next; /* next related symbol */
+    struct Sym *prev; /* prev symbol in stack */
+    struct Sym *prev_tok; /* previous symbol for this token */
+} Sym;
+
+/* section definition */
+/* XXX: use directly ELF structure for parameters ? */
+/* special flag to indicate that the section should not be linked to
+   the other ones */
+#define SHF_PRIVATE 0x80000000
+
+typedef struct Section {
+    unsigned long data_offset; /* current data offset */
+    unsigned char *data;       /* section data */
+    unsigned long data_allocated; /* used for realloc() handling */
+    int sh_name;             /* elf section name (only used during output) */
+    int sh_num;              /* elf section number */
+    int sh_type;             /* elf section type */
+    int sh_flags;            /* elf section flags */
+    int sh_info;             /* elf section info */
+    int sh_addralign;        /* elf section alignment */
+    int sh_entsize;          /* elf entry size */
+    unsigned long sh_size;   /* section size (only used during output) */
+    unsigned long sh_addr;      /* address at which the section is relocated */
+    unsigned long sh_offset;      /* address at which the section is relocated */
+    int nb_hashed_syms;      /* used to resize the hash table */
+    struct Section *link;    /* link to another section */
+    struct Section *reloc;   /* corresponding section for relocation, if any */
+    struct Section *hash;     /* hash table for symbols */
+    struct Section *next;
+    char name[1];           /* section name */
+} Section;
+
+typedef struct DLLReference {
+    int level;
+    char name[1];
+} DLLReference;
+
+/* GNUC attribute definition */
+typedef struct AttributeDef {
+    int aligned;
+    int packed; 
+    Section *section;
+    unsigned char func_call; /* FUNC_CDECL, FUNC_STDCALL, FUNC_FASTCALLx */
+    unsigned char dllexport; 
+} AttributeDef;
+
+#define SYM_STRUCT     0x40000000 /* struct/union/enum symbol space */
+#define SYM_FIELD      0x20000000 /* struct/union field symbol space */
+#define SYM_FIRST_ANOM 0x10000000 /* first anonymous sym */
+
+/* stored in 'Sym.c' field */
+#define FUNC_NEW       1 /* ansi function prototype */
+#define FUNC_OLD       2 /* old function prototype */
+#define FUNC_ELLIPSIS  3 /* ansi function prototype with ... */
+
+/* stored in 'Sym.r' field */
+#define FUNC_CDECL     0 /* standard c call */
+#define FUNC_STDCALL   1 /* pascal c call */
+#define FUNC_FASTCALL1 2 /* first param in %eax */
+#define FUNC_FASTCALL2 3 /* first parameters in %eax, %edx */
+#define FUNC_FASTCALL3 4 /* first parameter in %eax, %edx, %ecx */
+
+/* field 'Sym.t' for macros */
+#define MACRO_OBJ      0 /* object like macro */
+#define MACRO_FUNC     1 /* function like macro */
+
+/* field 'Sym.r' for C labels */
+#define LABEL_DEFINED  0 /* label is defined */
+#define LABEL_FORWARD  1 /* label is forward defined */
+#define LABEL_DECLARED 2 /* label is declared but never used */
+
+/* type_decl() types */
+#define TYPE_ABSTRACT  1 /* type without variable */
+#define TYPE_DIRECT    2 /* type with variable */
+
+#define IO_BUF_SIZE 8192
+
+typedef struct BufferedFile {
+    uint8_t *buf_ptr;
+    uint8_t *buf_end;
+    int fd;
+    int line_num;    /* current line number - here to simplify code */
+    int ifndef_macro;  /* #ifndef macro / #endif search */
+    int ifndef_macro_saved; /* saved ifndef_macro */
+    int *ifdef_stack_ptr; /* ifdef_stack value at the start of the file */
+    char inc_type;          /* type of include */
+    char inc_filename[512]; /* filename specified by the user */
+    char filename[1024];    /* current filename - here to simplify code */
+    unsigned char buffer[IO_BUF_SIZE + 1]; /* extra size for CH_EOB char */
+} BufferedFile;
+
+#define CH_EOB   '\\'       /* end of buffer or '\0' char in file */
+#define CH_EOF   (-1)   /* end of file */
+
+/* parsing state (used to save parser state to reparse part of the
+   source several times) */
+typedef struct ParseState {
+    int *macro_ptr;
+    int line_num;
+    int tok;
+    CValue tokc;
+} ParseState;
+
+/* used to record tokens */
+typedef struct TokenString {
+    int *str;
+    int len;
+    int allocated_len;
+    int last_line_num;
+} TokenString;
+
+/* include file cache, used to find files faster and also to eliminate
+   inclusion if the include file is protected by #ifndef ... #endif */
+typedef struct CachedInclude {
+    int ifndef_macro;
+    int hash_next; /* -1 if none */
+    char type; /* '"' or '>' to give include type */
+    char filename[1]; /* path specified in #include */
+} CachedInclude;
+
+#define CACHED_INCLUDES_HASH_SIZE 512
+
+/* parser */
+static struct BufferedFile *file;
+static int ch, tok;
+static CValue tokc;
+static CString tokcstr; /* current parsed string, if any */
+/* additional informations about token */
+static int tok_flags;
+#define TOK_FLAG_BOL   0x0001 /* beginning of line before */
+#define TOK_FLAG_BOF   0x0002 /* beginning of file before */
+#define TOK_FLAG_ENDIF 0x0004 /* a endif was found matching starting #ifdef */
+
+static int *macro_ptr, *macro_ptr_allocated;
+static int *unget_saved_macro_ptr;
+static int unget_saved_buffer[TOK_MAX_SIZE + 1];
+static int unget_buffer_enabled;
+static int parse_flags;
+#define PARSE_FLAG_PREPROCESS 0x0001 /* activate preprocessing */
+#define PARSE_FLAG_TOK_NUM    0x0002 /* return numbers instead of TOK_PPNUM */
+#define PARSE_FLAG_LINEFEED   0x0004 /* line feed is returned as a
+                                        token. line feed is also
+                                        returned at eof */
+#define PARSE_FLAG_ASM_COMMENTS 0x0008 /* '#' can be used for line comment */
+static Section *text_section, *data_section, *bss_section; /* predefined sections */
+static Section *cur_text_section; /* current section where function code is
+                              generated */
+#ifdef CONFIG_TCC_ASM
+static Section *last_text_section; /* to handle .previous asm directive */
+#endif
+/* bound check related sections */
+static Section *bounds_section; /* contains global data bound description */
+static Section *lbounds_section; /* contains local data bound description */
+/* symbol sections */
+static Section *symtab_section, *strtab_section;
+
+/* debug sections */
+static Section *stab_section, *stabstr_section;
+
+/* loc : local variable index
+   ind : output code index
+   rsym: return symbol
+   anon_sym: anonymous symbol index
+*/
+static int rsym, anon_sym, ind, loc;
+/* expression generation modifiers */
+static int const_wanted; /* true if constant wanted */
+static int nocode_wanted; /* true if no code generation wanted for an expression */
+static int global_expr;  /* true if compound literals must be allocated
+                            globally (used during initializers parsing */
+static CType func_vt; /* current function return type (used by return
+                         instruction) */
+static int func_vc;
+static int last_line_num, last_ind, func_ind; /* debug last line number and pc */
+static int tok_ident;
+static TokenSym **table_ident;
+static TokenSym *hash_ident[TOK_HASH_SIZE];
+static char token_buf[STRING_MAX_SIZE + 1];
+static char *funcname;
+static Sym *global_stack, *local_stack;
+static Sym *define_stack;
+static Sym *global_label_stack, *local_label_stack;
+/* symbol allocator */
+#define SYM_POOL_NB (8192 / sizeof(Sym))
+static Sym *sym_free_first;
+
+static SValue vstack[VSTACK_SIZE], *vtop;
+/* some predefined types */
+static CType char_pointer_type, func_old_type, int_type;
+/* true if isid(c) || isnum(c) */
+static unsigned char isidnum_table[256];
+
+/* compile with debug symbol (and use them if error during execution) */
+static int do_debug = 0;
+
+/* compile with built-in memory and bounds checker */
+static int do_bounds_check = 0;
+
+/* display benchmark infos */
+#if !defined(LIBTCC)
+static int do_bench = 0;
+#endif
+static int total_lines;
+static int total_bytes;
+
+/* use GNU C extensions */
+static int gnu_ext = 1;
+
+/* use Tiny C extensions */
+static int tcc_ext = 1;
+
+/* max number of callers shown if error */
+static int num_callers = 6;
+static const char **rt_bound_error_msg;
+
+/* XXX: get rid of this ASAP */
+static struct TCCState *tcc_state;
+
+/* give the path of the tcc libraries */
+static const char *tcc_lib_path = CONFIG_TCCDIR;
+
+struct TCCState {
+    int output_type;
+    BufferedFile **include_stack_ptr;
+    int *ifdef_stack_ptr;
+
+    /* include file handling */
+    char **include_paths;
+    int nb_include_paths;
+    char **sysinclude_paths;
+    int nb_sysinclude_paths;
+    CachedInclude **cached_includes;
+    int nb_cached_includes;
+
+    char **library_paths;
+    int nb_library_paths;
+
+    /* array of all loaded dlls (including those referenced by loaded
+       dlls) */
+    DLLReference **loaded_dlls;
+    int nb_loaded_dlls;
+
+    /* sections */
+    Section **sections;
+    int nb_sections; /* number of sections, including first dummy section */
+
+    /* got handling */
+    Section *got;
+    Section *plt;
+    unsigned long *got_offsets;
+    int nb_got_offsets;
+    /* give the correspondance from symtab indexes to dynsym indexes */
+    int *symtab_to_dynsym;
+
+    /* temporary dynamic symbol sections (for dll loading) */
+    Section *dynsymtab_section;
+    /* exported dynamic symbol section */
+    Section *dynsym;
+
+    int nostdinc; /* if true, no standard headers are added */
+    int nostdlib; /* if true, no standard libraries are added */
+
+    int nocommon; /* if true, do not use common symbols for .bss data */
+
+    /* if true, static linking is performed */
+    int static_link;
+
+    /* if true, all symbols are exported */
+    int rdynamic;
+
+    /* if true, only link in referenced objects from archive */
+    int alacarte_link;
+
+    /* address of text section */
+    unsigned long text_addr;
+    int has_text_addr;
+    
+    /* output format, see TCC_OUTPUT_FORMAT_xxx */
+    int output_format;
+
+    /* C language options */
+    int char_is_unsigned;
+    int leading_underscore;
+    
+    /* warning switches */
+    int warn_write_strings;
+    int warn_unsupported;
+    int warn_error;
+    int warn_none;
+    int warn_implicit_function_declaration;
+
+    /* error handling */
+    void *error_opaque;
+    void (*error_func)(void *opaque, const char *msg);
+    int error_set_jmp_enabled;
+    jmp_buf error_jmp_buf;
+    int nb_errors;
+
+    /* tiny assembler state */
+    Sym *asm_labels;
+
+    /* see include_stack_ptr */
+    BufferedFile *include_stack[INCLUDE_STACK_SIZE];
+
+    /* see ifdef_stack_ptr */
+    int ifdef_stack[IFDEF_STACK_SIZE];
+
+    /* see cached_includes */
+    int cached_includes_hash[CACHED_INCLUDES_HASH_SIZE];
+
+    /* pack stack */
+    int pack_stack[PACK_STACK_SIZE];
+    int *pack_stack_ptr;
+};
+
+/* The current value can be: */
+#define VT_VALMASK   0x00ff
+#define VT_CONST     0x00f0  /* constant in vc 
+                              (must be first non register value) */
+#define VT_LLOCAL    0x00f1  /* lvalue, offset on stack */
+#define VT_LOCAL     0x00f2  /* offset on stack */
+#define VT_CMP       0x00f3  /* the value is stored in processor flags (in vc) */
+#define VT_JMP       0x00f4  /* value is the consequence of jmp true (even) */
+#define VT_JMPI      0x00f5  /* value is the consequence of jmp false (odd) */
+#define VT_LVAL      0x0100  /* var is an lvalue */
+#define VT_SYM       0x0200  /* a symbol value is added */
+#define VT_MUSTCAST  0x0400  /* value must be casted to be correct (used for
+                                char/short stored in integer registers) */
+#define VT_MUSTBOUND 0x0800  /* bound checking must be done before
+                                dereferencing value */
+#define VT_BOUNDED   0x8000  /* value is bounded. The address of the
+                                bounding function call point is in vc */
+#define VT_LVAL_BYTE     0x1000  /* lvalue is a byte */
+#define VT_LVAL_SHORT    0x2000  /* lvalue is a short */
+#define VT_LVAL_UNSIGNED 0x4000  /* lvalue is unsigned */
+#define VT_LVAL_TYPE     (VT_LVAL_BYTE | VT_LVAL_SHORT | VT_LVAL_UNSIGNED)
+
+/* types */
+#define VT_INT        0  /* integer type */
+#define VT_BYTE       1  /* signed byte type */
+#define VT_SHORT      2  /* short type */
+#define VT_VOID       3  /* void type */
+#define VT_PTR        4  /* pointer */
+#define VT_ENUM       5  /* enum definition */
+#define VT_FUNC       6  /* function type */
+#define VT_STRUCT     7  /* struct/union definition */
+#define VT_FLOAT      8  /* IEEE float */
+#define VT_DOUBLE     9  /* IEEE double */
+#define VT_LDOUBLE   10  /* IEEE long double */
+#define VT_BOOL      11  /* ISOC99 boolean type */
+#define VT_LLONG     12  /* 64 bit integer */
+#define VT_LONG      13  /* long integer (NEVER USED as type, only
+                            during parsing) */
+#define VT_BTYPE      0x000f /* mask for basic type */
+#define VT_UNSIGNED   0x0010  /* unsigned type */
+#define VT_ARRAY      0x0020  /* array type (also has VT_PTR) */
+#define VT_BITFIELD   0x0040  /* bitfield modifier */
+#define VT_CONSTANT   0x0800  /* const modifier */
+#define VT_VOLATILE   0x1000  /* volatile modifier */
+#define VT_SIGNED     0x2000  /* signed type */
+
+/* storage */
+#define VT_EXTERN  0x00000080  /* extern definition */
+#define VT_STATIC  0x00000100  /* static variable */
+#define VT_TYPEDEF 0x00000200  /* typedef definition */
+#define VT_INLINE  0x00000400  /* inline definition */
+
+#define VT_STRUCT_SHIFT 16   /* shift for bitfield shift values */
+
+/* type mask (except storage) */
+#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE)
+#define VT_TYPE    (~(VT_STORAGE))
+
+/* token values */
+
+/* warning: the following compare tokens depend on i386 asm code */
+#define TOK_ULT 0x92
+#define TOK_UGE 0x93
+#define TOK_EQ  0x94
+#define TOK_NE  0x95
+#define TOK_ULE 0x96
+#define TOK_UGT 0x97
+#define TOK_LT  0x9c
+#define TOK_GE  0x9d
+#define TOK_LE  0x9e
+#define TOK_GT  0x9f
+
+#define TOK_LAND  0xa0
+#define TOK_LOR   0xa1
+
+#define TOK_DEC   0xa2
+#define TOK_MID   0xa3 /* inc/dec, to void constant */
+#define TOK_INC   0xa4
+#define TOK_UDIV  0xb0 /* unsigned division */
+#define TOK_UMOD  0xb1 /* unsigned modulo */
+#define TOK_PDIV  0xb2 /* fast division with undefined rounding for pointers */
+#define TOK_CINT   0xb3 /* number in tokc */
+#define TOK_CCHAR 0xb4 /* char constant in tokc */
+#define TOK_STR   0xb5 /* pointer to string in tokc */
+#define TOK_TWOSHARPS 0xb6 /* ## preprocessing token */
+#define TOK_LCHAR    0xb7
+#define TOK_LSTR     0xb8
+#define TOK_CFLOAT   0xb9 /* float constant */
+#define TOK_LINENUM  0xba /* line number info */
+#define TOK_CDOUBLE  0xc0 /* double constant */
+#define TOK_CLDOUBLE 0xc1 /* long double constant */
+#define TOK_UMULL    0xc2 /* unsigned 32x32 -> 64 mul */
+#define TOK_ADDC1    0xc3 /* add with carry generation */
+#define TOK_ADDC2    0xc4 /* add with carry use */
+#define TOK_SUBC1    0xc5 /* add with carry generation */
+#define TOK_SUBC2    0xc6 /* add with carry use */
+#define TOK_CUINT    0xc8 /* unsigned int constant */
+#define TOK_CLLONG   0xc9 /* long long constant */
+#define TOK_CULLONG  0xca /* unsigned long long constant */
+#define TOK_ARROW    0xcb
+#define TOK_DOTS     0xcc /* three dots */
+#define TOK_SHR      0xcd /* unsigned shift right */
+#define TOK_PPNUM    0xce /* preprocessor number */
+
+#define TOK_SHL   0x01 /* shift left */
+#define TOK_SAR   0x02 /* signed shift right */
+  
+/* assignement operators : normal operator or 0x80 */
+#define TOK_A_MOD 0xa5
+#define TOK_A_AND 0xa6
+#define TOK_A_MUL 0xaa
+#define TOK_A_ADD 0xab
+#define TOK_A_SUB 0xad
+#define TOK_A_DIV 0xaf
+#define TOK_A_XOR 0xde
+#define TOK_A_OR  0xfc
+#define TOK_A_SHL 0x81
+#define TOK_A_SAR 0x82
+
+#ifndef offsetof
+#define offsetof(type, field) ((size_t) &((type *)0)->field)
+#endif
+
+#ifndef countof
+#define countof(tab) (sizeof(tab) / sizeof((tab)[0]))
+#endif
+
+/* WARNING: the content of this string encodes token numbers */
+static char tok_two_chars[] = "<=\236>=\235!=\225&&\240||\241++\244--\242==\224<<\1>>\2+=\253-=\255*=\252/=\257%=\245&=\246^=\336|=\374->\313..\250##\266";
+
+#define TOK_EOF       (-1)  /* end of file */
+#define TOK_LINEFEED  10    /* line feed */
+
+/* all identificators and strings have token above that */
+#define TOK_IDENT 256
+
+/* only used for i386 asm opcodes definitions */
+#define DEF_ASM(x) DEF(TOK_ASM_ ## x, #x)
+
+#define DEF_BWL(x) \
+ DEF(TOK_ASM_ ## x ## b, #x "b") \
+ DEF(TOK_ASM_ ## x ## w, #x "w") \
+ DEF(TOK_ASM_ ## x ## l, #x "l") \
+ DEF(TOK_ASM_ ## x, #x)
+
+#define DEF_WL(x) \
+ DEF(TOK_ASM_ ## x ## w, #x "w") \
+ DEF(TOK_ASM_ ## x ## l, #x "l") \
+ DEF(TOK_ASM_ ## x, #x)
+
+#define DEF_FP1(x) \
+ DEF(TOK_ASM_ ## f ## x ## s, "f" #x "s") \
+ DEF(TOK_ASM_ ## fi ## x ## l, "fi" #x "l") \
+ DEF(TOK_ASM_ ## f ## x ## l, "f" #x "l") \
+ DEF(TOK_ASM_ ## fi ## x ## s, "fi" #x "s")
+
+#define DEF_FP(x) \
+ DEF(TOK_ASM_ ## f ## x, "f" #x ) \
+ DEF(TOK_ASM_ ## f ## x ## p, "f" #x "p") \
+ DEF_FP1(x)
+
+#define DEF_ASMTEST(x) \
+ DEF_ASM(x ## o) \
+ DEF_ASM(x ## no) \
+ DEF_ASM(x ## b) \
+ DEF_ASM(x ## c) \
+ DEF_ASM(x ## nae) \
+ DEF_ASM(x ## nb) \
+ DEF_ASM(x ## nc) \
+ DEF_ASM(x ## ae) \
+ DEF_ASM(x ## e) \
+ DEF_ASM(x ## z) \
+ DEF_ASM(x ## ne) \
+ DEF_ASM(x ## nz) \
+ DEF_ASM(x ## be) \
+ DEF_ASM(x ## na) \
+ DEF_ASM(x ## nbe) \
+ DEF_ASM(x ## a) \
+ DEF_ASM(x ## s) \
+ DEF_ASM(x ## ns) \
+ DEF_ASM(x ## p) \
+ DEF_ASM(x ## pe) \
+ DEF_ASM(x ## np) \
+ DEF_ASM(x ## po) \
+ DEF_ASM(x ## l) \
+ DEF_ASM(x ## nge) \
+ DEF_ASM(x ## nl) \
+ DEF_ASM(x ## ge) \
+ DEF_ASM(x ## le) \
+ DEF_ASM(x ## ng) \
+ DEF_ASM(x ## nle) \
+ DEF_ASM(x ## g)
+
+#define TOK_ASM_int TOK_INT
+
+enum tcc_token {
+    TOK_LAST = TOK_IDENT - 1,
+#define DEF(id, str) id,
+// njn: inlined tcctok.h
+//#include "tcctok.h"
+//---------------------------------------------------------------------------
+/* keywords */
+     DEF(TOK_INT, "int")
+     DEF(TOK_VOID, "void")
+     DEF(TOK_CHAR, "char")
+     DEF(TOK_IF, "if")
+     DEF(TOK_ELSE, "else")
+     DEF(TOK_WHILE, "while")
+     DEF(TOK_BREAK, "break")
+     DEF(TOK_RETURN, "return")
+     DEF(TOK_FOR, "for")
+     DEF(TOK_EXTERN, "extern")
+     DEF(TOK_STATIC, "static")
+     DEF(TOK_UNSIGNED, "unsigned")
+     DEF(TOK_GOTO, "goto")
+     DEF(TOK_DO, "do")
+     DEF(TOK_CONTINUE, "continue")
+     DEF(TOK_SWITCH, "switch")
+     DEF(TOK_CASE, "case")
+
+     DEF(TOK_CONST1, "const")
+     DEF(TOK_CONST2, "__const") /* gcc keyword */
+     DEF(TOK_CONST3, "__const__") /* gcc keyword */
+     DEF(TOK_VOLATILE1, "volatile")
+     DEF(TOK_VOLATILE2, "__volatile") /* gcc keyword */
+     DEF(TOK_VOLATILE3, "__volatile__") /* gcc keyword */
+     DEF(TOK_LONG, "long")
+     DEF(TOK_REGISTER, "register")
+     DEF(TOK_SIGNED1, "signed")
+     DEF(TOK_SIGNED2, "__signed") /* gcc keyword */
+     DEF(TOK_SIGNED3, "__signed__") /* gcc keyword */
+     DEF(TOK_AUTO, "auto")
+     DEF(TOK_INLINE1, "inline")
+     DEF(TOK_INLINE2, "__inline") /* gcc keyword */
+     DEF(TOK_INLINE3, "__inline__") /* gcc keyword */
+     DEF(TOK_RESTRICT1, "restrict")
+     DEF(TOK_RESTRICT2, "__restrict")
+     DEF(TOK_RESTRICT3, "__restrict__")
+     DEF(TOK_EXTENSION, "__extension__") /* gcc keyword */
+     
+     DEF(TOK_FLOAT, "float")
+     DEF(TOK_DOUBLE, "double")
+     DEF(TOK_BOOL, "_Bool")
+     DEF(TOK_SHORT, "short")
+     DEF(TOK_STRUCT, "struct")
+     DEF(TOK_UNION, "union")
+     DEF(TOK_TYPEDEF, "typedef")
+     DEF(TOK_DEFAULT, "default")
+     DEF(TOK_ENUM, "enum")
+     DEF(TOK_SIZEOF, "sizeof")
+     DEF(TOK_ATTRIBUTE1, "__attribute")
+     DEF(TOK_ATTRIBUTE2, "__attribute__")
+     DEF(TOK_ALIGNOF1, "__alignof")
+     DEF(TOK_ALIGNOF2, "__alignof__")
+     DEF(TOK_TYPEOF1, "typeof")
+     DEF(TOK_TYPEOF2, "__typeof")
+     DEF(TOK_TYPEOF3, "__typeof__")
+     DEF(TOK_LABEL, "__label__")
+     DEF(TOK_ASM1, "asm")
+     DEF(TOK_ASM2, "__asm")
+     DEF(TOK_ASM3, "__asm__")
+
+/*********************************************************************/
+/* the following are not keywords. They are included to ease parsing */
+/* preprocessor only */
+     DEF(TOK_DEFINE, "define")
+     DEF(TOK_INCLUDE, "include")
+     DEF(TOK_INCLUDE_NEXT, "include_next")
+     DEF(TOK_IFDEF, "ifdef")
+     DEF(TOK_IFNDEF, "ifndef")
+     DEF(TOK_ELIF, "elif")
+     DEF(TOK_ENDIF, "endif")
+     DEF(TOK_DEFINED, "defined")
+     DEF(TOK_UNDEF, "undef")
+     DEF(TOK_ERROR, "error")
+     DEF(TOK_WARNING, "warning")
+     DEF(TOK_LINE, "line")
+     DEF(TOK_PRAGMA, "pragma")
+     DEF(TOK___LINE__, "__LINE__")
+     DEF(TOK___FILE__, "__FILE__")
+     DEF(TOK___DATE__, "__DATE__")
+     DEF(TOK___TIME__, "__TIME__")
+     DEF(TOK___FUNCTION__, "__FUNCTION__")
+     DEF(TOK___VA_ARGS__, "__VA_ARGS__")
+     
+/* special identifiers */
+     DEF(TOK___FUNC__, "__func__")
+     
+/* attribute identifiers */
+/* XXX: handle all tokens generically since speed is not critical */
+     DEF(TOK_SECTION1, "section")
+     DEF(TOK_SECTION2, "__section__")
+     DEF(TOK_ALIGNED1, "aligned")
+     DEF(TOK_ALIGNED2, "__aligned__")
+     DEF(TOK_PACKED1, "packed")
+     DEF(TOK_PACKED2, "__packed__")
+     DEF(TOK_UNUSED1, "unused")
+     DEF(TOK_UNUSED2, "__unused__")
+     DEF(TOK_CDECL1, "cdecl")
+     DEF(TOK_CDECL2, "__cdecl")
+     DEF(TOK_CDECL3, "__cdecl__")
+     DEF(TOK_STDCALL1, "stdcall")
+     DEF(TOK_STDCALL2, "__stdcall")
+     DEF(TOK_STDCALL3, "__stdcall__")
+     DEF(TOK_DLLEXPORT, "dllexport")
+     DEF(TOK_NORETURN1, "noreturn")
+     DEF(TOK_NORETURN2, "__noreturn__")
+     DEF(TOK_builtin_types_compatible_p, "__builtin_types_compatible_p")
+     DEF(TOK_builtin_constant_p, "__builtin_constant_p")
+     DEF(TOK_REGPARM1, "regparm")
+     DEF(TOK_REGPARM2, "__regparm__")
+
+/* pragma */
+     DEF(TOK_pack, "pack")
+#if !defined(TCC_TARGET_I386)
+     /* already defined for assembler */
+     DEF(TOK_ASM_push, "push")
+     DEF(TOK_ASM_pop, "pop")
+#endif
+
+/* builtin functions or variables */
+     DEF(TOK_memcpy, "memcpy")
+     DEF(TOK_memset, "memset")
+     DEF(TOK_alloca, "alloca")
+     DEF(TOK___divdi3, "__divdi3")
+     DEF(TOK___moddi3, "__moddi3")
+     DEF(TOK___udivdi3, "__udivdi3")
+     DEF(TOK___umoddi3, "__umoddi3")
+#if defined(TCC_TARGET_ARM)
+     DEF(TOK___divsi3, "__divsi3")
+     DEF(TOK___modsi3, "__modsi3")
+     DEF(TOK___udivsi3, "__udivsi3")
+     DEF(TOK___umodsi3, "__umodsi3")
+     DEF(TOK___sardi3, "__ashrdi3")
+     DEF(TOK___shrdi3, "__lshrdi3")
+     DEF(TOK___shldi3, "__ashldi3")
+     DEF(TOK___slltold, "__slltold")
+     DEF(TOK___fixunssfsi, "__fixunssfsi")
+     DEF(TOK___fixunsdfsi, "__fixunsdfsi")
+     DEF(TOK___fixunsxfsi, "__fixunsxfsi")
+     DEF(TOK___fixsfdi, "__fixsfdi")
+     DEF(TOK___fixdfdi, "__fixdfdi")
+     DEF(TOK___fixxfdi, "__fixxfdi")
+#elif defined(TCC_TARGET_C67)
+     DEF(TOK__divi, "_divi")
+     DEF(TOK__divu, "_divu")
+     DEF(TOK__divf, "_divf")
+     DEF(TOK__divd, "_divd")
+     DEF(TOK__remi, "_remi")
+     DEF(TOK__remu, "_remu")
+     DEF(TOK___sardi3, "__sardi3")
+     DEF(TOK___shrdi3, "__shrdi3")
+     DEF(TOK___shldi3, "__shldi3")
+#else
+     /* XXX: same names on i386 ? */
+     DEF(TOK___sardi3, "__sardi3")
+     DEF(TOK___shrdi3, "__shrdi3")
+     DEF(TOK___shldi3, "__shldi3")
+#endif
+     DEF(TOK___tcc_int_fpu_control, "__tcc_int_fpu_control")
+     DEF(TOK___tcc_fpu_control, "__tcc_fpu_control")
+     DEF(TOK___ulltof, "__ulltof")
+     DEF(TOK___ulltod, "__ulltod")
+     DEF(TOK___ulltold, "__ulltold")
+     DEF(TOK___fixunssfdi, "__fixunssfdi")
+     DEF(TOK___fixunsdfdi, "__fixunsdfdi")
+     DEF(TOK___fixunsxfdi, "__fixunsxfdi")
+     DEF(TOK___chkstk, "__chkstk")
+
+/* bound checking symbols */
+#ifdef CONFIG_TCC_BCHECK
+     DEF(TOK___bound_ptr_add, "__bound_ptr_add")
+     DEF(TOK___bound_ptr_indir1, "__bound_ptr_indir1")
+     DEF(TOK___bound_ptr_indir2, "__bound_ptr_indir2")
+     DEF(TOK___bound_ptr_indir4, "__bound_ptr_indir4")
+     DEF(TOK___bound_ptr_indir8, "__bound_ptr_indir8")
+     DEF(TOK___bound_ptr_indir12, "__bound_ptr_indir12")
+     DEF(TOK___bound_ptr_indir16, "__bound_ptr_indir16")
+     DEF(TOK___bound_local_new, "__bound_local_new")
+     DEF(TOK___bound_local_delete, "__bound_local_delete")
+     DEF(TOK_malloc, "malloc")
+     DEF(TOK_free, "free")
+     DEF(TOK_realloc, "realloc")
+     DEF(TOK_memalign, "memalign")
+     DEF(TOK_calloc, "calloc")
+     DEF(TOK_memmove, "memmove")
+     DEF(TOK_strlen, "strlen")
+     DEF(TOK_strcpy, "strcpy")
+#endif
+
+/* Tiny Assembler */
+
+ DEF_ASM(byte)
+ DEF_ASM(align)
+ DEF_ASM(skip)
+ DEF_ASM(space)
+ DEF_ASM(string)
+ DEF_ASM(asciz)
+ DEF_ASM(ascii)
+ DEF_ASM(globl)
+ DEF_ASM(global)
+ DEF_ASM(text)
+ DEF_ASM(data)
+ DEF_ASM(bss)
+ DEF_ASM(previous)
+ DEF_ASM(fill)
+ DEF_ASM(org)
+ DEF_ASM(quad)
+
+#ifdef TCC_TARGET_I386
+
+/* WARNING: relative order of tokens is important. */
+ DEF_ASM(al)
+ DEF_ASM(cl)
+ DEF_ASM(dl)
+ DEF_ASM(bl)
+ DEF_ASM(ah)
+ DEF_ASM(ch)
+ DEF_ASM(dh)
+ DEF_ASM(bh)
+ DEF_ASM(ax)
+ DEF_ASM(cx)
+ DEF_ASM(dx)
+ DEF_ASM(bx)
+ DEF_ASM(sp)
+ DEF_ASM(bp)
+ DEF_ASM(si)
+ DEF_ASM(di)
+ DEF_ASM(eax)
+ DEF_ASM(ecx)
+ DEF_ASM(edx)
+ DEF_ASM(ebx)
+ DEF_ASM(esp)
+ DEF_ASM(ebp)
+ DEF_ASM(esi)
+ DEF_ASM(edi)
+ DEF_ASM(mm0)
+ DEF_ASM(mm1)
+ DEF_ASM(mm2)
+ DEF_ASM(mm3)
+ DEF_ASM(mm4)
+ DEF_ASM(mm5)
+ DEF_ASM(mm6)
+ DEF_ASM(mm7)
+ DEF_ASM(xmm0)
+ DEF_ASM(xmm1)
+ DEF_ASM(xmm2)
+ DEF_ASM(xmm3)
+ DEF_ASM(xmm4)
+ DEF_ASM(xmm5)
+ DEF_ASM(xmm6)
+ DEF_ASM(xmm7)
+ DEF_ASM(cr0)
+ DEF_ASM(cr1)
+ DEF_ASM(cr2)
+ DEF_ASM(cr3)
+ DEF_ASM(cr4)
+ DEF_ASM(cr5)
+ DEF_ASM(cr6)
+ DEF_ASM(cr7)
+ DEF_ASM(tr0)
+ DEF_ASM(tr1)
+ DEF_ASM(tr2)
+ DEF_ASM(tr3)
+ DEF_ASM(tr4)
+ DEF_ASM(tr5)
+ DEF_ASM(tr6)
+ DEF_ASM(tr7)
+ DEF_ASM(db0)
+ DEF_ASM(db1)
+ DEF_ASM(db2)
+ DEF_ASM(db3)
+ DEF_ASM(db4)
+ DEF_ASM(db5)
+ DEF_ASM(db6)
+ DEF_ASM(db7)
+ DEF_ASM(dr0)
+ DEF_ASM(dr1)
+ DEF_ASM(dr2)
+ DEF_ASM(dr3)
+ DEF_ASM(dr4)
+ DEF_ASM(dr5)
+ DEF_ASM(dr6)
+ DEF_ASM(dr7)
+ DEF_ASM(es)
+ DEF_ASM(cs)
+ DEF_ASM(ss)
+ DEF_ASM(ds)
+ DEF_ASM(fs)
+ DEF_ASM(gs)
+ DEF_ASM(st)
+
+ DEF_BWL(mov)
+
+ /* generic two operands */
+ DEF_BWL(add)
+ DEF_BWL(or)
+ DEF_BWL(adc)
+ DEF_BWL(sbb)
+ DEF_BWL(and)
+ DEF_BWL(sub)
+ DEF_BWL(xor)
+ DEF_BWL(cmp)
+
+ /* unary ops */
+ DEF_BWL(inc)
+ DEF_BWL(dec)
+ DEF_BWL(not)
+ DEF_BWL(neg)
+ DEF_BWL(mul)
+ DEF_BWL(imul)
+ DEF_BWL(div)
+ DEF_BWL(idiv)
+
+ DEF_BWL(xchg)
+ DEF_BWL(test)
+
+ /* shifts */
+ DEF_BWL(rol)
+ DEF_BWL(ror)
+ DEF_BWL(rcl)
+ DEF_BWL(rcr)
+ DEF_BWL(shl)
+ DEF_BWL(shr)
+ DEF_BWL(sar)
+
+ DEF_ASM(shldw)
+ DEF_ASM(shldl)
+ DEF_ASM(shld)
+ DEF_ASM(shrdw)
+ DEF_ASM(shrdl)
+ DEF_ASM(shrd)
+
+ DEF_ASM(pushw)
+ DEF_ASM(pushl)
+ DEF_ASM(push)
+ DEF_ASM(popw)
+ DEF_ASM(popl)
+ DEF_ASM(pop)
+ DEF_BWL(in)
+ DEF_BWL(out)
+
+ DEF_WL(movzb)
+
+ DEF_ASM(movzwl)
+ DEF_ASM(movsbw)
+ DEF_ASM(movsbl)
+ DEF_ASM(movswl)
+
+ DEF_WL(lea) 
+
+ DEF_ASM(les) 
+ DEF_ASM(lds) 
+ DEF_ASM(lss) 
+ DEF_ASM(lfs) 
+ DEF_ASM(lgs) 
+
+ DEF_ASM(call)
+ DEF_ASM(jmp)
+ DEF_ASM(lcall)
+ DEF_ASM(ljmp)
+ DEF_ASMTEST(j)
+
+ DEF_ASMTEST(set)
+ DEF_ASMTEST(cmov)
+
+ DEF_WL(bsf)
+ DEF_WL(bsr)
+ DEF_WL(bt)
+ DEF_WL(bts)
+ DEF_WL(btr)
+ DEF_WL(btc)
+
+ DEF_WL(lsl)
+
+ /* generic FP ops */
+ DEF_FP(add)
+ DEF_FP(mul)
+
+ DEF_ASM(fcom)
+ DEF_ASM(fcom_1) /* non existant op, just to have a regular table */
+ DEF_FP1(com)
+
+ DEF_FP(comp)
+ DEF_FP(sub)
+ DEF_FP(subr)
+ DEF_FP(div)
+ DEF_FP(divr)
+
+ DEF_BWL(xadd)
+ DEF_BWL(cmpxchg)
+
+ /* string ops */
+ DEF_BWL(cmps)
+ DEF_BWL(scmp)
+ DEF_BWL(ins)
+ DEF_BWL(outs)
+ DEF_BWL(lods)
+ DEF_BWL(slod)
+ DEF_BWL(movs)
+ DEF_BWL(smov)
+ DEF_BWL(scas)
+ DEF_BWL(ssca)
+ DEF_BWL(stos)
+ DEF_BWL(ssto)
+
+ /* generic asm ops */
+
+#define ALT(x)
+#define DEF_ASM_OP0(name, opcode) DEF_ASM(name)
+#define DEF_ASM_OP0L(name, opcode, group, instr_type)
+#define DEF_ASM_OP1(name, opcode, group, instr_type, op0)
+#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1)
+#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2)
+// njn: inlined i386-asm.h
+//#include "i386-asm.h"
+//---------------------------------------------------------------------------
+     DEF_ASM_OP0(pusha, 0x60) /* must be first OP0 */
+     DEF_ASM_OP0(popa, 0x61)
+     DEF_ASM_OP0(clc, 0xf8)
+     DEF_ASM_OP0(cld, 0xfc)
+     DEF_ASM_OP0(cli, 0xfa)
+     DEF_ASM_OP0(clts, 0x0f06)
+     DEF_ASM_OP0(cmc, 0xf5)
+     DEF_ASM_OP0(lahf, 0x9f)
+     DEF_ASM_OP0(sahf, 0x9e)
+     DEF_ASM_OP0(pushfl, 0x9c)
+     DEF_ASM_OP0(popfl, 0x9d)
+     DEF_ASM_OP0(pushf, 0x9c)
+     DEF_ASM_OP0(popf, 0x9d)
+     DEF_ASM_OP0(stc, 0xf9)
+     DEF_ASM_OP0(std, 0xfd)
+     DEF_ASM_OP0(sti, 0xfb)
+     DEF_ASM_OP0(aaa, 0x37)
+     DEF_ASM_OP0(aas, 0x3f)
+     DEF_ASM_OP0(daa, 0x27)
+     DEF_ASM_OP0(das, 0x2f)
+     DEF_ASM_OP0(aad, 0xd50a)
+     DEF_ASM_OP0(aam, 0xd40a)
+     DEF_ASM_OP0(cbw, 0x6698)
+     DEF_ASM_OP0(cwd, 0x6699)
+     DEF_ASM_OP0(cwde, 0x98)
+     DEF_ASM_OP0(cdq, 0x99)
+     DEF_ASM_OP0(cbtw, 0x6698)
+     DEF_ASM_OP0(cwtl, 0x98)
+     DEF_ASM_OP0(cwtd, 0x6699)
+     DEF_ASM_OP0(cltd, 0x99)
+     DEF_ASM_OP0(int3, 0xcc)
+     DEF_ASM_OP0(into, 0xce)
+     DEF_ASM_OP0(iret, 0xcf)
+     DEF_ASM_OP0(rsm, 0x0faa)
+     DEF_ASM_OP0(hlt, 0xf4)
+     DEF_ASM_OP0(wait, 0x9b)
+     DEF_ASM_OP0(nop, 0x90)
+     DEF_ASM_OP0(xlat, 0xd7)
+
+     /* strings */
+ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWL))
+
+     /* bits */
+     
+ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
+
+ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+     /* prefixes */
+     DEF_ASM_OP0(aword, 0x67)
+     DEF_ASM_OP0(addr16, 0x67)
+     DEF_ASM_OP0(word, 0x66)
+     DEF_ASM_OP0(data16, 0x66)
+     DEF_ASM_OP0(lock, 0xf0)
+     DEF_ASM_OP0(rep, 0xf3)
+     DEF_ASM_OP0(repe, 0xf3)
+     DEF_ASM_OP0(repz, 0xf3)
+     DEF_ASM_OP0(repne, 0xf2)
+     DEF_ASM_OP0(repnz, 0xf2)
+             
+     DEF_ASM_OP0(invd, 0x0f08)
+     DEF_ASM_OP0(wbinvd, 0x0f09)
+     DEF_ASM_OP0(cpuid, 0x0fa2)
+     DEF_ASM_OP0(wrmsr, 0x0f30)
+     DEF_ASM_OP0(rdtsc, 0x0f31)
+     DEF_ASM_OP0(rdmsr, 0x0f32)
+     DEF_ASM_OP0(rdpmc, 0x0f33)
+     DEF_ASM_OP0(ud2, 0x0f0b)
+
+     /* NOTE: we took the same order as gas opcode definition order */
+ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWL, OPT_ADDR, OPT_EAX))
+ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWL, OPT_EAX, OPT_ADDR))
+ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWL, OPT_IM, OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WL, OPT_SEG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_SEG))
+
+ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WL, OPT_CR, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WL, OPT_DB, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WL, OPT_TR, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_CR))
+ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_DB))
+ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_TR))
+
+ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16))
+ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
+
+ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP1(pushw, 0x6a, 0, OPC_WL, OPT_IM8S))
+ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WL, OPT_IM32))
+ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WL, OPT_SEG))
+
+ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WL, OPT_SEG))
+
+ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_REG, OPT_EAX))
+ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_EAX, OPT_REG))
+ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+
+ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX))
+ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8))
+ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX))
+ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX))
+
+ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8))
+ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8))
+ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX))
+ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX))
+
+ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WL, OPT_EA, OPT_REG))
+
+ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+
+     /* arith */
+ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */
+ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWL, OPT_IM, OPT_EAX))
+ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WL, OPT_IM8S, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWL, OPT_IM, OPT_EAX))
+ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WL, OPT_REG | OPT_EA, OPT_REG))
+ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW))
+ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW))
+
+ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
+ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
+
+     /* shifts */
+ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
+
+ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR))
+ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR))
+ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR))
+ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR))
+
+ALT(DEF_ASM_OP2(lcall, 0x9a, 0, 0, OPT_IM16, OPT_IM32))
+ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA))
+ALT(DEF_ASM_OP2(ljmp, 0xea, 0, 0, OPT_IM16, OPT_IM32))
+ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA))
+
+ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8))
+ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
+    DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8)
+    DEF_ASM_OP0(leave, 0xc9)
+    DEF_ASM_OP0(ret, 0xc3)
+ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16))
+    DEF_ASM_OP0(lret, 0xcb)
+ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
+
+ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR))
+    DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR)
+     
+     /* float */
+     /* specific fcomp handling */
+ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0))
+
+ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST))
+ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
+ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH))
+ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST))
+ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
+ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
+ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH))
+ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+
+     DEF_ASM_OP0(fucompp, 0xdae9)
+     DEF_ASM_OP0(ftst, 0xd9e4)
+     DEF_ASM_OP0(fxam, 0xd9e5)
+     DEF_ASM_OP0(fld1, 0xd9e8)
+     DEF_ASM_OP0(fldl2t, 0xd9e9)
+     DEF_ASM_OP0(fldl2e, 0xd9ea)
+     DEF_ASM_OP0(fldpi, 0xd9eb)
+     DEF_ASM_OP0(fldlg2, 0xd9ec)
+     DEF_ASM_OP0(fldln2, 0xd9ed)
+     DEF_ASM_OP0(fldz, 0xd9ee)
+
+     DEF_ASM_OP0(f2xm1, 0xd9f0)
+     DEF_ASM_OP0(fyl2x, 0xd9f1)
+     DEF_ASM_OP0(fptan, 0xd9f2)
+     DEF_ASM_OP0(fpatan, 0xd9f3)
+     DEF_ASM_OP0(fxtract, 0xd9f4)
+     DEF_ASM_OP0(fprem1, 0xd9f5)
+     DEF_ASM_OP0(fdecstp, 0xd9f6)
+     DEF_ASM_OP0(fincstp, 0xd9f7)
+     DEF_ASM_OP0(fprem, 0xd9f8)
+     DEF_ASM_OP0(fyl2xp1, 0xd9f9)
+     DEF_ASM_OP0(fsqrt, 0xd9fa)
+     DEF_ASM_OP0(fsincos, 0xd9fb)
+     DEF_ASM_OP0(frndint, 0xd9fc)
+     DEF_ASM_OP0(fscale, 0xd9fd)
+     DEF_ASM_OP0(fsin, 0xd9fe)
+     DEF_ASM_OP0(fcos, 0xd9ff)
+     DEF_ASM_OP0(fchs, 0xd9e0)
+     DEF_ASM_OP0(fabs, 0xd9e1)
+     DEF_ASM_OP0(fninit, 0xdbe3)
+     DEF_ASM_OP0(fnclex, 0xdbe2)
+     DEF_ASM_OP0(fnop, 0xd9d0)
+     DEF_ASM_OP0(fwait, 0x9b)
+
+    /* fp load */
+    DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA)
+ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA))
+    DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA)
+    DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA)
+    
+    /* fp store */
+    DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA)
+ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA))
+    DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA)
+
+    DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA)
+
+    /* exchange */
+    DEF_ASM_OP0(fxch, 0xd9c9)
+ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST))
+
+    /* misc FPU */
+    DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST )
+    DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST )
+
+    DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT)
+    DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )
+    DEF_ASM_OP0(fnstsw, 0xdfe0)
+ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX ))
+ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA ))
+    DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX )
+ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT))
+ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ))
+    DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT)
+    DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
+    DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
+    DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST )
+    DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST )
+    DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA )
+
+    /* segments */
+    DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA)
+    DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32)
+    DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG)
+    DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG)
+ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG))
+    DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG)
+    DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA)
+    DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA)
+    DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA)
+    DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA)
+    DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA)
+
+    /* 486 */
+    DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 )
+ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
+ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
+    DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA )
+
+    DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA)
+    DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA)
+
+    /* pentium */
+    DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA )
+    
+    /* pentium pro */
+    ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+
+    DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+
+    DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+
+    /* mmx */
+    DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */
+    DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX )
+ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 ))
+    DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX ))
+    DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+
+#undef ALT
+#undef DEF_ASM_OP0
+#undef DEF_ASM_OP0L
+#undef DEF_ASM_OP1
+#undef DEF_ASM_OP2
+#undef DEF_ASM_OP3
+//---------------------------------------------------------------------------
+
+#define ALT(x)
+#define DEF_ASM_OP0(name, opcode)
+#define DEF_ASM_OP0L(name, opcode, group, instr_type) DEF_ASM(name)
+#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) DEF_ASM(name)
+#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) DEF_ASM(name)
+#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) DEF_ASM(name)
+// njn: inlined i386-asm.h
+//#include "i386-asm.h"
+//---------------------------------------------------------------------------
+     DEF_ASM_OP0(pusha, 0x60) /* must be first OP0 */
+     DEF_ASM_OP0(popa, 0x61)
+     DEF_ASM_OP0(clc, 0xf8)
+     DEF_ASM_OP0(cld, 0xfc)
+     DEF_ASM_OP0(cli, 0xfa)
+     DEF_ASM_OP0(clts, 0x0f06)
+     DEF_ASM_OP0(cmc, 0xf5)
+     DEF_ASM_OP0(lahf, 0x9f)
+     DEF_ASM_OP0(sahf, 0x9e)
+     DEF_ASM_OP0(pushfl, 0x9c)
+     DEF_ASM_OP0(popfl, 0x9d)
+     DEF_ASM_OP0(pushf, 0x9c)
+     DEF_ASM_OP0(popf, 0x9d)
+     DEF_ASM_OP0(stc, 0xf9)
+     DEF_ASM_OP0(std, 0xfd)
+     DEF_ASM_OP0(sti, 0xfb)
+     DEF_ASM_OP0(aaa, 0x37)
+     DEF_ASM_OP0(aas, 0x3f)
+     DEF_ASM_OP0(daa, 0x27)
+     DEF_ASM_OP0(das, 0x2f)
+     DEF_ASM_OP0(aad, 0xd50a)
+     DEF_ASM_OP0(aam, 0xd40a)
+     DEF_ASM_OP0(cbw, 0x6698)
+     DEF_ASM_OP0(cwd, 0x6699)
+     DEF_ASM_OP0(cwde, 0x98)
+     DEF_ASM_OP0(cdq, 0x99)
+     DEF_ASM_OP0(cbtw, 0x6698)
+     DEF_ASM_OP0(cwtl, 0x98)
+     DEF_ASM_OP0(cwtd, 0x6699)
+     DEF_ASM_OP0(cltd, 0x99)
+     DEF_ASM_OP0(int3, 0xcc)
+     DEF_ASM_OP0(into, 0xce)
+     DEF_ASM_OP0(iret, 0xcf)
+     DEF_ASM_OP0(rsm, 0x0faa)
+     DEF_ASM_OP0(hlt, 0xf4)
+     DEF_ASM_OP0(wait, 0x9b)
+     DEF_ASM_OP0(nop, 0x90)
+     DEF_ASM_OP0(xlat, 0xd7)
+
+     /* strings */
+ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWL))
+
+     /* bits */
+     
+ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
+
+ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+     /* prefixes */
+     DEF_ASM_OP0(aword, 0x67)
+     DEF_ASM_OP0(addr16, 0x67)
+     DEF_ASM_OP0(word, 0x66)
+     DEF_ASM_OP0(data16, 0x66)
+     DEF_ASM_OP0(lock, 0xf0)
+     DEF_ASM_OP0(rep, 0xf3)
+     DEF_ASM_OP0(repe, 0xf3)
+     DEF_ASM_OP0(repz, 0xf3)
+     DEF_ASM_OP0(repne, 0xf2)
+     DEF_ASM_OP0(repnz, 0xf2)
+             
+     DEF_ASM_OP0(invd, 0x0f08)
+     DEF_ASM_OP0(wbinvd, 0x0f09)
+     DEF_ASM_OP0(cpuid, 0x0fa2)
+     DEF_ASM_OP0(wrmsr, 0x0f30)
+     DEF_ASM_OP0(rdtsc, 0x0f31)
+     DEF_ASM_OP0(rdmsr, 0x0f32)
+     DEF_ASM_OP0(rdpmc, 0x0f33)
+     DEF_ASM_OP0(ud2, 0x0f0b)
+
+     /* NOTE: we took the same order as gas opcode definition order */
+ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWL, OPT_ADDR, OPT_EAX))
+ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWL, OPT_EAX, OPT_ADDR))
+ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWL, OPT_IM, OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WL, OPT_SEG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_SEG))
+
+ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WL, OPT_CR, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WL, OPT_DB, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WL, OPT_TR, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_CR))
+ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_DB))
+ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_TR))
+
+ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16))
+ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
+
+ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP1(pushw, 0x6a, 0, OPC_WL, OPT_IM8S))
+ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WL, OPT_IM32))
+ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WL, OPT_SEG))
+
+ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WL, OPT_SEG))
+
+ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_REG, OPT_EAX))
+ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_EAX, OPT_REG))
+ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+
+ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX))
+ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8))
+ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX))
+ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX))
+
+ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8))
+ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8))
+ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX))
+ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX))
+
+ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WL, OPT_EA, OPT_REG))
+
+ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+
+     /* arith */
+ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */
+ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWL, OPT_IM, OPT_EAX))
+ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WL, OPT_IM8S, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWL, OPT_IM, OPT_EAX))
+ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WL, OPT_REG | OPT_EA, OPT_REG))
+ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW))
+ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW))
+
+ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
+ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
+
+     /* shifts */
+ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
+
+ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR))
+ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR))
+ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR))
+ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR))
+
+ALT(DEF_ASM_OP2(lcall, 0x9a, 0, 0, OPT_IM16, OPT_IM32))
+ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA))
+ALT(DEF_ASM_OP2(ljmp, 0xea, 0, 0, OPT_IM16, OPT_IM32))
+ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA))
+
+ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8))
+ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
+    DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8)
+    DEF_ASM_OP0(leave, 0xc9)
+    DEF_ASM_OP0(ret, 0xc3)
+ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16))
+    DEF_ASM_OP0(lret, 0xcb)
+ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
+
+ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR))
+    DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR)
+     
+     /* float */
+     /* specific fcomp handling */
+ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0))
+
+ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST))
+ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
+ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH))
+ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST))
+ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
+ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
+ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH))
+ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+
+     DEF_ASM_OP0(fucompp, 0xdae9)
+     DEF_ASM_OP0(ftst, 0xd9e4)
+     DEF_ASM_OP0(fxam, 0xd9e5)
+     DEF_ASM_OP0(fld1, 0xd9e8)
+     DEF_ASM_OP0(fldl2t, 0xd9e9)
+     DEF_ASM_OP0(fldl2e, 0xd9ea)
+     DEF_ASM_OP0(fldpi, 0xd9eb)
+     DEF_ASM_OP0(fldlg2, 0xd9ec)
+     DEF_ASM_OP0(fldln2, 0xd9ed)
+     DEF_ASM_OP0(fldz, 0xd9ee)
+
+     DEF_ASM_OP0(f2xm1, 0xd9f0)
+     DEF_ASM_OP0(fyl2x, 0xd9f1)
+     DEF_ASM_OP0(fptan, 0xd9f2)
+     DEF_ASM_OP0(fpatan, 0xd9f3)
+     DEF_ASM_OP0(fxtract, 0xd9f4)
+     DEF_ASM_OP0(fprem1, 0xd9f5)
+     DEF_ASM_OP0(fdecstp, 0xd9f6)
+     DEF_ASM_OP0(fincstp, 0xd9f7)
+     DEF_ASM_OP0(fprem, 0xd9f8)
+     DEF_ASM_OP0(fyl2xp1, 0xd9f9)
+     DEF_ASM_OP0(fsqrt, 0xd9fa)
+     DEF_ASM_OP0(fsincos, 0xd9fb)
+     DEF_ASM_OP0(frndint, 0xd9fc)
+     DEF_ASM_OP0(fscale, 0xd9fd)
+     DEF_ASM_OP0(fsin, 0xd9fe)
+     DEF_ASM_OP0(fcos, 0xd9ff)
+     DEF_ASM_OP0(fchs, 0xd9e0)
+     DEF_ASM_OP0(fabs, 0xd9e1)
+     DEF_ASM_OP0(fninit, 0xdbe3)
+     DEF_ASM_OP0(fnclex, 0xdbe2)
+     DEF_ASM_OP0(fnop, 0xd9d0)
+     DEF_ASM_OP0(fwait, 0x9b)
+
+    /* fp load */
+    DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA)
+ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA))
+    DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA)
+    DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA)
+    
+    /* fp store */
+    DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA)
+ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA))
+    DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA)
+
+    DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA)
+
+    /* exchange */
+    DEF_ASM_OP0(fxch, 0xd9c9)
+ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST))
+
+    /* misc FPU */
+    DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST )
+    DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST )
+
+    DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT)
+    DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )
+    DEF_ASM_OP0(fnstsw, 0xdfe0)
+ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX ))
+ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA ))
+    DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX )
+ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT))
+ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ))
+    DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT)
+    DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
+    DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
+    DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST )
+    DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST )
+    DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA )
+
+    /* segments */
+    DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA)
+    DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32)
+    DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG)
+    DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG)
+ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG))
+    DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG)
+    DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA)
+    DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA)
+    DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA)
+    DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA)
+    DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA)
+
+    /* 486 */
+    DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 )
+ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
+ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
+    DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA )
+
+    DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA)
+    DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA)
+
+    /* pentium */
+    DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA )
+    
+    /* pentium pro */
+    ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+
+    DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+
+    DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+
+    /* mmx */
+    DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */
+    DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX )
+ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 ))
+    DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX ))
+    DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+
+#undef ALT
+#undef DEF_ASM_OP0
+#undef DEF_ASM_OP0L
+#undef DEF_ASM_OP1
+#undef DEF_ASM_OP2
+#undef DEF_ASM_OP3
+//---------------------------------------------------------------------------
+
+#endif
+//---------------------------------------------------------------------------
+#undef DEF
+};
+
+static const char tcc_keywords[] = 
+#define DEF(id, str) str "\0"
+// njn: inlined tcctok.h
+//#include "tcctok.h"
+//---------------------------------------------------------------------------
+/* keywords */
+     DEF(TOK_INT, "int")
+     DEF(TOK_VOID, "void")
+     DEF(TOK_CHAR, "char")
+     DEF(TOK_IF, "if")
+     DEF(TOK_ELSE, "else")
+     DEF(TOK_WHILE, "while")
+     DEF(TOK_BREAK, "break")
+     DEF(TOK_RETURN, "return")
+     DEF(TOK_FOR, "for")
+     DEF(TOK_EXTERN, "extern")
+     DEF(TOK_STATIC, "static")
+     DEF(TOK_UNSIGNED, "unsigned")
+     DEF(TOK_GOTO, "goto")
+     DEF(TOK_DO, "do")
+     DEF(TOK_CONTINUE, "continue")
+     DEF(TOK_SWITCH, "switch")
+     DEF(TOK_CASE, "case")
+
+     DEF(TOK_CONST1, "const")
+     DEF(TOK_CONST2, "__const") /* gcc keyword */
+     DEF(TOK_CONST3, "__const__") /* gcc keyword */
+     DEF(TOK_VOLATILE1, "volatile")
+     DEF(TOK_VOLATILE2, "__volatile") /* gcc keyword */
+     DEF(TOK_VOLATILE3, "__volatile__") /* gcc keyword */
+     DEF(TOK_LONG, "long")
+     DEF(TOK_REGISTER, "register")
+     DEF(TOK_SIGNED1, "signed")
+     DEF(TOK_SIGNED2, "__signed") /* gcc keyword */
+     DEF(TOK_SIGNED3, "__signed__") /* gcc keyword */
+     DEF(TOK_AUTO, "auto")
+     DEF(TOK_INLINE1, "inline")
+     DEF(TOK_INLINE2, "__inline") /* gcc keyword */
+     DEF(TOK_INLINE3, "__inline__") /* gcc keyword */
+     DEF(TOK_RESTRICT1, "restrict")
+     DEF(TOK_RESTRICT2, "__restrict")
+     DEF(TOK_RESTRICT3, "__restrict__")
+     DEF(TOK_EXTENSION, "__extension__") /* gcc keyword */
+     
+     DEF(TOK_FLOAT, "float")
+     DEF(TOK_DOUBLE, "double")
+     DEF(TOK_BOOL, "_Bool")
+     DEF(TOK_SHORT, "short")
+     DEF(TOK_STRUCT, "struct")
+     DEF(TOK_UNION, "union")
+     DEF(TOK_TYPEDEF, "typedef")
+     DEF(TOK_DEFAULT, "default")
+     DEF(TOK_ENUM, "enum")
+     DEF(TOK_SIZEOF, "sizeof")
+     DEF(TOK_ATTRIBUTE1, "__attribute")
+     DEF(TOK_ATTRIBUTE2, "__attribute__")
+     DEF(TOK_ALIGNOF1, "__alignof")
+     DEF(TOK_ALIGNOF2, "__alignof__")
+     DEF(TOK_TYPEOF1, "typeof")
+     DEF(TOK_TYPEOF2, "__typeof")
+     DEF(TOK_TYPEOF3, "__typeof__")
+     DEF(TOK_LABEL, "__label__")
+     DEF(TOK_ASM1, "asm")
+     DEF(TOK_ASM2, "__asm")
+     DEF(TOK_ASM3, "__asm__")
+
+/*********************************************************************/
+/* the following are not keywords. They are included to ease parsing */
+/* preprocessor only */
+     DEF(TOK_DEFINE, "define")
+     DEF(TOK_INCLUDE, "include")
+     DEF(TOK_INCLUDE_NEXT, "include_next")
+     DEF(TOK_IFDEF, "ifdef")
+     DEF(TOK_IFNDEF, "ifndef")
+     DEF(TOK_ELIF, "elif")
+     DEF(TOK_ENDIF, "endif")
+     DEF(TOK_DEFINED, "defined")
+     DEF(TOK_UNDEF, "undef")
+     DEF(TOK_ERROR, "error")
+     DEF(TOK_WARNING, "warning")
+     DEF(TOK_LINE, "line")
+     DEF(TOK_PRAGMA, "pragma")
+     DEF(TOK___LINE__, "__LINE__")
+     DEF(TOK___FILE__, "__FILE__")
+     DEF(TOK___DATE__, "__DATE__")
+     DEF(TOK___TIME__, "__TIME__")
+     DEF(TOK___FUNCTION__, "__FUNCTION__")
+     DEF(TOK___VA_ARGS__, "__VA_ARGS__")
+     
+/* special identifiers */
+     DEF(TOK___FUNC__, "__func__")
+     
+/* attribute identifiers */
+/* XXX: handle all tokens generically since speed is not critical */
+     DEF(TOK_SECTION1, "section")
+     DEF(TOK_SECTION2, "__section__")
+     DEF(TOK_ALIGNED1, "aligned")
+     DEF(TOK_ALIGNED2, "__aligned__")
+     DEF(TOK_PACKED1, "packed")
+     DEF(TOK_PACKED2, "__packed__")
+     DEF(TOK_UNUSED1, "unused")
+     DEF(TOK_UNUSED2, "__unused__")
+     DEF(TOK_CDECL1, "cdecl")
+     DEF(TOK_CDECL2, "__cdecl")
+     DEF(TOK_CDECL3, "__cdecl__")
+     DEF(TOK_STDCALL1, "stdcall")
+     DEF(TOK_STDCALL2, "__stdcall")
+     DEF(TOK_STDCALL3, "__stdcall__")
+     DEF(TOK_DLLEXPORT, "dllexport")
+     DEF(TOK_NORETURN1, "noreturn")
+     DEF(TOK_NORETURN2, "__noreturn__")
+     DEF(TOK_builtin_types_compatible_p, "__builtin_types_compatible_p")
+     DEF(TOK_builtin_constant_p, "__builtin_constant_p")
+     DEF(TOK_REGPARM1, "regparm")
+     DEF(TOK_REGPARM2, "__regparm__")
+
+/* pragma */
+     DEF(TOK_pack, "pack")
+#if !defined(TCC_TARGET_I386)
+     /* already defined for assembler */
+     DEF(TOK_ASM_push, "push")
+     DEF(TOK_ASM_pop, "pop")
+#endif
+
+/* builtin functions or variables */
+     DEF(TOK_memcpy, "memcpy")
+     DEF(TOK_memset, "memset")
+     DEF(TOK_alloca, "alloca")
+     DEF(TOK___divdi3, "__divdi3")
+     DEF(TOK___moddi3, "__moddi3")
+     DEF(TOK___udivdi3, "__udivdi3")
+     DEF(TOK___umoddi3, "__umoddi3")
+#if defined(TCC_TARGET_ARM)
+     DEF(TOK___divsi3, "__divsi3")
+     DEF(TOK___modsi3, "__modsi3")
+     DEF(TOK___udivsi3, "__udivsi3")
+     DEF(TOK___umodsi3, "__umodsi3")
+     DEF(TOK___sardi3, "__ashrdi3")
+     DEF(TOK___shrdi3, "__lshrdi3")
+     DEF(TOK___shldi3, "__ashldi3")
+     DEF(TOK___slltold, "__slltold")
+     DEF(TOK___fixunssfsi, "__fixunssfsi")
+     DEF(TOK___fixunsdfsi, "__fixunsdfsi")
+     DEF(TOK___fixunsxfsi, "__fixunsxfsi")
+     DEF(TOK___fixsfdi, "__fixsfdi")
+     DEF(TOK___fixdfdi, "__fixdfdi")
+     DEF(TOK___fixxfdi, "__fixxfdi")
+#elif defined(TCC_TARGET_C67)
+     DEF(TOK__divi, "_divi")
+     DEF(TOK__divu, "_divu")
+     DEF(TOK__divf, "_divf")
+     DEF(TOK__divd, "_divd")
+     DEF(TOK__remi, "_remi")
+     DEF(TOK__remu, "_remu")
+     DEF(TOK___sardi3, "__sardi3")
+     DEF(TOK___shrdi3, "__shrdi3")
+     DEF(TOK___shldi3, "__shldi3")
+#else
+     /* XXX: same names on i386 ? */
+     DEF(TOK___sardi3, "__sardi3")
+     DEF(TOK___shrdi3, "__shrdi3")
+     DEF(TOK___shldi3, "__shldi3")
+#endif
+     DEF(TOK___tcc_int_fpu_control, "__tcc_int_fpu_control")
+     DEF(TOK___tcc_fpu_control, "__tcc_fpu_control")
+     DEF(TOK___ulltof, "__ulltof")
+     DEF(TOK___ulltod, "__ulltod")
+     DEF(TOK___ulltold, "__ulltold")
+     DEF(TOK___fixunssfdi, "__fixunssfdi")
+     DEF(TOK___fixunsdfdi, "__fixunsdfdi")
+     DEF(TOK___fixunsxfdi, "__fixunsxfdi")
+     DEF(TOK___chkstk, "__chkstk")
+
+/* bound checking symbols */
+#ifdef CONFIG_TCC_BCHECK
+     DEF(TOK___bound_ptr_add, "__bound_ptr_add")
+     DEF(TOK___bound_ptr_indir1, "__bound_ptr_indir1")
+     DEF(TOK___bound_ptr_indir2, "__bound_ptr_indir2")
+     DEF(TOK___bound_ptr_indir4, "__bound_ptr_indir4")
+     DEF(TOK___bound_ptr_indir8, "__bound_ptr_indir8")
+     DEF(TOK___bound_ptr_indir12, "__bound_ptr_indir12")
+     DEF(TOK___bound_ptr_indir16, "__bound_ptr_indir16")
+     DEF(TOK___bound_local_new, "__bound_local_new")
+     DEF(TOK___bound_local_delete, "__bound_local_delete")
+     DEF(TOK_malloc, "malloc")
+     DEF(TOK_free, "free")
+     DEF(TOK_realloc, "realloc")
+     DEF(TOK_memalign, "memalign")
+     DEF(TOK_calloc, "calloc")
+     DEF(TOK_memmove, "memmove")
+     DEF(TOK_strlen, "strlen")
+     DEF(TOK_strcpy, "strcpy")
+#endif
+
+/* Tiny Assembler */
+
+ DEF_ASM(byte)
+ DEF_ASM(align)
+ DEF_ASM(skip)
+ DEF_ASM(space)
+ DEF_ASM(string)
+ DEF_ASM(asciz)
+ DEF_ASM(ascii)
+ DEF_ASM(globl)
+ DEF_ASM(global)
+ DEF_ASM(text)
+ DEF_ASM(data)
+ DEF_ASM(bss)
+ DEF_ASM(previous)
+ DEF_ASM(fill)
+ DEF_ASM(org)
+ DEF_ASM(quad)
+
+#ifdef TCC_TARGET_I386
+
+/* WARNING: relative order of tokens is important. */
+ DEF_ASM(al)
+ DEF_ASM(cl)
+ DEF_ASM(dl)
+ DEF_ASM(bl)
+ DEF_ASM(ah)
+ DEF_ASM(ch)
+ DEF_ASM(dh)
+ DEF_ASM(bh)
+ DEF_ASM(ax)
+ DEF_ASM(cx)
+ DEF_ASM(dx)
+ DEF_ASM(bx)
+ DEF_ASM(sp)
+ DEF_ASM(bp)
+ DEF_ASM(si)
+ DEF_ASM(di)
+ DEF_ASM(eax)
+ DEF_ASM(ecx)
+ DEF_ASM(edx)
+ DEF_ASM(ebx)
+ DEF_ASM(esp)
+ DEF_ASM(ebp)
+ DEF_ASM(esi)
+ DEF_ASM(edi)
+ DEF_ASM(mm0)
+ DEF_ASM(mm1)
+ DEF_ASM(mm2)
+ DEF_ASM(mm3)
+ DEF_ASM(mm4)
+ DEF_ASM(mm5)
+ DEF_ASM(mm6)
+ DEF_ASM(mm7)
+ DEF_ASM(xmm0)
+ DEF_ASM(xmm1)
+ DEF_ASM(xmm2)
+ DEF_ASM(xmm3)
+ DEF_ASM(xmm4)
+ DEF_ASM(xmm5)
+ DEF_ASM(xmm6)
+ DEF_ASM(xmm7)
+ DEF_ASM(cr0)
+ DEF_ASM(cr1)
+ DEF_ASM(cr2)
+ DEF_ASM(cr3)
+ DEF_ASM(cr4)
+ DEF_ASM(cr5)
+ DEF_ASM(cr6)
+ DEF_ASM(cr7)
+ DEF_ASM(tr0)
+ DEF_ASM(tr1)
+ DEF_ASM(tr2)
+ DEF_ASM(tr3)
+ DEF_ASM(tr4)
+ DEF_ASM(tr5)
+ DEF_ASM(tr6)
+ DEF_ASM(tr7)
+ DEF_ASM(db0)
+ DEF_ASM(db1)
+ DEF_ASM(db2)
+ DEF_ASM(db3)
+ DEF_ASM(db4)
+ DEF_ASM(db5)
+ DEF_ASM(db6)
+ DEF_ASM(db7)
+ DEF_ASM(dr0)
+ DEF_ASM(dr1)
+ DEF_ASM(dr2)
+ DEF_ASM(dr3)
+ DEF_ASM(dr4)
+ DEF_ASM(dr5)
+ DEF_ASM(dr6)
+ DEF_ASM(dr7)
+ DEF_ASM(es)
+ DEF_ASM(cs)
+ DEF_ASM(ss)
+ DEF_ASM(ds)
+ DEF_ASM(fs)
+ DEF_ASM(gs)
+ DEF_ASM(st)
+
+ DEF_BWL(mov)
+
+ /* generic two operands */
+ DEF_BWL(add)
+ DEF_BWL(or)
+ DEF_BWL(adc)
+ DEF_BWL(sbb)
+ DEF_BWL(and)
+ DEF_BWL(sub)
+ DEF_BWL(xor)
+ DEF_BWL(cmp)
+
+ /* unary ops */
+ DEF_BWL(inc)
+ DEF_BWL(dec)
+ DEF_BWL(not)
+ DEF_BWL(neg)
+ DEF_BWL(mul)
+ DEF_BWL(imul)
+ DEF_BWL(div)
+ DEF_BWL(idiv)
+
+ DEF_BWL(xchg)
+ DEF_BWL(test)
+
+ /* shifts */
+ DEF_BWL(rol)
+ DEF_BWL(ror)
+ DEF_BWL(rcl)
+ DEF_BWL(rcr)
+ DEF_BWL(shl)
+ DEF_BWL(shr)
+ DEF_BWL(sar)
+
+ DEF_ASM(shldw)
+ DEF_ASM(shldl)
+ DEF_ASM(shld)
+ DEF_ASM(shrdw)
+ DEF_ASM(shrdl)
+ DEF_ASM(shrd)
+
+ DEF_ASM(pushw)
+ DEF_ASM(pushl)
+ DEF_ASM(push)
+ DEF_ASM(popw)
+ DEF_ASM(popl)
+ DEF_ASM(pop)
+ DEF_BWL(in)
+ DEF_BWL(out)
+
+ DEF_WL(movzb)
+
+ DEF_ASM(movzwl)
+ DEF_ASM(movsbw)
+ DEF_ASM(movsbl)
+ DEF_ASM(movswl)
+
+ DEF_WL(lea) 
+
+ DEF_ASM(les) 
+ DEF_ASM(lds) 
+ DEF_ASM(lss) 
+ DEF_ASM(lfs) 
+ DEF_ASM(lgs) 
+
+ DEF_ASM(call)
+ DEF_ASM(jmp)
+ DEF_ASM(lcall)
+ DEF_ASM(ljmp)
+ DEF_ASMTEST(j)
+
+ DEF_ASMTEST(set)
+ DEF_ASMTEST(cmov)
+
+ DEF_WL(bsf)
+ DEF_WL(bsr)
+ DEF_WL(bt)
+ DEF_WL(bts)
+ DEF_WL(btr)
+ DEF_WL(btc)
+
+ DEF_WL(lsl)
+
+ /* generic FP ops */
+ DEF_FP(add)
+ DEF_FP(mul)
+
+ DEF_ASM(fcom)
+ DEF_ASM(fcom_1) /* non existant op, just to have a regular table */
+ DEF_FP1(com)
+
+ DEF_FP(comp)
+ DEF_FP(sub)
+ DEF_FP(subr)
+ DEF_FP(div)
+ DEF_FP(divr)
+
+ DEF_BWL(xadd)
+ DEF_BWL(cmpxchg)
+
+ /* string ops */
+ DEF_BWL(cmps)
+ DEF_BWL(scmp)
+ DEF_BWL(ins)
+ DEF_BWL(outs)
+ DEF_BWL(lods)
+ DEF_BWL(slod)
+ DEF_BWL(movs)
+ DEF_BWL(smov)
+ DEF_BWL(scas)
+ DEF_BWL(ssca)
+ DEF_BWL(stos)
+ DEF_BWL(ssto)
+
+ /* generic asm ops */
+
+#define ALT(x)
+#define DEF_ASM_OP0(name, opcode) DEF_ASM(name)
+#define DEF_ASM_OP0L(name, opcode, group, instr_type)
+#define DEF_ASM_OP1(name, opcode, group, instr_type, op0)
+#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1)
+#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2)
+// njn: inlined i386-asm.h
+//#include "i386-asm.h"
+//---------------------------------------------------------------------------
+     DEF_ASM_OP0(pusha, 0x60) /* must be first OP0 */
+     DEF_ASM_OP0(popa, 0x61)
+     DEF_ASM_OP0(clc, 0xf8)
+     DEF_ASM_OP0(cld, 0xfc)
+     DEF_ASM_OP0(cli, 0xfa)
+     DEF_ASM_OP0(clts, 0x0f06)
+     DEF_ASM_OP0(cmc, 0xf5)
+     DEF_ASM_OP0(lahf, 0x9f)
+     DEF_ASM_OP0(sahf, 0x9e)
+     DEF_ASM_OP0(pushfl, 0x9c)
+     DEF_ASM_OP0(popfl, 0x9d)
+     DEF_ASM_OP0(pushf, 0x9c)
+     DEF_ASM_OP0(popf, 0x9d)
+     DEF_ASM_OP0(stc, 0xf9)
+     DEF_ASM_OP0(std, 0xfd)
+     DEF_ASM_OP0(sti, 0xfb)
+     DEF_ASM_OP0(aaa, 0x37)
+     DEF_ASM_OP0(aas, 0x3f)
+     DEF_ASM_OP0(daa, 0x27)
+     DEF_ASM_OP0(das, 0x2f)
+     DEF_ASM_OP0(aad, 0xd50a)
+     DEF_ASM_OP0(aam, 0xd40a)
+     DEF_ASM_OP0(cbw, 0x6698)
+     DEF_ASM_OP0(cwd, 0x6699)
+     DEF_ASM_OP0(cwde, 0x98)
+     DEF_ASM_OP0(cdq, 0x99)
+     DEF_ASM_OP0(cbtw, 0x6698)
+     DEF_ASM_OP0(cwtl, 0x98)
+     DEF_ASM_OP0(cwtd, 0x6699)
+     DEF_ASM_OP0(cltd, 0x99)
+     DEF_ASM_OP0(int3, 0xcc)
+     DEF_ASM_OP0(into, 0xce)
+     DEF_ASM_OP0(iret, 0xcf)
+     DEF_ASM_OP0(rsm, 0x0faa)
+     DEF_ASM_OP0(hlt, 0xf4)
+     DEF_ASM_OP0(wait, 0x9b)
+     DEF_ASM_OP0(nop, 0x90)
+     DEF_ASM_OP0(xlat, 0xd7)
+
+     /* strings */
+ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWL))
+
+     /* bits */
+     
+ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
+
+ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+     /* prefixes */
+     DEF_ASM_OP0(aword, 0x67)
+     DEF_ASM_OP0(addr16, 0x67)
+     DEF_ASM_OP0(word, 0x66)
+     DEF_ASM_OP0(data16, 0x66)
+     DEF_ASM_OP0(lock, 0xf0)
+     DEF_ASM_OP0(rep, 0xf3)
+     DEF_ASM_OP0(repe, 0xf3)
+     DEF_ASM_OP0(repz, 0xf3)
+     DEF_ASM_OP0(repne, 0xf2)
+     DEF_ASM_OP0(repnz, 0xf2)
+             
+     DEF_ASM_OP0(invd, 0x0f08)
+     DEF_ASM_OP0(wbinvd, 0x0f09)
+     DEF_ASM_OP0(cpuid, 0x0fa2)
+     DEF_ASM_OP0(wrmsr, 0x0f30)
+     DEF_ASM_OP0(rdtsc, 0x0f31)
+     DEF_ASM_OP0(rdmsr, 0x0f32)
+     DEF_ASM_OP0(rdpmc, 0x0f33)
+     DEF_ASM_OP0(ud2, 0x0f0b)
+
+     /* NOTE: we took the same order as gas opcode definition order */
+ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWL, OPT_ADDR, OPT_EAX))
+ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWL, OPT_EAX, OPT_ADDR))
+ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWL, OPT_IM, OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WL, OPT_SEG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_SEG))
+
+ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WL, OPT_CR, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WL, OPT_DB, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WL, OPT_TR, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_CR))
+ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_DB))
+ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_TR))
+
+ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16))
+ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
+
+ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP1(pushw, 0x6a, 0, OPC_WL, OPT_IM8S))
+ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WL, OPT_IM32))
+ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WL, OPT_SEG))
+
+ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WL, OPT_SEG))
+
+ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_REG, OPT_EAX))
+ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_EAX, OPT_REG))
+ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+
+ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX))
+ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8))
+ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX))
+ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX))
+
+ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8))
+ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8))
+ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX))
+ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX))
+
+ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WL, OPT_EA, OPT_REG))
+
+ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+
+     /* arith */
+ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */
+ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWL, OPT_IM, OPT_EAX))
+ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WL, OPT_IM8S, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWL, OPT_IM, OPT_EAX))
+ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WL, OPT_REG | OPT_EA, OPT_REG))
+ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW))
+ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW))
+
+ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
+ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
+
+     /* shifts */
+ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
+
+ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR))
+ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR))
+ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR))
+ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR))
+
+ALT(DEF_ASM_OP2(lcall, 0x9a, 0, 0, OPT_IM16, OPT_IM32))
+ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA))
+ALT(DEF_ASM_OP2(ljmp, 0xea, 0, 0, OPT_IM16, OPT_IM32))
+ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA))
+
+ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8))
+ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
+    DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8)
+    DEF_ASM_OP0(leave, 0xc9)
+    DEF_ASM_OP0(ret, 0xc3)
+ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16))
+    DEF_ASM_OP0(lret, 0xcb)
+ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
+
+ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR))
+    DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR)
+     
+     /* float */
+     /* specific fcomp handling */
+ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0))
+
+ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST))
+ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
+ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH))
+ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST))
+ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
+ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
+ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH))
+ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+
+     DEF_ASM_OP0(fucompp, 0xdae9)
+     DEF_ASM_OP0(ftst, 0xd9e4)
+     DEF_ASM_OP0(fxam, 0xd9e5)
+     DEF_ASM_OP0(fld1, 0xd9e8)
+     DEF_ASM_OP0(fldl2t, 0xd9e9)
+     DEF_ASM_OP0(fldl2e, 0xd9ea)
+     DEF_ASM_OP0(fldpi, 0xd9eb)
+     DEF_ASM_OP0(fldlg2, 0xd9ec)
+     DEF_ASM_OP0(fldln2, 0xd9ed)
+     DEF_ASM_OP0(fldz, 0xd9ee)
+
+     DEF_ASM_OP0(f2xm1, 0xd9f0)
+     DEF_ASM_OP0(fyl2x, 0xd9f1)
+     DEF_ASM_OP0(fptan, 0xd9f2)
+     DEF_ASM_OP0(fpatan, 0xd9f3)
+     DEF_ASM_OP0(fxtract, 0xd9f4)
+     DEF_ASM_OP0(fprem1, 0xd9f5)
+     DEF_ASM_OP0(fdecstp, 0xd9f6)
+     DEF_ASM_OP0(fincstp, 0xd9f7)
+     DEF_ASM_OP0(fprem, 0xd9f8)
+     DEF_ASM_OP0(fyl2xp1, 0xd9f9)
+     DEF_ASM_OP0(fsqrt, 0xd9fa)
+     DEF_ASM_OP0(fsincos, 0xd9fb)
+     DEF_ASM_OP0(frndint, 0xd9fc)
+     DEF_ASM_OP0(fscale, 0xd9fd)
+     DEF_ASM_OP0(fsin, 0xd9fe)
+     DEF_ASM_OP0(fcos, 0xd9ff)
+     DEF_ASM_OP0(fchs, 0xd9e0)
+     DEF_ASM_OP0(fabs, 0xd9e1)
+     DEF_ASM_OP0(fninit, 0xdbe3)
+     DEF_ASM_OP0(fnclex, 0xdbe2)
+     DEF_ASM_OP0(fnop, 0xd9d0)
+     DEF_ASM_OP0(fwait, 0x9b)
+
+    /* fp load */
+    DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA)
+ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA))
+    DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA)
+    DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA)
+    
+    /* fp store */
+    DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA)
+ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA))
+    DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA)
+
+    DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA)
+
+    /* exchange */
+    DEF_ASM_OP0(fxch, 0xd9c9)
+ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST))
+
+    /* misc FPU */
+    DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST )
+    DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST )
+
+    DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT)
+    DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )
+    DEF_ASM_OP0(fnstsw, 0xdfe0)
+ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX ))
+ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA ))
+    DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX )
+ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT))
+ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ))
+    DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT)
+    DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
+    DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
+    DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST )
+    DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST )
+    DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA )
+
+    /* segments */
+    DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA)
+    DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32)
+    DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG)
+    DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG)
+ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG))
+    DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG)
+    DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA)
+    DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA)
+    DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA)
+    DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA)
+    DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA)
+
+    /* 486 */
+    DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 )
+ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
+ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
+    DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA )
+
+    DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA)
+    DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA)
+
+    /* pentium */
+    DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA )
+    
+    /* pentium pro */
+    ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+
+    DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+
+    DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+
+    /* mmx */
+    DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */
+    DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX )
+ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 ))
+    DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX ))
+    DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+
+#undef ALT
+#undef DEF_ASM_OP0
+#undef DEF_ASM_OP0L
+#undef DEF_ASM_OP1
+#undef DEF_ASM_OP2
+#undef DEF_ASM_OP3
+//---------------------------------------------------------------------------
+
+#define ALT(x)
+#define DEF_ASM_OP0(name, opcode)
+#define DEF_ASM_OP0L(name, opcode, group, instr_type) DEF_ASM(name)
+#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) DEF_ASM(name)
+#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) DEF_ASM(name)
+#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) DEF_ASM(name)
+// njn: inlined i386-asm.h
+//#include "i386-asm.h"
+//---------------------------------------------------------------------------
+     DEF_ASM_OP0(pusha, 0x60) /* must be first OP0 */
+     DEF_ASM_OP0(popa, 0x61)
+     DEF_ASM_OP0(clc, 0xf8)
+     DEF_ASM_OP0(cld, 0xfc)
+     DEF_ASM_OP0(cli, 0xfa)
+     DEF_ASM_OP0(clts, 0x0f06)
+     DEF_ASM_OP0(cmc, 0xf5)
+     DEF_ASM_OP0(lahf, 0x9f)
+     DEF_ASM_OP0(sahf, 0x9e)
+     DEF_ASM_OP0(pushfl, 0x9c)
+     DEF_ASM_OP0(popfl, 0x9d)
+     DEF_ASM_OP0(pushf, 0x9c)
+     DEF_ASM_OP0(popf, 0x9d)
+     DEF_ASM_OP0(stc, 0xf9)
+     DEF_ASM_OP0(std, 0xfd)
+     DEF_ASM_OP0(sti, 0xfb)
+     DEF_ASM_OP0(aaa, 0x37)
+     DEF_ASM_OP0(aas, 0x3f)
+     DEF_ASM_OP0(daa, 0x27)
+     DEF_ASM_OP0(das, 0x2f)
+     DEF_ASM_OP0(aad, 0xd50a)
+     DEF_ASM_OP0(aam, 0xd40a)
+     DEF_ASM_OP0(cbw, 0x6698)
+     DEF_ASM_OP0(cwd, 0x6699)
+     DEF_ASM_OP0(cwde, 0x98)
+     DEF_ASM_OP0(cdq, 0x99)
+     DEF_ASM_OP0(cbtw, 0x6698)
+     DEF_ASM_OP0(cwtl, 0x98)
+     DEF_ASM_OP0(cwtd, 0x6699)
+     DEF_ASM_OP0(cltd, 0x99)
+     DEF_ASM_OP0(int3, 0xcc)
+     DEF_ASM_OP0(into, 0xce)
+     DEF_ASM_OP0(iret, 0xcf)
+     DEF_ASM_OP0(rsm, 0x0faa)
+     DEF_ASM_OP0(hlt, 0xf4)
+     DEF_ASM_OP0(wait, 0x9b)
+     DEF_ASM_OP0(nop, 0x90)
+     DEF_ASM_OP0(xlat, 0xd7)
+
+     /* strings */
+ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWL))
+
+     /* bits */
+     
+ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
+
+ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+     /* prefixes */
+     DEF_ASM_OP0(aword, 0x67)
+     DEF_ASM_OP0(addr16, 0x67)
+     DEF_ASM_OP0(word, 0x66)
+     DEF_ASM_OP0(data16, 0x66)
+     DEF_ASM_OP0(lock, 0xf0)
+     DEF_ASM_OP0(rep, 0xf3)
+     DEF_ASM_OP0(repe, 0xf3)
+     DEF_ASM_OP0(repz, 0xf3)
+     DEF_ASM_OP0(repne, 0xf2)
+     DEF_ASM_OP0(repnz, 0xf2)
+             
+     DEF_ASM_OP0(invd, 0x0f08)
+     DEF_ASM_OP0(wbinvd, 0x0f09)
+     DEF_ASM_OP0(cpuid, 0x0fa2)
+     DEF_ASM_OP0(wrmsr, 0x0f30)
+     DEF_ASM_OP0(rdtsc, 0x0f31)
+     DEF_ASM_OP0(rdmsr, 0x0f32)
+     DEF_ASM_OP0(rdpmc, 0x0f33)
+     DEF_ASM_OP0(ud2, 0x0f0b)
+
+     /* NOTE: we took the same order as gas opcode definition order */
+ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWL, OPT_ADDR, OPT_EAX))
+ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWL, OPT_EAX, OPT_ADDR))
+ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWL, OPT_IM, OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WL, OPT_SEG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_SEG))
+
+ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WL, OPT_CR, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WL, OPT_DB, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WL, OPT_TR, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_CR))
+ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_DB))
+ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_TR))
+
+ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16))
+ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
+
+ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP1(pushw, 0x6a, 0, OPC_WL, OPT_IM8S))
+ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WL, OPT_IM32))
+ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WL, OPT_SEG))
+
+ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WL, OPT_SEG))
+
+ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_REG, OPT_EAX))
+ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_EAX, OPT_REG))
+ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+
+ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX))
+ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8))
+ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX))
+ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX))
+
+ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8))
+ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8))
+ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX))
+ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX))
+
+ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WL, OPT_EA, OPT_REG))
+
+ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+
+     /* arith */
+ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */
+ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWL, OPT_IM, OPT_EAX))
+ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WL, OPT_IM8S, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWL, OPT_IM, OPT_EAX))
+ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WL, OPT_REG | OPT_EA, OPT_REG))
+ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW))
+ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW))
+
+ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
+ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
+
+     /* shifts */
+ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
+
+ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR))
+ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR))
+ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR))
+ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR))
+
+ALT(DEF_ASM_OP2(lcall, 0x9a, 0, 0, OPT_IM16, OPT_IM32))
+ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA))
+ALT(DEF_ASM_OP2(ljmp, 0xea, 0, 0, OPT_IM16, OPT_IM32))
+ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA))
+
+ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8))
+ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
+    DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8)
+    DEF_ASM_OP0(leave, 0xc9)
+    DEF_ASM_OP0(ret, 0xc3)
+ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16))
+    DEF_ASM_OP0(lret, 0xcb)
+ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
+
+ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR))
+    DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR)
+     
+     /* float */
+     /* specific fcomp handling */
+ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0))
+
+ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST))
+ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
+ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH))
+ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST))
+ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
+ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
+ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH))
+ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+
+     DEF_ASM_OP0(fucompp, 0xdae9)
+     DEF_ASM_OP0(ftst, 0xd9e4)
+     DEF_ASM_OP0(fxam, 0xd9e5)
+     DEF_ASM_OP0(fld1, 0xd9e8)
+     DEF_ASM_OP0(fldl2t, 0xd9e9)
+     DEF_ASM_OP0(fldl2e, 0xd9ea)
+     DEF_ASM_OP0(fldpi, 0xd9eb)
+     DEF_ASM_OP0(fldlg2, 0xd9ec)
+     DEF_ASM_OP0(fldln2, 0xd9ed)
+     DEF_ASM_OP0(fldz, 0xd9ee)
+
+     DEF_ASM_OP0(f2xm1, 0xd9f0)
+     DEF_ASM_OP0(fyl2x, 0xd9f1)
+     DEF_ASM_OP0(fptan, 0xd9f2)
+     DEF_ASM_OP0(fpatan, 0xd9f3)
+     DEF_ASM_OP0(fxtract, 0xd9f4)
+     DEF_ASM_OP0(fprem1, 0xd9f5)
+     DEF_ASM_OP0(fdecstp, 0xd9f6)
+     DEF_ASM_OP0(fincstp, 0xd9f7)
+     DEF_ASM_OP0(fprem, 0xd9f8)
+     DEF_ASM_OP0(fyl2xp1, 0xd9f9)
+     DEF_ASM_OP0(fsqrt, 0xd9fa)
+     DEF_ASM_OP0(fsincos, 0xd9fb)
+     DEF_ASM_OP0(frndint, 0xd9fc)
+     DEF_ASM_OP0(fscale, 0xd9fd)
+     DEF_ASM_OP0(fsin, 0xd9fe)
+     DEF_ASM_OP0(fcos, 0xd9ff)
+     DEF_ASM_OP0(fchs, 0xd9e0)
+     DEF_ASM_OP0(fabs, 0xd9e1)
+     DEF_ASM_OP0(fninit, 0xdbe3)
+     DEF_ASM_OP0(fnclex, 0xdbe2)
+     DEF_ASM_OP0(fnop, 0xd9d0)
+     DEF_ASM_OP0(fwait, 0x9b)
+
+    /* fp load */
+    DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA)
+ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA))
+    DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA)
+    DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA)
+    
+    /* fp store */
+    DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA)
+ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA))
+    DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA)
+
+    DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA)
+
+    /* exchange */
+    DEF_ASM_OP0(fxch, 0xd9c9)
+ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST))
+
+    /* misc FPU */
+    DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST )
+    DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST )
+
+    DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT)
+    DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )
+    DEF_ASM_OP0(fnstsw, 0xdfe0)
+ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX ))
+ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA ))
+    DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX )
+ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT))
+ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ))
+    DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT)
+    DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
+    DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
+    DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST )
+    DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST )
+    DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA )
+
+    /* segments */
+    DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA)
+    DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32)
+    DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG)
+    DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG)
+ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG))
+    DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG)
+    DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA)
+    DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA)
+    DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA)
+    DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA)
+    DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA)
+
+    /* 486 */
+    DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 )
+ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
+ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
+    DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA )
+
+    DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA)
+    DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA)
+
+    /* pentium */
+    DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA )
+    
+    /* pentium pro */
+    ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+
+    DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+
+    DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+
+    /* mmx */
+    DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */
+    DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX )
+ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 ))
+    DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX ))
+    DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+
+#undef ALT
+#undef DEF_ASM_OP0
+#undef DEF_ASM_OP0L
+#undef DEF_ASM_OP1
+#undef DEF_ASM_OP2
+#undef DEF_ASM_OP3
+//---------------------------------------------------------------------------
+
+#endif
+//---------------------------------------------------------------------------
+#undef DEF
+;
+
+#define TOK_UIDENT TOK_DEFINE
+
+#ifdef WIN32
+int __stdcall GetModuleFileNameA(void *, char *, int);
+void *__stdcall GetProcAddress(void *, const char *);
+void *__stdcall GetModuleHandleA(const char *);
+void *__stdcall LoadLibraryA(const char *);
+int __stdcall FreeConsole(void);
+
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#ifndef __GNUC__
+  #define strtold (long double)strtod
+  #define strtof (float)strtod
+  #define strtoll (long long)strtol
+#endif
+#elif defined(TCC_UCLIBC) || defined(__FreeBSD__)
+/* currently incorrect */
+long double strtold(const char *nptr, char **endptr)
+{
+    return (long double)strtod(nptr, endptr);
+}
+float strtof(const char *nptr, char **endptr)
+{
+    return (float)strtod(nptr, endptr);
+}
+#else
+/* XXX: need to define this to use them in non ISOC99 context */
+extern float strtof (const char *__nptr, char **__endptr);
+extern long double strtold (const char *__nptr, char **__endptr);
+#endif
+
+static char *pstrcpy(char *buf, int buf_size, const char *s);
+static char *pstrcat(char *buf, int buf_size, const char *s);
+static const char *tcc_basename(const char *name);
+
+static void next(void);
+static void next_nomacro(void);
+static void parse_expr_type(CType *type);
+static void expr_type(CType *type);
+static void unary_type(CType *type);
+static void block(int *bsym, int *csym, int *case_sym, int *def_sym, 
+                  int case_reg, int is_expr);
+static int expr_const(void);
+static void expr_eq(void);
+static void gexpr(void);
+static void gen_inline_functions(void);
+static void decl(int l);
+static void decl_initializer(CType *type, Section *sec, unsigned long c, 
+                             int first, int size_only);
+static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, 
+                                   int has_init, int v, int scope);
+int gv(int rc);
+void gv2(int rc1, int rc2);
+void move_reg(int r, int s);
+void save_regs(int n);
+void save_reg(int r);
+void vpop(void);
+void vswap(void);
+void vdup(void);
+int get_reg(int rc);
+int get_reg_ex(int rc,int rc2);
+
+struct macro_level {
+    struct macro_level *prev;
+    int *p;
+};
+
+static void macro_subst(TokenString *tok_str, Sym **nested_list, 
+                        const int *macro_str, struct macro_level **can_read_stream);
+void gen_op(int op);
+void force_charshort_cast(int t);
+static void gen_cast(CType *type);
+void vstore(void);
+static Sym *sym_find(int v);
+static Sym *sym_push(int v, CType *type, int r, int c);
+
+/* type handling */
+static int type_size(CType *type, int *a);
+static inline CType *pointed_type(CType *type);
+static int pointed_size(CType *type);
+static int lvalue_type(int t);
+static int parse_btype(CType *type, AttributeDef *ad);
+static void type_decl(CType *type, AttributeDef *ad, int *v, int td);
+static int is_compatible_types(CType *type1, CType *type2);
+
+int ieee_finite(double d);
+void error(const char *fmt, ...);
+void vpushi(int v);
+void vrott(int n);
+void vnrott(int n);
+void lexpand_nr(void);
+static void vpush_global_sym(CType *type, int v);
+void vset(CType *type, int r, int v);
+void type_to_str(char *buf, int buf_size, 
+                 CType *type, const char *varstr);
+char *get_tok_str(int v, CValue *cv);
+static Sym *get_sym_ref(CType *type, Section *sec, 
+                        unsigned long offset, unsigned long size);
+static Sym *external_global_sym(int v, CType *type, int r);
+
+/* section generation */
+static void section_realloc(Section *sec, unsigned long new_size);
+static void *section_ptr_add(Section *sec, unsigned long size);
+static void put_extern_sym(Sym *sym, Section *section, 
+                           unsigned long value, unsigned long size);
+static void greloc(Section *s, Sym *sym, unsigned long addr, int type);
+static int put_elf_str(Section *s, const char *sym);
+static int put_elf_sym(Section *s, 
+                       unsigned long value, unsigned long size,
+                       int info, int other, int shndx, const char *name);
+static int add_elf_sym(Section *s, unsigned long value, unsigned long size,
+                       int info, int other, int sh_num, const char *name);
+static void put_elf_reloc(Section *symtab, Section *s, unsigned long offset,
+                          int type, int symbol);
+static void put_stabs(const char *str, int type, int other, int desc, 
+                      unsigned long value);
+static void put_stabs_r(const char *str, int type, int other, int desc, 
+                        unsigned long value, Section *sec, int sym_index);
+static void put_stabn(int type, int other, int desc, int value);
+static void put_stabd(int type, int other, int desc);
+static int tcc_add_dll(TCCState *s, const char *filename, int flags);
+
+#define AFF_PRINT_ERROR     0x0001 /* print error if file not found */
+#define AFF_REFERENCED_DLL  0x0002 /* load a referenced dll from another dll */
+static int tcc_add_file_internal(TCCState *s, const char *filename, int flags);
+
+/* tcccoff.c */
+int tcc_output_coff(TCCState *s1, FILE *f);
+
+/* tccpe.c */
+void *resolve_sym(TCCState *s1, const char *sym, int type);
+int pe_load_def_file(struct TCCState *s1, FILE *fp);
+void pe_setup_paths(struct TCCState *s1, int *p_output_type, const char **p_outfile, char *first_file);
+unsigned long pe_add_runtime(struct TCCState *s1);
+int tcc_output_pe(struct TCCState *s1, const char *filename);
+
+/* tccasm.c */
+
+#ifdef CONFIG_TCC_ASM
+
+typedef struct ExprValue {
+    uint32_t v;
+    Sym *sym;
+} ExprValue;
+
+#define MAX_ASM_OPERANDS 30
+
+typedef struct ASMOperand {
+    int id; /* GCC 3 optionnal identifier (0 if number only supported */
+    char *constraint;
+    char asm_str[16]; /* computed asm string for operand */
+    SValue *vt; /* C value of the expression */
+    int ref_index; /* if >= 0, gives reference to a output constraint */
+    int input_index; /* if >= 0, gives reference to an input constraint */
+    int priority; /* priority, used to assign registers */
+    int reg; /* if >= 0, register number used for this operand */
+    int is_llong; /* true if double register value */
+    int is_memory; /* true if memory operand */
+    int is_rw;     /* for '+' modifier */
+} ASMOperand;
+
+static void asm_expr(TCCState *s1, ExprValue *pe);
+static int asm_int_expr(TCCState *s1);
+static int find_constraint(ASMOperand *operands, int nb_operands, 
+                           const char *name, const char **pp);
+
+static int tcc_assemble(TCCState *s1, int do_preprocess);
+
+#endif
+
+static void asm_instr(void);
+static void asm_global_instr(void);
+
+/* true if float/double/long double type */
+static inline int is_float(int t)
+{
+    int bt;
+    bt = t & VT_BTYPE;
+    return bt == VT_LDOUBLE || bt == VT_DOUBLE || bt == VT_FLOAT;
+}
+
+#ifdef TCC_TARGET_I386
+// njn: inlined i386-gen.c
+//#include "i386-gen.c"
+//---------------------------------------------------------------------------
+/*
+ *  X86 code generator for TCC
+ * 
+ *  Copyright (c) 2001-2004 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* number of available registers */
+#define NB_REGS             4
+
+/* a register can belong to several classes. The classes must be
+   sorted from more general to more precise (see gv2() code which does
+   assumptions on it). */
+#define RC_INT     0x0001 /* generic integer register */
+#define RC_FLOAT   0x0002 /* generic float register */
+#define RC_EAX     0x0004
+#define RC_ST0     0x0008 
+#define RC_ECX     0x0010
+#define RC_EDX     0x0020
+#define RC_IRET    RC_EAX /* function return: integer register */
+#define RC_LRET    RC_EDX /* function return: second integer register */
+#define RC_FRET    RC_ST0 /* function return: float register */
+
+/* pretty names for the registers */
+enum {
+    TREG_EAX = 0,
+    TREG_ECX,
+    TREG_EDX,
+    TREG_ST0,
+};
+
+int reg_classes[NB_REGS] = {
+    /* eax */ RC_INT | RC_EAX,
+    /* ecx */ RC_INT | RC_ECX,
+    /* edx */ RC_INT | RC_EDX,
+    /* st0 */ RC_FLOAT | RC_ST0,
+};
+
+/* return registers for function */
+#define REG_IRET TREG_EAX /* single word int return register */
+#define REG_LRET TREG_EDX /* second word return register (for long long) */
+#define REG_FRET TREG_ST0 /* float return register */
+
+/* defined if function parameters must be evaluated in reverse order */
+#define INVERT_FUNC_PARAMS
+
+/* defined if structures are passed as pointers. Otherwise structures
+   are directly pushed on stack. */
+//#define FUNC_STRUCT_PARAM_AS_PTR
+
+/* pointer size, in bytes */
+#define PTR_SIZE 4
+
+/* long double size and alignment, in bytes */
+#define LDOUBLE_SIZE  12
+#define LDOUBLE_ALIGN 4
+/* maximum alignment (for aligned attribute support) */
+#define MAX_ALIGN     8
+
+/******************************************************/
+/* ELF defines */
+
+#define EM_TCC_TARGET EM_386
+
+/* relocation type for 32 bit data relocation */
+#define R_DATA_32   R_386_32
+#define R_JMP_SLOT  R_386_JMP_SLOT
+#define R_COPY      R_386_COPY
+
+#define ELF_START_ADDR 0x08048000
+#define ELF_PAGE_SIZE  0x1000
+
+/******************************************************/
+
+static unsigned long func_sub_sp_offset;
+static unsigned long func_bound_offset;
+static int func_ret_sub;
+
+/* XXX: make it faster ? */
+void g(int c)
+{
+    int ind1;
+    ind1 = ind + 1;
+    if (ind1 > cur_text_section->data_allocated)
+        section_realloc(cur_text_section, ind1);
+    cur_text_section->data[ind] = c;
+    ind = ind1;
+}
+
+void o(unsigned int c)
+{
+    while (c) {
+        g(c);
+        c = c >> 8;
+    }
+}
+
+void gen_le32(int c)
+{
+    g(c);
+    g(c >> 8);
+    g(c >> 16);
+    g(c >> 24);
+}
+
+/* output a symbol and patch all calls to it */
+void gsym_addr(int t, int a)
+{
+    int n, *ptr;
+    while (t) {
+        ptr = (int *)(cur_text_section->data + t);
+        n = *ptr; /* next value */
+        *ptr = a - t - 4;
+        t = n;
+    }
+}
+
+void gsym(int t)
+{
+    gsym_addr(t, ind);
+}
+
+/* psym is used to put an instruction with a data field which is a
+   reference to a symbol. It is in fact the same as oad ! */
+#define psym oad
+
+/* instruction + 4 bytes data. Return the address of the data */
+static int oad(int c, int s)
+{
+    int ind1;
+
+    o(c);
+    ind1 = ind + 4;
+    if (ind1 > cur_text_section->data_allocated)
+        section_realloc(cur_text_section, ind1);
+    *(int *)(cur_text_section->data + ind) = s;
+    s = ind;
+    ind = ind1;
+    return s;
+}
+
+/* output constant with relocation if 'r & VT_SYM' is true */
+static void gen_addr32(int r, Sym *sym, int c)
+{
+    if (r & VT_SYM)
+        greloc(cur_text_section, sym, ind, R_386_32);
+    gen_le32(c);
+}
+
+/* generate a modrm reference. 'op_reg' contains the addtionnal 3
+   opcode bits */
+static void gen_modrm(int op_reg, int r, Sym *sym, int c)
+{
+    op_reg = op_reg << 3;
+    if ((r & VT_VALMASK) == VT_CONST) {
+        /* constant memory reference */
+        o(0x05 | op_reg);
+        gen_addr32(r, sym, c);
+    } else if ((r & VT_VALMASK) == VT_LOCAL) {
+        /* currently, we use only ebp as base */
+        if (c == (char)c) {
+            /* short reference */
+            o(0x45 | op_reg);
+            g(c);
+        } else {
+            oad(0x85 | op_reg, c);
+        }
+    } else {
+        g(0x00 | op_reg | (r & VT_VALMASK));
+    }
+}
+
+
+/* load 'r' from value 'sv' */
+void load(int r, SValue *sv)
+{
+    int v, t, ft, fc, fr;
+    SValue v1;
+
+    fr = sv->r;
+    ft = sv->type.t;
+    fc = sv->c.ul;
+
+    v = fr & VT_VALMASK;
+    if (fr & VT_LVAL) {
+        if (v == VT_LLOCAL) {
+            v1.type.t = VT_INT;
+            v1.r = VT_LOCAL | VT_LVAL;
+            v1.c.ul = fc;
+            load(r, &v1);
+            fr = r;
+        }
+        if ((ft & VT_BTYPE) == VT_FLOAT) {
+            o(0xd9); /* flds */
+            r = 0;
+        } else if ((ft & VT_BTYPE) == VT_DOUBLE) {
+            o(0xdd); /* fldl */
+            r = 0;
+        } else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
+            o(0xdb); /* fldt */
+            r = 5;
+        } else if ((ft & VT_TYPE) == VT_BYTE) {
+            o(0xbe0f);   /* movsbl */
+        } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) {
+            o(0xb60f);   /* movzbl */
+        } else if ((ft & VT_TYPE) == VT_SHORT) {
+            o(0xbf0f);   /* movswl */
+        } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) {
+            o(0xb70f);   /* movzwl */
+        } else {
+            o(0x8b);     /* movl */
+        }
+        gen_modrm(r, fr, sv->sym, fc);
+    } else {
+        if (v == VT_CONST) {
+            o(0xb8 + r); /* mov $xx, r */
+            gen_addr32(fr, sv->sym, fc);
+        } else if (v == VT_LOCAL) {
+            o(0x8d); /* lea xxx(%ebp), r */
+            gen_modrm(r, VT_LOCAL, sv->sym, fc);
+        } else if (v == VT_CMP) {
+            oad(0xb8 + r, 0); /* mov $0, r */
+            o(0x0f); /* setxx %br */
+            o(fc);
+            o(0xc0 + r);
+        } else if (v == VT_JMP || v == VT_JMPI) {
+            t = v & 1;
+            oad(0xb8 + r, t); /* mov $1, r */
+            o(0x05eb); /* jmp after */
+            gsym(fc);
+            oad(0xb8 + r, t ^ 1); /* mov $0, r */
+        } else if (v != r) {
+            o(0x89);
+            o(0xc0 + r + v * 8); /* mov v, r */
+        }
+    }
+}
+
+/* store register 'r' in lvalue 'v' */
+void store(int r, SValue *v)
+{
+    int fr, bt, ft, fc;
+
+    ft = v->type.t;
+    fc = v->c.ul;
+    fr = v->r & VT_VALMASK;
+    bt = ft & VT_BTYPE;
+    /* XXX: incorrect if float reg to reg */
+    if (bt == VT_FLOAT) {
+        o(0xd9); /* fsts */
+        r = 2;
+    } else if (bt == VT_DOUBLE) {
+        o(0xdd); /* fstpl */
+        r = 2;
+    } else if (bt == VT_LDOUBLE) {
+        o(0xc0d9); /* fld %st(0) */
+        o(0xdb); /* fstpt */
+        r = 7;
+    } else {
+        if (bt == VT_SHORT)
+            o(0x66);
+        if (bt == VT_BYTE || bt == VT_BOOL)
+            o(0x88);
+        else
+            o(0x89);
+    }
+    if (fr == VT_CONST ||
+        fr == VT_LOCAL ||
+        (v->r & VT_LVAL)) {
+        gen_modrm(r, v->r, v->sym, fc);
+    } else if (fr != r) {
+        o(0xc0 + fr + r * 8); /* mov r, fr */
+    }
+}
+
+static void gadd_sp(int val)
+{
+    if (val == (char)val) {
+        o(0xc483);
+        g(val);
+    } else {
+        oad(0xc481, val); /* add $xxx, %esp */
+    }
+}
+
+/* 'is_jmp' is '1' if it is a jump */
+static void gcall_or_jmp(int is_jmp)
+{
+    int r;
+    if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
+        /* constant case */
+        if (vtop->r & VT_SYM) {
+            /* relocation case */
+            greloc(cur_text_section, vtop->sym, 
+                   ind + 1, R_386_PC32);
+        } else {
+            /* put an empty PC32 relocation */
+            put_elf_reloc(symtab_section, cur_text_section, 
+                          ind + 1, R_386_PC32, 0);
+        }
+        oad(0xe8 + is_jmp, vtop->c.ul - 4); /* call/jmp im */
+    } else {
+        /* otherwise, indirect call */
+        r = gv(RC_INT);
+        o(0xff); /* call/jmp *r */
+        o(0xd0 + r + (is_jmp << 4));
+    }
+}
+
+static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX };
+
+/* Generate function call. The function address is pushed first, then
+   all the parameters in call order. This functions pops all the
+   parameters and the function address. */
+void gfunc_call(int nb_args)
+{
+    int size, align, r, args_size, i, func_call;
+    Sym *func_sym;
+    
+    args_size = 0;
+    for(i = 0;i < nb_args; i++) {
+        if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
+            size = type_size(&vtop->type, &align);
+            /* align to stack align size */
+            size = (size + 3) & ~3;
+            /* allocate the necessary size on stack */
+            oad(0xec81, size); /* sub $xxx, %esp */
+            /* generate structure store */
+            r = get_reg(RC_INT);
+            o(0x89); /* mov %esp, r */
+            o(0xe0 + r);
+            vset(&vtop->type, r | VT_LVAL, 0);
+            vswap();
+            vstore();
+            args_size += size;
+        } else if (is_float(vtop->type.t)) {
+            gv(RC_FLOAT); /* only one float register */
+            if ((vtop->type.t & VT_BTYPE) == VT_FLOAT)
+                size = 4;
+            else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE)
+                size = 8;
+            else
+                size = 12;
+            oad(0xec81, size); /* sub $xxx, %esp */
+            if (size == 12)
+                o(0x7cdb);
+            else
+                o(0x5cd9 + size - 4); /* fstp[s|l] 0(%esp) */
+            g(0x24);
+            g(0x00);
+            args_size += size;
+        } else {
+            /* simple type (currently always same size) */
+            /* XXX: implicit cast ? */
+            r = gv(RC_INT);
+            if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
+                size = 8;
+                o(0x50 + vtop->r2); /* push r */
+            } else {
+                size = 4;
+            }
+            o(0x50 + r); /* push r */
+            args_size += size;
+        }
+        vtop--;
+    }
+    save_regs(0); /* save used temporary registers */
+    func_sym = vtop->type.ref;
+    func_call = func_sym->r;
+    /* fast call case */
+    if (func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) {
+        int fastcall_nb_regs;
+        fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1;
+        for(i = 0;i < fastcall_nb_regs; i++) {
+            if (args_size <= 0)
+                break;
+            o(0x58 + fastcall_regs[i]); /* pop r */
+            /* XXX: incorrect for struct/floats */
+            args_size -= 4;
+        }
+    }
+    gcall_or_jmp(0);
+    if (args_size && func_sym->r != FUNC_STDCALL)
+        gadd_sp(args_size);
+    vtop--;
+}
+
+#ifdef TCC_TARGET_PE
+#define FUNC_PROLOG_SIZE 10
+#else
+#define FUNC_PROLOG_SIZE 9
+#endif
+
+/* generate function prolog of type 't' */
+void gfunc_prolog(CType *func_type)
+{
+    int addr, align, size, func_call, fastcall_nb_regs;
+    int param_index, param_addr;
+    Sym *sym;
+    CType *type;
+
+    sym = func_type->ref;
+    func_call = sym->r;
+    addr = 8;
+    loc = 0;
+    if (func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) {
+        fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1;
+    } else {
+        fastcall_nb_regs = 0;
+    }
+    param_index = 0;
+
+    ind += FUNC_PROLOG_SIZE;
+    func_sub_sp_offset = ind;
+    /* if the function returns a structure, then add an
+       implicit pointer parameter */
+    func_vt = sym->type;
+    if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
+        /* XXX: fastcall case ? */
+        func_vc = addr;
+        addr += 4;
+        param_index++;
+    }
+    /* define parameters */
+    while ((sym = sym->next) != NULL) {
+        type = &sym->type;
+        size = type_size(type, &align);
+        size = (size + 3) & ~3;
+#ifdef FUNC_STRUCT_PARAM_AS_PTR
+        /* structs are passed as pointer */
+        if ((type->t & VT_BTYPE) == VT_STRUCT) {
+            size = 4;
+        }
+#endif
+        if (param_index < fastcall_nb_regs) {
+            /* save FASTCALL register */
+            loc -= 4;
+            o(0x89);     /* movl */
+            gen_modrm(fastcall_regs[param_index], VT_LOCAL, NULL, loc);
+            param_addr = loc;
+        } else {
+            param_addr = addr;
+            addr += size;
+        }
+        sym_push(sym->v & ~SYM_FIELD, type,
+                 VT_LOCAL | VT_LVAL, param_addr);
+        param_index++;
+    }
+    func_ret_sub = 0;
+    /* pascal type call ? */
+    if (func_call == FUNC_STDCALL)
+        func_ret_sub = addr - 8;
+
+    /* leave some room for bound checking code */
+    if (do_bounds_check) {
+        oad(0xb8, 0); /* lbound section pointer */
+        oad(0xb8, 0); /* call to function */
+        func_bound_offset = lbounds_section->data_offset;
+    }
+}
+
+/* generate function epilog */
+void gfunc_epilog(void)
+{
+    int v, saved_ind;
+
+#ifdef CONFIG_TCC_BCHECK
+    if (do_bounds_check && func_bound_offset != lbounds_section->data_offset) {
+        int saved_ind;
+        int *bounds_ptr;
+        Sym *sym, *sym_data;
+        /* add end of table info */
+        bounds_ptr = section_ptr_add(lbounds_section, sizeof(int));
+        *bounds_ptr = 0;
+        /* generate bound local allocation */
+        saved_ind = ind;
+        ind = func_sub_sp_offset;
+        sym_data = get_sym_ref(&char_pointer_type, lbounds_section, 
+                               func_bound_offset, lbounds_section->data_offset);
+        greloc(cur_text_section, sym_data,
+               ind + 1, R_386_32);
+        oad(0xb8, 0); /* mov %eax, xxx */
+        sym = external_global_sym(TOK___bound_local_new, &func_old_type, 0);
+        greloc(cur_text_section, sym, 
+               ind + 1, R_386_PC32);
+        oad(0xe8, -4);
+        ind = saved_ind;
+        /* generate bound check local freeing */
+        o(0x5250); /* save returned value, if any */
+        greloc(cur_text_section, sym_data,
+               ind + 1, R_386_32);
+        oad(0xb8, 0); /* mov %eax, xxx */
+        sym = external_global_sym(TOK___bound_local_delete, &func_old_type, 0);
+        greloc(cur_text_section, sym, 
+               ind + 1, R_386_PC32);
+        oad(0xe8, -4);
+        o(0x585a); /* restore returned value, if any */
+    }
+#endif
+    o(0xc9); /* leave */
+    if (func_ret_sub == 0) {
+        o(0xc3); /* ret */
+    } else {
+        o(0xc2); /* ret n */
+        g(func_ret_sub);
+        g(func_ret_sub >> 8);
+    }
+    /* align local size to word & save local variables */
+    
+    v = (-loc + 3) & -4; 
+    saved_ind = ind;
+    ind = func_sub_sp_offset - FUNC_PROLOG_SIZE;
+#ifdef TCC_TARGET_PE
+    if (v >= 4096) {
+        Sym *sym = external_global_sym(TOK___chkstk, &func_old_type, 0);
+        oad(0xb8, v); /* mov stacksize, %eax */
+        oad(0xe8, -4); /* call __chkstk, (does the stackframe too) */
+        greloc(cur_text_section, sym, ind-4, R_386_PC32);
+    } else
+#endif
+    {
+        o(0xe58955);  /* push %ebp, mov %esp, %ebp */
+        o(0xec81);  /* sub esp, stacksize */
+        gen_le32(v);
+#if FUNC_PROLOG_SIZE == 10
+        o(0x90);  /* adjust to FUNC_PROLOG_SIZE */
+#endif
+    }
+    ind = saved_ind;
+}
+
+/* generate a jump to a label */
+int gjmp(int t)
+{
+    return psym(0xe9, t);
+}
+
+/* generate a jump to a fixed address */
+void gjmp_addr(int a)
+{
+    int r;
+    r = a - ind - 2;
+    if (r == (char)r) {
+        g(0xeb);
+        g(r);
+    } else {
+        oad(0xe9, a - ind - 5);
+    }
+}
+
+/* generate a test. set 'inv' to invert test. Stack entry is popped */
+int gtst(int inv, int t)
+{
+    int v, *p;
+
+    v = vtop->r & VT_VALMASK;
+    if (v == VT_CMP) {
+        /* fast case : can jump directly since flags are set */
+        g(0x0f);
+        t = psym((vtop->c.i - 16) ^ inv, t);
+    } else if (v == VT_JMP || v == VT_JMPI) {
+        /* && or || optimization */
+        if ((v & 1) == inv) {
+            /* insert vtop->c jump list in t */
+            p = &vtop->c.i;
+            while (*p != 0)
+                p = (int *)(cur_text_section->data + *p);
+            *p = t;
+            t = vtop->c.i;
+        } else {
+            t = gjmp(t);
+            gsym(vtop->c.i);
+        }
+    } else {
+        if (is_float(vtop->type.t)) {
+            vpushi(0);
+            gen_op(TOK_NE);
+        }
+        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
+            /* constant jmp optimization */
+            if ((vtop->c.i != 0) != inv) 
+                t = gjmp(t);
+        } else {
+            v = gv(RC_INT);
+            o(0x85);
+            o(0xc0 + v * 9);
+            g(0x0f);
+            t = psym(0x85 ^ inv, t);
+        }
+    }
+    vtop--;
+    return t;
+}
+
+/* generate an integer binary operation */
+void gen_opi(int op)
+{
+    int r, fr, opc, c;
+
+    switch(op) {
+    case '+':
+    case TOK_ADDC1: /* add with carry generation */
+        opc = 0;
+    gen_op8:
+        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
+            /* constant case */
+            vswap();
+            r = gv(RC_INT);
+            vswap();
+            c = vtop->c.i;
+            if (c == (char)c) {
+                /* XXX: generate inc and dec for smaller code ? */
+                o(0x83);
+                o(0xc0 | (opc << 3) | r);
+                g(c);
+            } else {
+                o(0x81);
+                oad(0xc0 | (opc << 3) | r, c);
+            }
+        } else {
+            gv2(RC_INT, RC_INT);
+            r = vtop[-1].r;
+            fr = vtop[0].r;
+            o((opc << 3) | 0x01);
+            o(0xc0 + r + fr * 8); 
+        }
+        vtop--;
+        if (op >= TOK_ULT && op <= TOK_GT) {
+            vtop->r = VT_CMP;
+            vtop->c.i = op;
+        }
+        break;
+    case '-':
+    case TOK_SUBC1: /* sub with carry generation */
+        opc = 5;
+        goto gen_op8;
+    case TOK_ADDC2: /* add with carry use */
+        opc = 2;
+        goto gen_op8;
+    case TOK_SUBC2: /* sub with carry use */
+        opc = 3;
+        goto gen_op8;
+    case '&':
+        opc = 4;
+        goto gen_op8;
+    case '^':
+        opc = 6;
+        goto gen_op8;
+    case '|':
+        opc = 1;
+        goto gen_op8;
+    case '*':
+        gv2(RC_INT, RC_INT);
+        r = vtop[-1].r;
+        fr = vtop[0].r;
+        vtop--;
+        o(0xaf0f); /* imul fr, r */
+        o(0xc0 + fr + r * 8);
+        break;
+    case TOK_SHL:
+        opc = 4;
+        goto gen_shift;
+    case TOK_SHR:
+        opc = 5;
+        goto gen_shift;
+    case TOK_SAR:
+        opc = 7;
+    gen_shift:
+        opc = 0xc0 | (opc << 3);
+        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
+            /* constant case */
+            vswap();
+            r = gv(RC_INT);
+            vswap();
+            c = vtop->c.i & 0x1f;
+            o(0xc1); /* shl/shr/sar $xxx, r */
+            o(opc | r);
+            g(c);
+        } else {
+            /* we generate the shift in ecx */
+            gv2(RC_INT, RC_ECX);
+            r = vtop[-1].r;
+            o(0xd3); /* shl/shr/sar %cl, r */
+            o(opc | r);
+        }
+        vtop--;
+        break;
+    case '/':
+    case TOK_UDIV:
+    case TOK_PDIV:
+    case '%':
+    case TOK_UMOD:
+    case TOK_UMULL:
+        /* first operand must be in eax */
+        /* XXX: need better constraint for second operand */
+        gv2(RC_EAX, RC_ECX);
+        r = vtop[-1].r;
+        fr = vtop[0].r;
+        vtop--;
+        save_reg(TREG_EDX);
+        if (op == TOK_UMULL) {
+            o(0xf7); /* mul fr */
+            o(0xe0 + fr);
+            vtop->r2 = TREG_EDX;
+            r = TREG_EAX;
+        } else {
+            if (op == TOK_UDIV || op == TOK_UMOD) {
+                o(0xf7d231); /* xor %edx, %edx, div fr, %eax */
+                o(0xf0 + fr);
+            } else {
+                o(0xf799); /* cltd, idiv fr, %eax */
+                o(0xf8 + fr);
+            }
+            if (op == '%' || op == TOK_UMOD)
+                r = TREG_EDX;
+            else
+                r = TREG_EAX;
+        }
+        vtop->r = r;
+        break;
+    default:
+        opc = 7;
+        goto gen_op8;
+    }
+}
+
+/* generate a floating point operation 'v = t1 op t2' instruction. The
+   two operands are guaranted to have the same floating point type */
+/* XXX: need to use ST1 too */
+void gen_opf(int op)
+{
+    int a, ft, fc, swapped, r;
+
+    /* convert constants to memory references */
+    if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
+        vswap();
+        gv(RC_FLOAT);
+        vswap();
+    }
+    if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST)
+        gv(RC_FLOAT);
+
+    /* must put at least one value in the floating point register */
+    if ((vtop[-1].r & VT_LVAL) &&
+        (vtop[0].r & VT_LVAL)) {
+        vswap();
+        gv(RC_FLOAT);
+        vswap();
+    }
+    swapped = 0;
+    /* swap the stack if needed so that t1 is the register and t2 is
+       the memory reference */
+    if (vtop[-1].r & VT_LVAL) {
+        vswap();
+        swapped = 1;
+    }
+    if (op >= TOK_ULT && op <= TOK_GT) {
+        /* load on stack second operand */
+        load(TREG_ST0, vtop);
+        save_reg(TREG_EAX); /* eax is used by FP comparison code */
+        if (op == TOK_GE || op == TOK_GT)
+            swapped = !swapped;
+        else if (op == TOK_EQ || op == TOK_NE)
+            swapped = 0;
+        if (swapped)
+            o(0xc9d9); /* fxch %st(1) */
+        o(0xe9da); /* fucompp */
+        o(0xe0df); /* fnstsw %ax */
+        if (op == TOK_EQ) {
+            o(0x45e480); /* and $0x45, %ah */
+            o(0x40fC80); /* cmp $0x40, %ah */
+        } else if (op == TOK_NE) {
+            o(0x45e480); /* and $0x45, %ah */
+            o(0x40f480); /* xor $0x40, %ah */
+            op = TOK_NE;
+        } else if (op == TOK_GE || op == TOK_LE) {
+            o(0x05c4f6); /* test $0x05, %ah */
+            op = TOK_EQ;
+        } else {
+            o(0x45c4f6); /* test $0x45, %ah */
+            op = TOK_EQ;
+        }
+        vtop--;
+        vtop->r = VT_CMP;
+        vtop->c.i = op;
+    } else {
+        /* no memory reference possible for long double operations */
+        if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
+            load(TREG_ST0, vtop);
+            swapped = !swapped;
+        }
+        
+        switch(op) {
+        default:
+        case '+':
+            a = 0;
+            break;
+        case '-':
+            a = 4;
+            if (swapped)
+                a++;
+            break;
+        case '*':
+            a = 1;
+            break;
+        case '/':
+            a = 6;
+            if (swapped)
+                a++;
+            break;
+        }
+        ft = vtop->type.t;
+        fc = vtop->c.ul;
+        if ((ft & VT_BTYPE) == VT_LDOUBLE) {
+            o(0xde); /* fxxxp %st, %st(1) */
+            o(0xc1 + (a << 3));
+        } else {
+            /* if saved lvalue, then we must reload it */
+            r = vtop->r;
+            if ((r & VT_VALMASK) == VT_LLOCAL) {
+                SValue v1;
+                r = get_reg(RC_INT);
+                v1.type.t = VT_INT;
+                v1.r = VT_LOCAL | VT_LVAL;
+                v1.c.ul = fc;
+                load(r, &v1);
+                fc = 0;
+            }
+
+            if ((ft & VT_BTYPE) == VT_DOUBLE)
+                o(0xdc);
+            else
+                o(0xd8);
+            gen_modrm(a, r, vtop->sym, fc);
+        }
+        vtop--;
+    }
+}
+
+/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
+   and 'long long' cases. */
+void gen_cvt_itof(int t)
+{
+    save_reg(TREG_ST0);
+    gv(RC_INT);
+    if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
+        /* signed long long to float/double/long double (unsigned case
+           is handled generically) */
+        o(0x50 + vtop->r2); /* push r2 */
+        o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
+        o(0x242cdf); /* fildll (%esp) */
+        o(0x08c483); /* add $8, %esp */
+    } else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == 
+               (VT_INT | VT_UNSIGNED)) {
+        /* unsigned int to float/double/long double */
+        o(0x6a); /* push $0 */
+        g(0x00);
+        o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
+        o(0x242cdf); /* fildll (%esp) */
+        o(0x08c483); /* add $8, %esp */
+    } else {
+        /* int to float/double/long double */
+        o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
+        o(0x2404db); /* fildl (%esp) */
+        o(0x04c483); /* add $4, %esp */
+    }
+    vtop->r = TREG_ST0;
+}
+
+/* convert fp to int 't' type */
+/* XXX: handle long long case */
+void gen_cvt_ftoi(int t)
+{
+    int r, r2, size;
+    Sym *sym;
+    CType ushort_type;
+
+    ushort_type.t = VT_SHORT | VT_UNSIGNED;
+
+    gv(RC_FLOAT);
+    if (t != VT_INT)
+        size = 8;
+    else 
+        size = 4;
+    
+    o(0x2dd9); /* ldcw xxx */
+    sym = external_global_sym(TOK___tcc_int_fpu_control, 
+                              &ushort_type, VT_LVAL);
+    greloc(cur_text_section, sym, 
+           ind, R_386_32);
+    gen_le32(0);
+    
+    oad(0xec81, size); /* sub $xxx, %esp */
+    if (size == 4)
+        o(0x1cdb); /* fistpl */
+    else
+        o(0x3cdf); /* fistpll */
+    o(0x24);
+    o(0x2dd9); /* ldcw xxx */
+    sym = external_global_sym(TOK___tcc_fpu_control, 
+                              &ushort_type, VT_LVAL);
+    greloc(cur_text_section, sym, 
+           ind, R_386_32);
+    gen_le32(0);
+
+    r = get_reg(RC_INT);
+    o(0x58 + r); /* pop r */
+    if (size == 8) {
+        if (t == VT_LLONG) {
+            vtop->r = r; /* mark reg as used */
+            r2 = get_reg(RC_INT);
+            o(0x58 + r2); /* pop r2 */
+            vtop->r2 = r2;
+        } else {
+            o(0x04c483); /* add $4, %esp */
+        }
+    }
+    vtop->r = r;
+}
+
+/* convert from one floating point type to another */
+void gen_cvt_ftof(int t)
+{
+    /* all we have to do on i386 is to put the float in a register */
+    gv(RC_FLOAT);
+}
+
+/* computed goto support */
+void ggoto(void)
+{
+    gcall_or_jmp(1);
+    vtop--;
+}
+
+/* bound check support functions */
+#ifdef CONFIG_TCC_BCHECK
+
+/* generate a bounded pointer addition */
+void gen_bounded_ptr_add(void)
+{
+    Sym *sym;
+
+    /* prepare fast i386 function call (args in eax and edx) */
+    gv2(RC_EAX, RC_EDX);
+    /* save all temporary registers */
+    vtop -= 2;
+    save_regs(0);
+    /* do a fast function call */
+    sym = external_global_sym(TOK___bound_ptr_add, &func_old_type, 0);
+    greloc(cur_text_section, sym, 
+           ind + 1, R_386_PC32);
+    oad(0xe8, -4);
+    /* returned pointer is in eax */
+    vtop++;
+    vtop->r = TREG_EAX | VT_BOUNDED;
+    /* address of bounding function call point */
+    vtop->c.ul = (cur_text_section->reloc->data_offset - sizeof(Elf32_Rel)); 
+}
+
+/* patch pointer addition in vtop so that pointer dereferencing is
+   also tested */
+void gen_bounded_ptr_deref(void)
+{
+    int func;
+    int size, align;
+    Elf32_Rel *rel;
+    Sym *sym;
+
+    size = 0;
+    /* XXX: put that code in generic part of tcc */
+    if (!is_float(vtop->type.t)) {
+        if (vtop->r & VT_LVAL_BYTE)
+            size = 1;
+        else if (vtop->r & VT_LVAL_SHORT)
+            size = 2;
+    }
+    if (!size)
+        size = type_size(&vtop->type, &align);
+    switch(size) {
+    case  1: func = TOK___bound_ptr_indir1; break;
+    case  2: func = TOK___bound_ptr_indir2; break;
+    case  4: func = TOK___bound_ptr_indir4; break;
+    case  8: func = TOK___bound_ptr_indir8; break;
+    case 12: func = TOK___bound_ptr_indir12; break;
+    case 16: func = TOK___bound_ptr_indir16; break;
+    default:
+        error("unhandled size when derefencing bounded pointer");
+        func = 0;
+        break;
+    }
+
+    /* patch relocation */
+    /* XXX: find a better solution ? */
+    rel = (Elf32_Rel *)(cur_text_section->reloc->data + vtop->c.ul);
+    sym = external_global_sym(func, &func_old_type, 0);
+    if (!sym->c)
+        put_extern_sym(sym, NULL, 0, 0);
+    rel->r_info = ELF32_R_INFO(sym->c, ELF32_R_TYPE(rel->r_info));
+}
+#endif
+
+/* end of X86 code generator */
+/*************************************************************/
+
+//---------------------------------------------------------------------------
+#endif
+
+// njn: commented these out
+//#ifdef TCC_TARGET_ARM
+//#include "arm-gen.c"
+//#endif
+//
+//#ifdef TCC_TARGET_C67
+//#include "c67-gen.c"
+//#endif
+
+#ifdef CONFIG_TCC_STATIC
+
+#define RTLD_LAZY       0x001
+#define RTLD_NOW        0x002
+#define RTLD_GLOBAL     0x100
+#define RTLD_DEFAULT    NULL
+
+/* dummy function for profiling */
+void *dlopen(const char *filename, int flag)
+{
+    return NULL;
+}
+
+const char *dlerror(void)
+{
+    return "error";
+}
+
+typedef struct TCCSyms {
+    char *str;
+    void *ptr;
+} TCCSyms;
+
+#define TCCSYM(a) { #a, &a, },
+
+/* add the symbol you want here if no dynamic linking is done */
+static TCCSyms tcc_syms[] = {
+#if !defined(CONFIG_TCCBOOT)
+    TCCSYM(printf)
+    TCCSYM(fprintf)
+    TCCSYM(fopen)
+    TCCSYM(fclose)
+#endif
+    { NULL, NULL },
+};
+
+void *resolve_sym(TCCState *s1, const char *symbol, int type)
+{
+    TCCSyms *p;
+    p = tcc_syms;
+    while (p->str != NULL) {
+        if (!strcmp(p->str, symbol))
+            return p->ptr;
+        p++;
+    }
+    return NULL;
+}
+
+#elif !defined(WIN32)
+
+#include <dlfcn.h>
+
+void *resolve_sym(TCCState *s1, const char *sym, int type)
+{
+    return dlsym(RTLD_DEFAULT, sym);
+}
+
+#endif
+
+/********************************************************/
+
+/* we use our own 'finite' function to avoid potential problems with
+   non standard math libs */
+/* XXX: endianness dependent */
+int ieee_finite(double d)
+{
+    int *p = (int *)&d;
+    return ((unsigned)((p[1] | 0x800fffff) + 1)) >> 31;
+}
+
+/* copy a string and truncate it. */
+static char *pstrcpy(char *buf, int buf_size, const char *s)
+{
+    char *q, *q_end;
+    int c;
+
+    if (buf_size > 0) {
+        q = buf;
+        q_end = buf + buf_size - 1;
+        while (q < q_end) {
+            c = *s++;
+            if (c == '\0')
+                break;
+            *q++ = c;
+        }
+        *q = '\0';
+    }
+    return buf;
+}
+
+/* strcat and truncate. */
+static char *pstrcat(char *buf, int buf_size, const char *s)
+{
+    int len;
+    len = strlen(buf);
+    if (len < buf_size) 
+        pstrcpy(buf + len, buf_size - len, s);
+    return buf;
+}
+
+static int strstart(const char *str, const char *val, const char **ptr)
+{
+    const char *p, *q;
+    p = str;
+    q = val;
+    while (*q != '\0') {
+        if (*p != *q)
+            return 0;
+        p++;
+        q++;
+    }
+    if (ptr)
+        *ptr = p;
+    return 1;
+}
+
+/* memory management */
+#ifdef MEM_DEBUG
+int mem_cur_size;
+int mem_max_size;
+#endif
+
+static inline void tcc_free(void *ptr)
+{
+#ifdef MEM_DEBUG
+    mem_cur_size -= malloc_usable_size(ptr);
+#endif
+    free(ptr);
+}
+
+static void *tcc_malloc(unsigned long size)
+{
+    void *ptr;
+    ptr = malloc(size);
+    if (!ptr && size)
+        error("memory full");
+#ifdef MEM_DEBUG
+    mem_cur_size += malloc_usable_size(ptr);
+    if (mem_cur_size > mem_max_size)
+        mem_max_size = mem_cur_size;
+#endif
+    return ptr;
+}
+
+static void *tcc_mallocz(unsigned long size)
+{
+    void *ptr;
+    ptr = tcc_malloc(size);
+    memset(ptr, 0, size);
+    return ptr;
+}
+
+static inline void *tcc_realloc(void *ptr, unsigned long size)
+{
+    void *ptr1;
+#ifdef MEM_DEBUG
+    mem_cur_size -= malloc_usable_size(ptr);
+#endif
+    ptr1 = realloc(ptr, size);
+#ifdef MEM_DEBUG
+    /* NOTE: count not correct if alloc error, but not critical */
+    mem_cur_size += malloc_usable_size(ptr1);
+    if (mem_cur_size > mem_max_size)
+        mem_max_size = mem_cur_size;
+#endif
+    return ptr1;
+}
+
+static char *tcc_strdup(const char *str)
+{
+    char *ptr;
+    ptr = tcc_malloc(strlen(str) + 1);
+    strcpy(ptr, str);
+    return ptr;
+}
+
+#define free(p) use_tcc_free(p)
+#define malloc(s) use_tcc_malloc(s)
+#define realloc(p, s) use_tcc_realloc(p, s)
+
+static void dynarray_add(void ***ptab, int *nb_ptr, void *data)
+{
+    int nb, nb_alloc;
+    void **pp;
+    
+    nb = *nb_ptr;
+    pp = *ptab;
+    /* every power of two we double array size */
+    if ((nb & (nb - 1)) == 0) {
+        if (!nb)
+            nb_alloc = 1;
+        else
+            nb_alloc = nb * 2;
+        pp = tcc_realloc(pp, nb_alloc * sizeof(void *));
+        if (!pp)
+            error("memory full");
+        *ptab = pp;
+    }
+    pp[nb++] = data;
+    *nb_ptr = nb;
+}
+
+/* symbol allocator */
+static Sym *__sym_malloc(void)
+{
+    Sym *sym_pool, *sym, *last_sym;
+    int i;
+
+    sym_pool = tcc_malloc(SYM_POOL_NB * sizeof(Sym));
+
+    last_sym = sym_free_first;
+    sym = sym_pool;
+    for(i = 0; i < SYM_POOL_NB; i++) {
+        sym->next = last_sym;
+        last_sym = sym;
+        sym++;
+    }
+    sym_free_first = last_sym;
+    return last_sym;
+}
+
+static inline Sym *sym_malloc(void)
+{
+    Sym *sym;
+    sym = sym_free_first;
+    if (!sym)
+        sym = __sym_malloc();
+    sym_free_first = sym->next;
+    return sym;
+}
+
+static inline void sym_free(Sym *sym)
+{
+    sym->next = sym_free_first;
+    sym_free_first = sym;
+}
+
+Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags)
+{
+    Section *sec;
+
+    sec = tcc_mallocz(sizeof(Section) + strlen(name));
+    strcpy(sec->name, name);
+    sec->sh_type = sh_type;
+    sec->sh_flags = sh_flags;
+    switch(sh_type) {
+    case SHT_HASH:
+    case SHT_REL:
+    case SHT_DYNSYM:
+    case SHT_SYMTAB:
+    case SHT_DYNAMIC:
+        sec->sh_addralign = 4;
+        break;
+    case SHT_STRTAB:
+        sec->sh_addralign = 1;
+        break;
+    default:
+        sec->sh_addralign = 32; /* default conservative alignment */
+        break;
+    }
+
+    /* only add section if not private */
+    if (!(sh_flags & SHF_PRIVATE)) {
+        sec->sh_num = s1->nb_sections;
+        dynarray_add((void ***)&s1->sections, &s1->nb_sections, sec);
+    }
+    return sec;
+}
+
+static void free_section(Section *s)
+{
+    tcc_free(s->data);
+    tcc_free(s);
+}
+
+/* realloc section and set its content to zero */
+static void section_realloc(Section *sec, unsigned long new_size)
+{
+    unsigned long size;
+    unsigned char *data;
+    
+    size = sec->data_allocated;
+    if (size == 0)
+        size = 1;
+    while (size < new_size)
+        size = size * 2;
+    data = tcc_realloc(sec->data, size);
+    if (!data)
+        error("memory full");
+    memset(data + sec->data_allocated, 0, size - sec->data_allocated);
+    sec->data = data;
+    sec->data_allocated = size;
+}
+
+/* reserve at least 'size' bytes in section 'sec' from
+   sec->data_offset. */
+static void *section_ptr_add(Section *sec, unsigned long size)
+{
+    unsigned long offset, offset1;
+
+    offset = sec->data_offset;
+    offset1 = offset + size;
+    if (offset1 > sec->data_allocated)
+        section_realloc(sec, offset1);
+    sec->data_offset = offset1;
+    return sec->data + offset;
+}
+
+/* return a reference to a section, and create it if it does not
+   exists */
+Section *find_section(TCCState *s1, const char *name)
+{
+    Section *sec;
+    int i;
+    for(i = 1; i < s1->nb_sections; i++) {
+        sec = s1->sections[i];
+        if (!strcmp(name, sec->name)) 
+            return sec;
+    }
+    /* sections are created as PROGBITS */
+    return new_section(s1, name, SHT_PROGBITS, SHF_ALLOC);
+}
+
+#define SECTION_ABS ((void *)1)
+
+/* update sym->c so that it points to an external symbol in section
+   'section' with value 'value' */
+static void put_extern_sym2(Sym *sym, Section *section, 
+                            unsigned long value, unsigned long size,
+                            int can_add_underscore)
+{
+    int sym_type, sym_bind, sh_num, info;
+    Elf32_Sym *esym;
+    const char *name;
+    char buf1[256];
+
+    if (section == NULL)
+        sh_num = SHN_UNDEF;
+    else if (section == SECTION_ABS) 
+        sh_num = SHN_ABS;
+    else
+        sh_num = section->sh_num;
+    if (!sym->c) {
+        if ((sym->type.t & VT_BTYPE) == VT_FUNC)
+            sym_type = STT_FUNC;
+        else
+            sym_type = STT_OBJECT;
+        if (sym->type.t & VT_STATIC)
+            sym_bind = STB_LOCAL;
+        else
+            sym_bind = STB_GLOBAL;
+        
+        name = get_tok_str(sym->v, NULL);
+#ifdef CONFIG_TCC_BCHECK
+        if (do_bounds_check) {
+            char buf[32];
+
+            /* XXX: avoid doing that for statics ? */
+            /* if bound checking is activated, we change some function
+               names by adding the "__bound" prefix */
+            switch(sym->v) {
+#if 0
+            /* XXX: we rely only on malloc hooks */
+            case TOK_malloc: 
+            case TOK_free: 
+            case TOK_realloc: 
+            case TOK_memalign: 
+            case TOK_calloc: 
+#endif
+            case TOK_memcpy: 
+            case TOK_memmove:
+            case TOK_memset:
+            case TOK_strlen:
+            case TOK_strcpy:
+                strcpy(buf, "__bound_");
+                strcat(buf, name);
+                name = buf;
+                break;
+            }
+        }
+#endif
+        if (tcc_state->leading_underscore && can_add_underscore) {
+            buf1[0] = '_';
+            pstrcpy(buf1 + 1, sizeof(buf1) - 1, name);
+            name = buf1;
+        }
+        info = ELF32_ST_INFO(sym_bind, sym_type);
+        sym->c = add_elf_sym(symtab_section, value, size, info, 0, sh_num, name);
+    } else {
+        esym = &((Elf32_Sym *)symtab_section->data)[sym->c];
+        esym->st_value = value;
+        esym->st_size = size;
+        esym->st_shndx = sh_num;
+    }
+}
+
+static void put_extern_sym(Sym *sym, Section *section, 
+                           unsigned long value, unsigned long size)
+{
+    put_extern_sym2(sym, section, value, size, 1);
+}
+
+/* add a new relocation entry to symbol 'sym' in section 's' */
+static void greloc(Section *s, Sym *sym, unsigned long offset, int type)
+{
+    if (!sym->c) 
+        put_extern_sym(sym, NULL, 0, 0);
+    /* now we can add ELF relocation info */
+    put_elf_reloc(symtab_section, s, offset, type, sym->c);
+}
+
+static inline int isid(int c)
+{
+    return (c >= 'a' && c <= 'z') ||
+        (c >= 'A' && c <= 'Z') ||
+        c == '_';
+}
+
+static inline int isnum(int c)
+{
+    return c >= '0' && c <= '9';
+}
+
+static inline int isoct(int c)
+{
+    return c >= '0' && c <= '7';
+}
+
+static inline int toup(int c)
+{
+    if (c >= 'a' && c <= 'z')
+        return c - 'a' + 'A';
+    else
+        return c;
+}
+
+static void strcat_vprintf(char *buf, int buf_size, const char *fmt, va_list ap)
+{
+    int len;
+    len = strlen(buf);
+    vsnprintf(buf + len, buf_size - len, fmt, ap);
+}
+
+static void strcat_printf(char *buf, int buf_size, const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    strcat_vprintf(buf, buf_size, fmt, ap);
+    va_end(ap);
+}
+
+void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap)
+{
+    char buf[2048];
+    BufferedFile **f;
+    
+    buf[0] = '\0';
+    if (file) {
+        for(f = s1->include_stack; f < s1->include_stack_ptr; f++)
+            strcat_printf(buf, sizeof(buf), "In file included from %s:%d:\n", 
+                          (*f)->filename, (*f)->line_num);
+        if (file->line_num > 0) {
+            strcat_printf(buf, sizeof(buf), 
+                          "%s:%d: ", file->filename, file->line_num);
+        } else {
+            strcat_printf(buf, sizeof(buf),
+                          "%s: ", file->filename);
+        }
+    } else {
+        strcat_printf(buf, sizeof(buf),
+                      "tcc: ");
+    }
+    if (is_warning)
+        strcat_printf(buf, sizeof(buf), "warning: ");
+    strcat_vprintf(buf, sizeof(buf), fmt, ap);
+
+    if (!s1->error_func) {
+        /* default case: stderr */
+        fprintf(stderr, "%s\n", buf);
+    } else {
+        s1->error_func(s1->error_opaque, buf);
+    }
+    if (!is_warning || s1->warn_error)
+        s1->nb_errors++;
+}
+
+#ifdef LIBTCC
+void tcc_set_error_func(TCCState *s, void *error_opaque,
+                        void (*error_func)(void *opaque, const char *msg))
+{
+    s->error_opaque = error_opaque;
+    s->error_func = error_func;
+}
+#endif
+
+/* error without aborting current compilation */
+void error_noabort(const char *fmt, ...)
+{
+    TCCState *s1 = tcc_state;
+    va_list ap;
+
+    va_start(ap, fmt);
+    error1(s1, 0, fmt, ap);
+    va_end(ap);
+}
+
+void error(const char *fmt, ...)
+{
+    TCCState *s1 = tcc_state;
+    va_list ap;
+
+    va_start(ap, fmt);
+    error1(s1, 0, fmt, ap);
+    va_end(ap);
+    /* better than nothing: in some cases, we accept to handle errors */
+    if (s1->error_set_jmp_enabled) {
+        longjmp(s1->error_jmp_buf, 1);
+    } else {
+        /* XXX: eliminate this someday */
+        exit(1);
+    }
+}
+
+void expect(const char *msg)
+{
+    error("%s expected", msg);
+}
+
+void warning(const char *fmt, ...)
+{
+    TCCState *s1 = tcc_state;
+    va_list ap;
+
+    if (s1->warn_none)
+        return;
+
+    va_start(ap, fmt);
+    error1(s1, 1, fmt, ap);
+    va_end(ap);
+}
+
+void skip(int c)
+{
+    if (tok != c)
+        error("'%c' expected", c);
+    next();
+}
+
+static void test_lvalue(void)
+{
+    if (!(vtop->r & VT_LVAL))
+        expect("lvalue");
+}
+
+/* allocate a new token */
+static TokenSym *tok_alloc_new(TokenSym **pts, const char *str, int len)
+{
+    TokenSym *ts, **ptable;
+    int i;
+
+    if (tok_ident >= SYM_FIRST_ANOM) 
+        error("memory full");
+
+    /* expand token table if needed */
+    i = tok_ident - TOK_IDENT;
+    if ((i % TOK_ALLOC_INCR) == 0) {
+        ptable = tcc_realloc(table_ident, (i + TOK_ALLOC_INCR) * sizeof(TokenSym *));
+        if (!ptable)
+            error("memory full");
+        table_ident = ptable;
+    }
+
+    ts = tcc_malloc(sizeof(TokenSym) + len);
+    table_ident[i] = ts;
+    ts->tok = tok_ident++;
+    ts->sym_define = NULL;
+    ts->sym_label = NULL;
+    ts->sym_struct = NULL;
+    ts->sym_identifier = NULL;
+    ts->len = len;
+    ts->hash_next = NULL;
+    memcpy(ts->str, str, len);
+    ts->str[len] = '\0';
+    *pts = ts;
+    return ts;
+}
+
+#define TOK_HASH_INIT 1
+#define TOK_HASH_FUNC(h, c) ((h) * 263 + (c))
+
+/* find a token and add it if not found */
+static TokenSym *tok_alloc(const char *str, int len)
+{
+    TokenSym *ts, **pts;
+    int i;
+    unsigned int h;
+    
+    h = TOK_HASH_INIT;
+    for(i=0;i<len;i++)
+        h = TOK_HASH_FUNC(h, ((unsigned char *)str)[i]);
+    h &= (TOK_HASH_SIZE - 1);
+
+    pts = &hash_ident[h];
+    for(;;) {
+        ts = *pts;
+        if (!ts)
+            break;
+        if (ts->len == len && !memcmp(ts->str, str, len))
+            return ts;
+        pts = &(ts->hash_next);
+    }
+    return tok_alloc_new(pts, str, len);
+}
+
+/* CString handling */
+
+static void cstr_realloc(CString *cstr, int new_size)
+{
+    int size;
+    void *data;
+
+    size = cstr->size_allocated;
+    if (size == 0)
+        size = 8; /* no need to allocate a too small first string */
+    while (size < new_size)
+        size = size * 2;
+    data = tcc_realloc(cstr->data_allocated, size);
+    if (!data)
+        error("memory full");
+    cstr->data_allocated = data;
+    cstr->size_allocated = size;
+    cstr->data = data;
+}
+
+/* add a byte */
+static inline void cstr_ccat(CString *cstr, int ch)
+{
+    int size;
+    size = cstr->size + 1;
+    if (size > cstr->size_allocated)
+        cstr_realloc(cstr, size);
+    ((unsigned char *)cstr->data)[size - 1] = ch;
+    cstr->size = size;
+}
+
+static void cstr_cat(CString *cstr, const char *str)
+{
+    int c;
+    for(;;) {
+        c = *str;
+        if (c == '\0')
+            break;
+        cstr_ccat(cstr, c);
+        str++;
+    }
+}
+
+/* add a wide char */
+static void cstr_wccat(CString *cstr, int ch)
+{
+    int size;
+    size = cstr->size + sizeof(int);
+    if (size > cstr->size_allocated)
+        cstr_realloc(cstr, size);
+    *(int *)(((unsigned char *)cstr->data) + size - sizeof(int)) = ch;
+    cstr->size = size;
+}
+
+static void cstr_new(CString *cstr)
+{
+    memset(cstr, 0, sizeof(CString));
+}
+
+/* free string and reset it to NULL */
+static void cstr_free(CString *cstr)
+{
+    tcc_free(cstr->data_allocated);
+    cstr_new(cstr);
+}
+
+#define cstr_reset(cstr) cstr_free(cstr)
+
+/* XXX: unicode ? */
+static void add_char(CString *cstr, int c)
+{
+    if (c == '\'' || c == '\"' || c == '\\') {
+        /* XXX: could be more precise if char or string */
+        cstr_ccat(cstr, '\\');
+    }
+    if (c >= 32 && c <= 126) {
+        cstr_ccat(cstr, c);
+    } else {
+        cstr_ccat(cstr, '\\');
+        if (c == '\n') {
+            cstr_ccat(cstr, 'n');
+        } else {
+            cstr_ccat(cstr, '0' + ((c >> 6) & 7));
+            cstr_ccat(cstr, '0' + ((c >> 3) & 7));
+            cstr_ccat(cstr, '0' + (c & 7));
+        }
+    }
+}
+
+/* XXX: buffer overflow */
+/* XXX: float tokens */
+char *get_tok_str(int v, CValue *cv)
+{
+    static char buf[STRING_MAX_SIZE + 1];
+    static CString cstr_buf;
+    CString *cstr;
+    unsigned char *q;
+    char *p;
+    int i, len;
+
+    /* NOTE: to go faster, we give a fixed buffer for small strings */
+    cstr_reset(&cstr_buf);
+    cstr_buf.data = buf;
+    cstr_buf.size_allocated = sizeof(buf);
+    p = buf;
+
+    switch(v) {
+    case TOK_CINT:
+    case TOK_CUINT:
+        /* XXX: not quite exact, but only useful for testing */
+        sprintf(p, "%u", cv->ui);
+        break;
+    case TOK_CLLONG:
+    case TOK_CULLONG:
+        /* XXX: not quite exact, but only useful for testing  */
+        sprintf(p, "%Lu", cv->ull);
+        break;
+    case TOK_CCHAR:
+    case TOK_LCHAR:
+        cstr_ccat(&cstr_buf, '\'');
+        add_char(&cstr_buf, cv->i);
+        cstr_ccat(&cstr_buf, '\'');
+        cstr_ccat(&cstr_buf, '\0');
+        break;
+    case TOK_PPNUM:
+        cstr = cv->cstr;
+        len = cstr->size - 1;
+        for(i=0;i<len;i++)
+            add_char(&cstr_buf, ((unsigned char *)cstr->data)[i]);
+        cstr_ccat(&cstr_buf, '\0');
+        break;
+    case TOK_STR:
+    case TOK_LSTR:
+        cstr = cv->cstr;
+        cstr_ccat(&cstr_buf, '\"');
+        if (v == TOK_STR) {
+            len = cstr->size - 1;
+            for(i=0;i<len;i++)
+                add_char(&cstr_buf, ((unsigned char *)cstr->data)[i]);
+        } else {
+            len = (cstr->size / sizeof(int)) - 1;
+            for(i=0;i<len;i++)
+                add_char(&cstr_buf, ((int *)cstr->data)[i]);
+        }
+        cstr_ccat(&cstr_buf, '\"');
+        cstr_ccat(&cstr_buf, '\0');
+        break;
+    case TOK_LT:
+        v = '<';
+        goto addv;
+    case TOK_GT:
+        v = '>';
+        goto addv;
+    case TOK_A_SHL:
+        return strcpy(p, "<<=");
+    case TOK_A_SAR:
+        return strcpy(p, ">>=");
+    default:
+        if (v < TOK_IDENT) {
+            /* search in two bytes table */
+            q = tok_two_chars;
+            while (*q) {
+                if (q[2] == v) {
+                    *p++ = q[0];
+                    *p++ = q[1];
+                    *p = '\0';
+                    return buf;
+                }
+                q += 3;
+            }
+        addv:
+            *p++ = v;
+            *p = '\0';
+        } else if (v < tok_ident) {
+            return table_ident[v - TOK_IDENT]->str;
+        } else if (v >= SYM_FIRST_ANOM) {
+            /* special name for anonymous symbol */
+            sprintf(p, "L.%u", v - SYM_FIRST_ANOM);
+        } else {
+            /* should never happen */
+            return NULL;
+        }
+        break;
+    }
+    return cstr_buf.data;
+}
+
+/* push, without hashing */
+static Sym *sym_push2(Sym **ps, int v, int t, int c)
+{
+    Sym *s;
+    s = sym_malloc();
+    s->v = v;
+    s->type.t = t;
+    s->c = c;
+    s->next = NULL;
+    /* add in stack */
+    s->prev = *ps;
+    *ps = s;
+    return s;
+}
+
+/* find a symbol and return its associated structure. 's' is the top
+   of the symbol stack */
+static Sym *sym_find2(Sym *s, int v)
+{
+    while (s) {
+        if (s->v == v)
+            return s;
+        s = s->prev;
+    }
+    return NULL;
+}
+
+/* structure lookup */
+static inline Sym *struct_find(int v)
+{
+    v -= TOK_IDENT;
+    if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
+        return NULL;
+    return table_ident[v]->sym_struct;
+}
+
+/* find an identifier */
+static inline Sym *sym_find(int v)
+{
+    v -= TOK_IDENT;
+    if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
+        return NULL;
+    return table_ident[v]->sym_identifier;
+}
+
+/* push a given symbol on the symbol stack */
+static Sym *sym_push(int v, CType *type, int r, int c)
+{
+    Sym *s, **ps;
+    TokenSym *ts;
+
+    if (local_stack)
+        ps = &local_stack;
+    else
+        ps = &global_stack;
+    s = sym_push2(ps, v, type->t, c);
+    s->type.ref = type->ref;
+    s->r = r;
+    /* don't record fields or anonymous symbols */
+    /* XXX: simplify */
+    if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
+        /* record symbol in token array */
+        ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT];
+        if (v & SYM_STRUCT)
+            ps = &ts->sym_struct;
+        else
+            ps = &ts->sym_identifier;
+        s->prev_tok = *ps;
+        *ps = s;
+    }
+    return s;
+}
+
+/* push a global identifier */
+static Sym *global_identifier_push(int v, int t, int c)
+{
+    Sym *s, **ps;
+    s = sym_push2(&global_stack, v, t, c);
+    /* don't record anonymous symbol */
+    if (v < SYM_FIRST_ANOM) {
+        ps = &table_ident[v - TOK_IDENT]->sym_identifier;
+        /* modify the top most local identifier, so that
+           sym_identifier will point to 's' when popped */
+        while (*ps != NULL)
+            ps = &(*ps)->prev_tok;
+        s->prev_tok = NULL;
+        *ps = s;
+    }
+    return s;
+}
+
+/* pop symbols until top reaches 'b' */
+static void sym_pop(Sym **ptop, Sym *b)
+{
+    Sym *s, *ss, **ps;
+    TokenSym *ts;
+    int v;
+
+    s = *ptop;
+    while(s != b) {
+        ss = s->prev;
+        v = s->v;
+        /* remove symbol in token array */
+        /* XXX: simplify */
+        if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
+            ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT];
+            if (v & SYM_STRUCT)
+                ps = &ts->sym_struct;
+            else
+                ps = &ts->sym_identifier;
+            *ps = s->prev_tok;
+        }
+        sym_free(s);
+        s = ss;
+    }
+    *ptop = b;
+}
+
+/* I/O layer */
+
+BufferedFile *tcc_open(TCCState *s1, const char *filename)
+{
+    int fd;
+    BufferedFile *bf;
+
+    fd = open(filename, O_RDONLY | O_BINARY);
+    if (fd < 0)
+        return NULL;
+    bf = tcc_malloc(sizeof(BufferedFile));
+    if (!bf) {
+        close(fd);
+        return NULL;
+    }
+    bf->fd = fd;
+    bf->buf_ptr = bf->buffer;
+    bf->buf_end = bf->buffer;
+    bf->buffer[0] = CH_EOB; /* put eob symbol */
+    pstrcpy(bf->filename, sizeof(bf->filename), filename);
+    bf->line_num = 1;
+    bf->ifndef_macro = 0;
+    bf->ifdef_stack_ptr = s1->ifdef_stack_ptr;
+    //    printf("opening '%s'\n", filename);
+    return bf;
+}
+
+void tcc_close(BufferedFile *bf)
+{
+    total_lines += bf->line_num;
+    close(bf->fd);
+    tcc_free(bf);
+}
+
+/* fill input buffer and peek next char */
+static int tcc_peekc_slow(BufferedFile *bf)
+{
+    int len;
+    /* only tries to read if really end of buffer */
+    if (bf->buf_ptr >= bf->buf_end) {
+        if (bf->fd != -1) {
+#if defined(PARSE_DEBUG)
+            len = 8;
+#else
+            len = IO_BUF_SIZE;
+#endif
+            len = read(bf->fd, bf->buffer, len);
+            if (len < 0)
+                len = 0;
+        } else {
+            len = 0;
+        }
+        total_bytes += len;
+        bf->buf_ptr = bf->buffer;
+        bf->buf_end = bf->buffer + len;
+        *bf->buf_end = CH_EOB;
+    }
+    if (bf->buf_ptr < bf->buf_end) {
+        return bf->buf_ptr[0];
+    } else {
+        bf->buf_ptr = bf->buf_end;
+        return CH_EOF;
+    }
+}
+
+/* return the current character, handling end of block if necessary
+   (but not stray) */
+static int handle_eob(void)
+{
+    return tcc_peekc_slow(file);
+}
+
+/* read next char from current input file and handle end of input buffer */
+static inline void inp(void)
+{
+    ch = *(++(file->buf_ptr));
+    /* end of buffer/file handling */
+    if (ch == CH_EOB)
+        ch = handle_eob();
+}
+
+/* handle '\[\r]\n' */
+static void handle_stray(void)
+{
+    while (ch == '\\') {
+        inp();
+        if (ch == '\n') {
+            file->line_num++;
+            inp();
+        } else if (ch == '\r') {
+            inp();
+            if (ch != '\n')
+                goto fail;
+            file->line_num++;
+            inp();
+        } else {
+        fail:
+            error("stray '\\' in program");
+        }
+    }
+}
+
+/* skip the stray and handle the \\n case. Output an error if
+   incorrect char after the stray */
+static int handle_stray1(uint8_t *p)
+{
+    int c;
+
+    if (p >= file->buf_end) {
+        file->buf_ptr = p;
+        c = handle_eob();
+        p = file->buf_ptr;
+        if (c == '\\')
+            goto parse_stray;
+    } else {
+    parse_stray:
+        file->buf_ptr = p;
+        ch = *p;
+        handle_stray();
+        p = file->buf_ptr;
+        c = *p;
+    }
+    return c;
+}
+
+/* handle just the EOB case, but not stray */
+#define PEEKC_EOB(c, p)\
+{\
+    p++;\
+    c = *p;\
+    if (c == '\\') {\
+        file->buf_ptr = p;\
+        c = handle_eob();\
+        p = file->buf_ptr;\
+    }\
+}
+
+/* handle the complicated stray case */
+#define PEEKC(c, p)\
+{\
+    p++;\
+    c = *p;\
+    if (c == '\\') {\
+        c = handle_stray1(p);\
+        p = file->buf_ptr;\
+    }\
+}
+
+/* input with '\[\r]\n' handling. Note that this function cannot
+   handle other characters after '\', so you cannot call it inside
+   strings or comments */
+static void minp(void)
+{
+    inp();
+    if (ch == '\\') 
+        handle_stray();
+}
+
+
+/* single line C++ comments */
+static uint8_t *parse_line_comment(uint8_t *p)
+{
+    int c;
+
+    p++;
+    for(;;) {
+        c = *p;
+    redo:
+        if (c == '\n' || c == CH_EOF) {
+            break;
+        } else if (c == '\\') {
+            file->buf_ptr = p;
+            c = handle_eob();
+            p = file->buf_ptr;
+            if (c == '\\') {
+                PEEKC_EOB(c, p);
+                if (c == '\n') {
+                    file->line_num++;
+                    PEEKC_EOB(c, p);
+                } else if (c == '\r') {
+                    PEEKC_EOB(c, p);
+                    if (c == '\n') {
+                        file->line_num++;
+                        PEEKC_EOB(c, p);
+                    }
+                }
+            } else {
+                goto redo;
+            }
+        } else {
+            p++;
+        }
+    }
+    return p;
+}
+
+/* C comments */
+static uint8_t *parse_comment(uint8_t *p)
+{
+    int c;
+    
+    p++;
+    for(;;) {
+        /* fast skip loop */
+        for(;;) {
+            c = *p;
+            if (c == '\n' || c == '*' || c == '\\')
+                break;
+            p++;
+            c = *p;
+            if (c == '\n' || c == '*' || c == '\\')
+                break;
+            p++;
+        }
+        /* now we can handle all the cases */
+        if (c == '\n') {
+            file->line_num++;
+            p++;
+        } else if (c == '*') {
+            p++;
+            for(;;) {
+                c = *p;
+                if (c == '*') {
+                    p++;
+                } else if (c == '/') {
+                    goto end_of_comment;
+                } else if (c == '\\') {
+                    file->buf_ptr = p;
+                    c = handle_eob();
+                    p = file->buf_ptr;
+                    if (c == '\\') {
+                        /* skip '\[\r]\n', otherwise just skip the stray */
+                        while (c == '\\') {
+                            PEEKC_EOB(c, p);
+                            if (c == '\n') {
+                                file->line_num++;
+                                PEEKC_EOB(c, p);
+                            } else if (c == '\r') {
+                                PEEKC_EOB(c, p);
+                                if (c == '\n') {
+                                    file->line_num++;
+                                    PEEKC_EOB(c, p);
+                                }
+                            } else {
+                                goto after_star;
+                            }
+                        }
+                    }
+                } else {
+                    break;
+                }
+            }
+        after_star: ;
+        } else {
+            /* stray, eob or eof */
+            file->buf_ptr = p;
+            c = handle_eob();
+            p = file->buf_ptr;
+            if (c == CH_EOF) {
+                error("unexpected end of file in comment");
+            } else if (c == '\\') {
+                p++;
+            }
+        }
+    }
+ end_of_comment:
+    p++;
+    return p;
+}
+
+#define cinp minp
+
+/* space exlcuding newline */
+static inline int is_space(int ch)
+{
+    return ch == ' ' || ch == '\t' || ch == '\v' || ch == '\f' || ch == '\r';
+}
+
+static inline void skip_spaces(void)
+{
+    while (is_space(ch))
+        cinp();
+}
+
+/* parse a string without interpreting escapes */
+static uint8_t *parse_pp_string(uint8_t *p,
+                                int sep, CString *str)
+{
+    int c;
+    p++;
+    for(;;) {
+        c = *p;
+        if (c == sep) {
+            break;
+        } else if (c == '\\') {
+            file->buf_ptr = p;
+            c = handle_eob();
+            p = file->buf_ptr;
+            if (c == CH_EOF) {
+            unterminated_string:
+                /* XXX: indicate line number of start of string */
+                error("missing terminating %c character", sep);
+            } else if (c == '\\') {
+                /* escape : just skip \[\r]\n */
+                PEEKC_EOB(c, p);
+                if (c == '\n') {
+                    file->line_num++;
+                    p++;
+                } else if (c == '\r') {
+                    PEEKC_EOB(c, p);
+                    if (c != '\n')
+                        expect("'\n' after '\r'");
+                    file->line_num++;
+                    p++;
+                } else if (c == CH_EOF) {
+                    goto unterminated_string;
+                } else {
+                    if (str) {
+                        cstr_ccat(str, '\\');
+                        cstr_ccat(str, c);
+                    }
+                    p++;
+                }
+            }
+        } else if (c == '\n') {
+            file->line_num++;
+            goto add_char;
+        } else if (c == '\r') {
+            PEEKC_EOB(c, p);
+            if (c != '\n') {
+                if (str)
+                    cstr_ccat(str, '\r');
+            } else {
+                file->line_num++;
+                goto add_char;
+            }
+        } else {
+        add_char:
+            if (str)
+                cstr_ccat(str, c);
+            p++;
+        }
+    }
+    p++;
+    return p;
+}
+
+/* skip block of text until #else, #elif or #endif. skip also pairs of
+   #if/#endif */
+void preprocess_skip(void)
+{
+    int a, start_of_line, c;
+    uint8_t *p;
+
+    p = file->buf_ptr;
+    start_of_line = 1;
+    a = 0;
+    for(;;) {
+    redo_no_start:
+        c = *p;
+        switch(c) {
+        case ' ':
+        case '\t':
+        case '\f':
+        case '\v':
+        case '\r':
+            p++;
+            goto redo_no_start;
+        case '\n':
+            start_of_line = 1;
+            file->line_num++;
+            p++;
+            goto redo_no_start;
+        case '\\':
+            file->buf_ptr = p;
+            c = handle_eob();
+            if (c == CH_EOF) {
+                expect("#endif");
+            } else if (c == '\\') {
+                /* XXX: incorrect: should not give an error */
+                ch = file->buf_ptr[0];
+                handle_stray();
+            }
+            p = file->buf_ptr;
+            goto redo_no_start;
+            /* skip strings */
+        case '\"':
+        case '\'':
+            p = parse_pp_string(p, c, NULL);
+            break;
+            /* skip comments */
+        case '/':
+            file->buf_ptr = p;
+            ch = *p;
+            minp();
+            p = file->buf_ptr;
+            if (ch == '*') {
+                p = parse_comment(p);
+            } else if (ch == '/') {
+                p = parse_line_comment(p);
+            }
+            break;
+
+        case '#':
+            p++;
+            if (start_of_line) {
+                file->buf_ptr = p;
+                next_nomacro();
+                p = file->buf_ptr;
+                if (a == 0 && 
+                    (tok == TOK_ELSE || tok == TOK_ELIF || tok == TOK_ENDIF))
+                    goto the_end;
+                if (tok == TOK_IF || tok == TOK_IFDEF || tok == TOK_IFNDEF)
+                    a++;
+                else if (tok == TOK_ENDIF)
+                    a--;
+            }
+            break;
+        default:
+            p++;
+            break;
+        }
+        start_of_line = 0;
+    }
+ the_end: ;
+    file->buf_ptr = p;
+}
+
+/* ParseState handling */
+
+/* XXX: currently, no include file info is stored. Thus, we cannot display
+   accurate messages if the function or data definition spans multiple
+   files */
+
+/* save current parse state in 's' */
+void save_parse_state(ParseState *s)
+{
+    s->line_num = file->line_num;
+    s->macro_ptr = macro_ptr;
+    s->tok = tok;
+    s->tokc = tokc;
+}
+
+/* restore parse state from 's' */
+void restore_parse_state(ParseState *s)
+{
+    file->line_num = s->line_num;
+    macro_ptr = s->macro_ptr;
+    tok = s->tok;
+    tokc = s->tokc;
+}
+
+/* return the number of additional 'ints' necessary to store the
+   token */
+static inline int tok_ext_size(int t)
+{
+    switch(t) {
+        /* 4 bytes */
+    case TOK_CINT:
+    case TOK_CUINT:
+    case TOK_CCHAR:
+    case TOK_LCHAR:
+    case TOK_CFLOAT:
+    case TOK_LINENUM:
+        return 1;
+    case TOK_STR:
+    case TOK_LSTR:
+    case TOK_PPNUM:
+        error("unsupported token");
+        return 1;
+    case TOK_CDOUBLE:
+    case TOK_CLLONG:
+    case TOK_CULLONG:
+        return 2;
+    case TOK_CLDOUBLE:
+        return LDOUBLE_SIZE / 4;
+    default:
+        return 0;
+    }
+}
+
+/* token string handling */
+
+static inline void tok_str_new(TokenString *s)
+{
+    s->str = NULL;
+    s->len = 0;
+    s->allocated_len = 0;
+    s->last_line_num = -1;
+}
+
+static void tok_str_free(int *str)
+{
+    tcc_free(str);
+}
+
+static int *tok_str_realloc(TokenString *s)
+{
+    int *str, len;
+
+    if (s->allocated_len == 0) {
+        len = 8;
+    } else {
+        len = s->allocated_len * 2;
+    }
+    str = tcc_realloc(s->str, len * sizeof(int));
+    if (!str)
+        error("memory full");
+    s->allocated_len = len;
+    s->str = str;
+    return str;
+}
+
+static void tok_str_add(TokenString *s, int t)
+{
+    int len, *str;
+
+    len = s->len;
+    str = s->str;
+    if (len >= s->allocated_len)
+        str = tok_str_realloc(s);
+    str[len++] = t;
+    s->len = len;
+}
+
+static void tok_str_add2(TokenString *s, int t, CValue *cv)
+{
+    int len, *str;
+
+    len = s->len;
+    str = s->str;
+
+    /* allocate space for worst case */
+    if (len + TOK_MAX_SIZE > s->allocated_len)
+        str = tok_str_realloc(s);
+    str[len++] = t;
+    switch(t) {
+    case TOK_CINT:
+    case TOK_CUINT:
+    case TOK_CCHAR:
+    case TOK_LCHAR:
+    case TOK_CFLOAT:
+    case TOK_LINENUM:
+        str[len++] = cv->tab[0];
+        break;
+    case TOK_PPNUM:
+    case TOK_STR:
+    case TOK_LSTR:
+        {
+            int nb_words;
+            CString *cstr;
+
+            nb_words = (sizeof(CString) + cv->cstr->size + 3) >> 2;
+            while ((len + nb_words) > s->allocated_len)
+                str = tok_str_realloc(s);
+            cstr = (CString *)(str + len);
+            cstr->data = NULL;
+            cstr->size = cv->cstr->size;
+            cstr->data_allocated = NULL;
+            cstr->size_allocated = cstr->size;
+            memcpy((char *)cstr + sizeof(CString), 
+                   cv->cstr->data, cstr->size);
+            len += nb_words;
+        }
+        break;
+    case TOK_CDOUBLE:
+    case TOK_CLLONG:
+    case TOK_CULLONG:
+#if LDOUBLE_SIZE == 8
+    case TOK_CLDOUBLE:
+#endif
+        str[len++] = cv->tab[0];
+        str[len++] = cv->tab[1];
+        break;
+#if LDOUBLE_SIZE == 12
+    case TOK_CLDOUBLE:
+        str[len++] = cv->tab[0];
+        str[len++] = cv->tab[1];
+        str[len++] = cv->tab[2];
+#elif LDOUBLE_SIZE != 8
+#error add long double size support
+#endif
+        break;
+    default:
+        break;
+    }
+    s->len = len;
+}
+
+/* add the current parse token in token string 's' */
+static void tok_str_add_tok(TokenString *s)
+{
+    CValue cval;
+
+    /* save line number info */
+    if (file->line_num != s->last_line_num) {
+        s->last_line_num = file->line_num;
+        cval.i = s->last_line_num;
+        tok_str_add2(s, TOK_LINENUM, &cval);
+    }
+    tok_str_add2(s, tok, &tokc);
+}
+
+#if LDOUBLE_SIZE == 12
+#define LDOUBLE_GET(p, cv)                      \
+        cv.tab[0] = p[0];                       \
+        cv.tab[1] = p[1];                       \
+        cv.tab[2] = p[2];
+#elif LDOUBLE_SIZE == 8
+#define LDOUBLE_GET(p, cv)                      \
+        cv.tab[0] = p[0];                       \
+        cv.tab[1] = p[1];
+#else
+#error add long double size support
+#endif
+
+
+/* get a token from an integer array and increment pointer
+   accordingly. we code it as a macro to avoid pointer aliasing. */
+#define TOK_GET(t, p, cv)                       \
+{                                               \
+    t = *p++;                                   \
+    switch(t) {                                 \
+    case TOK_CINT:                              \
+    case TOK_CUINT:                             \
+    case TOK_CCHAR:                             \
+    case TOK_LCHAR:                             \
+    case TOK_CFLOAT:                            \
+    case TOK_LINENUM:                           \
+        cv.tab[0] = *p++;                       \
+        break;                                  \
+    case TOK_STR:                               \
+    case TOK_LSTR:                              \
+    case TOK_PPNUM:                             \
+        cv.cstr = (CString *)p;                 \
+        cv.cstr->data = (char *)p + sizeof(CString);\
+        p += (sizeof(CString) + cv.cstr->size + 3) >> 2;\
+        break;                                  \
+    case TOK_CDOUBLE:                           \
+    case TOK_CLLONG:                            \
+    case TOK_CULLONG:                           \
+        cv.tab[0] = p[0];                       \
+        cv.tab[1] = p[1];                       \
+        p += 2;                                 \
+        break;                                  \
+    case TOK_CLDOUBLE:                          \
+        LDOUBLE_GET(p, cv);                     \
+        p += LDOUBLE_SIZE / 4;                  \
+        break;                                  \
+    default:                                    \
+        break;                                  \
+    }                                           \
+}
+
+/* defines handling */
+static inline void define_push(int v, int macro_type, int *str, Sym *first_arg)
+{
+    Sym *s;
+
+    s = sym_push2(&define_stack, v, macro_type, (int)str);
+    s->next = first_arg;
+    table_ident[v - TOK_IDENT]->sym_define = s;
+}
+
+/* undefined a define symbol. Its name is just set to zero */
+static void define_undef(Sym *s)
+{
+    int v;
+    v = s->v;
+    if (v >= TOK_IDENT && v < tok_ident)
+        table_ident[v - TOK_IDENT]->sym_define = NULL;
+    s->v = 0;
+}
+
+static inline Sym *define_find(int v)
+{
+    v -= TOK_IDENT;
+    if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
+        return NULL;
+    return table_ident[v]->sym_define;
+}
+
+/* free define stack until top reaches 'b' */
+static void free_defines(Sym *b)
+{
+    Sym *top, *top1;
+    int v;
+
+    top = define_stack;
+    while (top != b) {
+        top1 = top->prev;
+        /* do not free args or predefined defines */
+        if (top->c)
+            tok_str_free((int *)top->c);
+        v = top->v;
+        if (v >= TOK_IDENT && v < tok_ident)
+            table_ident[v - TOK_IDENT]->sym_define = NULL;
+        sym_free(top);
+        top = top1;
+    }
+    define_stack = b;
+}
+
+/* label lookup */
+static Sym *label_find(int v)
+{
+    v -= TOK_IDENT;
+    if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
+        return NULL;
+    return table_ident[v]->sym_label;
+}
+
+static Sym *label_push(Sym **ptop, int v, int flags)
+{
+    Sym *s, **ps;
+    s = sym_push2(ptop, v, 0, 0);
+    s->r = flags;
+    ps = &table_ident[v - TOK_IDENT]->sym_label;
+    if (ptop == &global_label_stack) {
+        /* modify the top most local identifier, so that
+           sym_identifier will point to 's' when popped */
+        while (*ps != NULL)
+            ps = &(*ps)->prev_tok;
+    }
+    s->prev_tok = *ps;
+    *ps = s;
+    return s;
+}
+
+/* pop labels until element last is reached. Look if any labels are
+   undefined. Define symbols if '&&label' was used. */
+static void label_pop(Sym **ptop, Sym *slast)
+{
+    Sym *s, *s1;
+    for(s = *ptop; s != slast; s = s1) {
+        s1 = s->prev;
+        if (s->r == LABEL_DECLARED) {
+            warning("label '%s' declared but not used", get_tok_str(s->v, NULL));
+        } else if (s->r == LABEL_FORWARD) {
+                error("label '%s' used but not defined",
+                      get_tok_str(s->v, NULL));
+        } else {
+            if (s->c) {
+                /* define corresponding symbol. A size of
+                   1 is put. */
+                put_extern_sym(s, cur_text_section, (long)s->next, 1);
+            }
+        }
+        /* remove label */
+        table_ident[s->v - TOK_IDENT]->sym_label = s->prev_tok;
+        sym_free(s);
+    }
+    *ptop = slast;
+}
+
+/* eval an expression for #if/#elif */
+static int expr_preprocess(void)
+{
+    int c, t;
+    TokenString str;
+    
+    tok_str_new(&str);
+    while (tok != TOK_LINEFEED && tok != TOK_EOF) {
+        next(); /* do macro subst */
+        if (tok == TOK_DEFINED) {
+            next_nomacro();
+            t = tok;
+            if (t == '(') 
+                next_nomacro();
+            c = define_find(tok) != 0;
+            if (t == '(')
+                next_nomacro();
+            tok = TOK_CINT;
+            tokc.i = c;
+        } else if (tok >= TOK_IDENT) {
+            /* if undefined macro */
+            tok = TOK_CINT;
+            tokc.i = 0;
+        }
+        tok_str_add_tok(&str);
+    }
+    tok_str_add(&str, -1); /* simulate end of file */
+    tok_str_add(&str, 0);
+    /* now evaluate C constant expression */
+    macro_ptr = str.str;
+    next();
+    c = expr_const();
+    macro_ptr = NULL;
+    tok_str_free(str.str);
+    return c != 0;
+}
+
+#if defined(PARSE_DEBUG) || defined(PP_DEBUG)
+static void tok_print(int *str)
+{
+    int t;
+    CValue cval;
+
+    while (1) {
+        TOK_GET(t, str, cval);
+        if (!t)
+            break;
+        printf(" %s", get_tok_str(t, &cval));
+    }
+    printf("\n");
+}
+#endif
+
+/* parse after #define */
+static void parse_define(void)
+{
+    Sym *s, *first, **ps;
+    int v, t, varg, is_vaargs, c;
+    TokenString str;
+    
+    v = tok;
+    if (v < TOK_IDENT)
+        error("invalid macro name '%s'", get_tok_str(tok, &tokc));
+    /* XXX: should check if same macro (ANSI) */
+    first = NULL;
+    t = MACRO_OBJ;
+    /* '(' must be just after macro definition for MACRO_FUNC */
+    c = file->buf_ptr[0];
+    if (c == '\\')
+        c = handle_stray1(file->buf_ptr);
+    if (c == '(') {
+        next_nomacro();
+        next_nomacro();
+        ps = &first;
+        while (tok != ')') {
+            varg = tok;
+            next_nomacro();
+            is_vaargs = 0;
+            if (varg == TOK_DOTS) {
+                varg = TOK___VA_ARGS__;
+                is_vaargs = 1;
+            } else if (tok == TOK_DOTS && gnu_ext) {
+                is_vaargs = 1;
+                next_nomacro();
+            }
+            if (varg < TOK_IDENT)
+                error("badly punctuated parameter list");
+            s = sym_push2(&define_stack, varg | SYM_FIELD, is_vaargs, 0);
+            *ps = s;
+            ps = &s->next;
+            if (tok != ',')
+                break;
+            next_nomacro();
+        }
+        t = MACRO_FUNC;
+    }
+    tok_str_new(&str);
+    next_nomacro();
+    /* EOF testing necessary for '-D' handling */
+    while (tok != TOK_LINEFEED && tok != TOK_EOF) {
+        tok_str_add2(&str, tok, &tokc);
+        next_nomacro();
+    }
+    tok_str_add(&str, 0);
+#ifdef PP_DEBUG
+    printf("define %s %d: ", get_tok_str(v, NULL), t);
+    tok_print(str.str);
+#endif
+    define_push(v, t, str.str, first);
+}
+
+static inline int hash_cached_include(int type, const char *filename)
+{
+    const unsigned char *s;
+    unsigned int h;
+
+    h = TOK_HASH_INIT;
+    h = TOK_HASH_FUNC(h, type);
+    s = filename;
+    while (*s) {
+        h = TOK_HASH_FUNC(h, *s);
+        s++;
+    }
+    h &= (CACHED_INCLUDES_HASH_SIZE - 1);
+    return h;
+}
+
+/* XXX: use a token or a hash table to accelerate matching ? */
+static CachedInclude *search_cached_include(TCCState *s1,
+                                            int type, const char *filename)
+{
+    CachedInclude *e;
+    int i, h;
+    h = hash_cached_include(type, filename);
+    i = s1->cached_includes_hash[h];
+    for(;;) {
+        if (i == 0)
+            break;
+        e = s1->cached_includes[i - 1];
+        if (e->type == type && !strcmp(e->filename, filename))
+            return e;
+        i = e->hash_next;
+    }
+    return NULL;
+}
+
+static inline void add_cached_include(TCCState *s1, int type, 
+                                      const char *filename, int ifndef_macro)
+{
+    CachedInclude *e;
+    int h;
+
+    if (search_cached_include(s1, type, filename))
+        return;
+#ifdef INC_DEBUG
+    printf("adding cached '%s' %s\n", filename, get_tok_str(ifndef_macro, NULL));
+#endif
+    e = tcc_malloc(sizeof(CachedInclude) + strlen(filename));
+    if (!e)
+        return;
+    e->type = type;
+    strcpy(e->filename, filename);
+    e->ifndef_macro = ifndef_macro;
+    dynarray_add((void ***)&s1->cached_includes, &s1->nb_cached_includes, e);
+    /* add in hash table */
+    h = hash_cached_include(type, filename);
+    e->hash_next = s1->cached_includes_hash[h];
+    s1->cached_includes_hash[h] = s1->nb_cached_includes;
+}
+
+static void pragma_parse(TCCState *s1)
+{
+    int val;
+
+    next();
+    if (tok == TOK_pack) {
+        /*
+          This may be:
+          #pragma pack(1) // set
+          #pragma pack() // reset to default
+          #pragma pack(push,1) // push & set
+          #pragma pack(pop) // restore previous
+        */
+        next();
+        skip('(');
+        if (tok == TOK_ASM_pop) {
+            next();
+            if (s1->pack_stack_ptr <= s1->pack_stack) {
+            stk_error:
+                error("out of pack stack");
+            }
+            s1->pack_stack_ptr--;
+        } else {
+            val = 0;
+            if (tok != ')') {
+                if (tok == TOK_ASM_push) {
+                    next();
+                    if (s1->pack_stack_ptr >= s1->pack_stack + PACK_STACK_SIZE - 1)
+                        goto stk_error;
+                    s1->pack_stack_ptr++;
+                    skip(',');
+                }
+                if (tok != TOK_CINT) {
+                pack_error:
+                    error("invalid pack pragma");
+                }
+                val = tokc.i;
+                if (val < 1 || val > 16 || (val & (val - 1)) != 0)
+                    goto pack_error;
+                next();
+            }
+            *s1->pack_stack_ptr = val;
+            skip(')');
+        }
+    }
+}
+
+/* is_bof is true if first non space token at beginning of file */
+static void preprocess(int is_bof)
+{
+    TCCState *s1 = tcc_state;
+    int size, i, c, n, saved_parse_flags;
+    char buf[1024], *q, *p;
+    char buf1[1024];
+    BufferedFile *f;
+    Sym *s;
+    CachedInclude *e;
+    
+    saved_parse_flags = parse_flags;
+    parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM | 
+        PARSE_FLAG_LINEFEED;
+    next_nomacro();
+ redo:
+    switch(tok) {
+    case TOK_DEFINE:
+        next_nomacro();
+        parse_define();
+        break;
+    case TOK_UNDEF:
+        next_nomacro();
+        s = define_find(tok);
+        /* undefine symbol by putting an invalid name */
+        if (s)
+            define_undef(s);
+        break;
+    case TOK_INCLUDE:
+    case TOK_INCLUDE_NEXT:
+        ch = file->buf_ptr[0];
+        /* XXX: incorrect if comments : use next_nomacro with a special mode */
+        skip_spaces();
+        if (ch == '<') {
+            c = '>';
+            goto read_name;
+        } else if (ch == '\"') {
+            c = ch;
+        read_name:
+            /* XXX: better stray handling */
+            minp();
+            q = buf;
+            while (ch != c && ch != '\n' && ch != CH_EOF) {
+                if ((q - buf) < sizeof(buf) - 1)
+                    *q++ = ch;
+                minp();
+            }
+            *q = '\0';
+            minp();
+#if 0
+            /* eat all spaces and comments after include */
+            /* XXX: slightly incorrect */
+            while (ch1 != '\n' && ch1 != CH_EOF)
+                inp();
+#endif
+        } else {
+            /* computed #include : either we have only strings or
+               we have anything enclosed in '<>' */
+            next();
+            buf[0] = '\0';
+            if (tok == TOK_STR) {
+                while (tok != TOK_LINEFEED) {
+                    if (tok != TOK_STR) {
+                    include_syntax:
+                        error("'#include' expects \"FILENAME\" or <FILENAME>");
+                    }
+                    pstrcat(buf, sizeof(buf), (char *)tokc.cstr->data);
+                    next();
+                }
+                c = '\"';
+            } else {
+                int len;
+                while (tok != TOK_LINEFEED) {
+                    pstrcat(buf, sizeof(buf), get_tok_str(tok, &tokc));
+                    next();
+                }
+                len = strlen(buf);
+                /* check syntax and remove '<>' */
+                if (len < 2 || buf[0] != '<' || buf[len - 1] != '>')
+                    goto include_syntax;
+                memmove(buf, buf + 1, len - 2);
+                buf[len - 2] = '\0';
+                c = '>';
+            }
+        }
+
+        e = search_cached_include(s1, c, buf);
+        if (e && define_find(e->ifndef_macro)) {
+            /* no need to parse the include because the 'ifndef macro'
+               is defined */
+#ifdef INC_DEBUG
+            printf("%s: skipping %s\n", file->filename, buf);
+#endif
+        } else {
+            if (c == '\"') {
+                /* first search in current dir if "header.h" */
+                size = 0;
+                p = strrchr(file->filename, '/');
+                if (p) 
+                    size = p + 1 - file->filename;
+                if (size > sizeof(buf1) - 1)
+                    size = sizeof(buf1) - 1;
+                memcpy(buf1, file->filename, size);
+                buf1[size] = '\0';
+                pstrcat(buf1, sizeof(buf1), buf);
+                f = tcc_open(s1, buf1);
+                if (f) {
+                    if (tok == TOK_INCLUDE_NEXT)
+                        tok = TOK_INCLUDE;
+                    else
+                        goto found;
+                }
+            }
+            if (s1->include_stack_ptr >= s1->include_stack + INCLUDE_STACK_SIZE)
+                error("#include recursion too deep");
+            /* now search in all the include paths */
+            n = s1->nb_include_paths + s1->nb_sysinclude_paths;
+            for(i = 0; i < n; i++) {
+                const char *path;
+                if (i < s1->nb_include_paths)
+                    path = s1->include_paths[i];
+                else
+                    path = s1->sysinclude_paths[i - s1->nb_include_paths];
+                pstrcpy(buf1, sizeof(buf1), path);
+                pstrcat(buf1, sizeof(buf1), "/");
+                pstrcat(buf1, sizeof(buf1), buf);
+                f = tcc_open(s1, buf1);
+                if (f) {
+                    if (tok == TOK_INCLUDE_NEXT)
+                        tok = TOK_INCLUDE;
+                    else
+                        goto found;
+                }
+            }
+            error("include file '%s' not found", buf);
+            f = NULL;
+        found:
+#ifdef INC_DEBUG
+            printf("%s: including %s\n", file->filename, buf1);
+#endif
+            f->inc_type = c;
+            pstrcpy(f->inc_filename, sizeof(f->inc_filename), buf);
+            /* push current file in stack */
+            /* XXX: fix current line init */
+            *s1->include_stack_ptr++ = file;
+            file = f;
+            /* add include file debug info */
+            if (do_debug) {
+                put_stabs(file->filename, N_BINCL, 0, 0, 0);
+            }
+            tok_flags |= TOK_FLAG_BOF | TOK_FLAG_BOL;
+            ch = file->buf_ptr[0];
+            goto the_end;
+        }
+        break;
+    case TOK_IFNDEF:
+        c = 1;
+        goto do_ifdef;
+    case TOK_IF:
+        c = expr_preprocess();
+        goto do_if;
+    case TOK_IFDEF:
+        c = 0;
+    do_ifdef:
+        next_nomacro();
+        if (tok < TOK_IDENT)
+            error("invalid argument for '#if%sdef'", c ? "n" : "");
+        if (is_bof) {
+            if (c) {
+#ifdef INC_DEBUG
+                printf("#ifndef %s\n", get_tok_str(tok, NULL));
+#endif
+                file->ifndef_macro = tok;
+            }
+        }
+        c = (define_find(tok) != 0) ^ c;
+    do_if:
+        if (s1->ifdef_stack_ptr >= s1->ifdef_stack + IFDEF_STACK_SIZE)
+            error("memory full");
+        *s1->ifdef_stack_ptr++ = c;
+        goto test_skip;
+    case TOK_ELSE:
+        if (s1->ifdef_stack_ptr == s1->ifdef_stack)
+            error("#else without matching #if");
+        if (s1->ifdef_stack_ptr[-1] & 2)
+            error("#else after #else");
+        c = (s1->ifdef_stack_ptr[-1] ^= 3);
+        goto test_skip;
+    case TOK_ELIF:
+        if (s1->ifdef_stack_ptr == s1->ifdef_stack)
+            error("#elif without matching #if");
+        c = s1->ifdef_stack_ptr[-1];
+        if (c > 1)
+            error("#elif after #else");
+        /* last #if/#elif expression was true: we skip */
+        if (c == 1)
+            goto skip;
+        c = expr_preprocess();
+        s1->ifdef_stack_ptr[-1] = c;
+    test_skip:
+        if (!(c & 1)) {
+        skip:
+            preprocess_skip();
+            is_bof = 0;
+            goto redo;
+        }
+        break;
+    case TOK_ENDIF:
+        if (s1->ifdef_stack_ptr <= file->ifdef_stack_ptr)
+            error("#endif without matching #if");
+        s1->ifdef_stack_ptr--;
+        /* '#ifndef macro' was at the start of file. Now we check if
+           an '#endif' is exactly at the end of file */
+        if (file->ifndef_macro &&
+            s1->ifdef_stack_ptr == file->ifdef_stack_ptr) {
+            file->ifndef_macro_saved = file->ifndef_macro;
+            /* need to set to zero to avoid false matches if another
+               #ifndef at middle of file */
+            file->ifndef_macro = 0;
+            while (tok != TOK_LINEFEED)
+                next_nomacro();
+            tok_flags |= TOK_FLAG_ENDIF;
+            goto the_end;
+        }
+        break;
+    case TOK_LINE:
+        next();
+        if (tok != TOK_CINT)
+            error("#line");
+        file->line_num = tokc.i - 1; /* the line number will be incremented after */
+        next();
+        if (tok != TOK_LINEFEED) {
+            if (tok != TOK_STR)
+                error("#line");
+            pstrcpy(file->filename, sizeof(file->filename), 
+                    (char *)tokc.cstr->data);
+        }
+        break;
+    case TOK_ERROR:
+    case TOK_WARNING:
+        c = tok;
+        ch = file->buf_ptr[0];
+        skip_spaces();
+        q = buf;
+        while (ch != '\n' && ch != CH_EOF) {
+            if ((q - buf) < sizeof(buf) - 1)
+                *q++ = ch;
+            minp();
+        }
+        *q = '\0';
+        if (c == TOK_ERROR)
+            error("#error %s", buf);
+        else
+            warning("#warning %s", buf);
+        break;
+    case TOK_PRAGMA:
+        pragma_parse(s1);
+        break;
+    default:
+        if (tok == TOK_LINEFEED || tok == '!' || tok == TOK_CINT) {
+            /* '!' is ignored to allow C scripts. numbers are ignored
+               to emulate cpp behaviour */
+        } else {
+            if (!(saved_parse_flags & PARSE_FLAG_ASM_COMMENTS))
+                error("invalid preprocessing directive #%s", get_tok_str(tok, &tokc));
+        }
+        break;
+    }
+    /* ignore other preprocess commands or #! for C scripts */
+    while (tok != TOK_LINEFEED)
+        next_nomacro();
+ the_end:
+    parse_flags = saved_parse_flags;
+}
+
+/* evaluate escape codes in a string. */
+static void parse_escape_string(CString *outstr, const uint8_t *buf, int is_long)
+{
+    int c, n;
+    const uint8_t *p;
+
+    p = buf;
+    for(;;) {
+        c = *p;
+        if (c == '\0')
+            break;
+        if (c == '\\') {
+            p++;
+            /* escape */
+            c = *p;
+            switch(c) {
+            case '0': case '1': case '2': case '3':
+            case '4': case '5': case '6': case '7':
+                /* at most three octal digits */
+                n = c - '0';
+                p++;
+                c = *p;
+                if (isoct(c)) {
+                    n = n * 8 + c - '0';
+                    p++;
+                    c = *p;
+                    if (isoct(c)) {
+                        n = n * 8 + c - '0';
+                        p++;
+                    }
+                }
+                c = n;
+                goto add_char_nonext;
+            case 'x':
+                p++;
+                n = 0;
+                for(;;) {
+                    c = *p;
+                    if (c >= 'a' && c <= 'f')
+                        c = c - 'a' + 10;
+                    else if (c >= 'A' && c <= 'F')
+                        c = c - 'A' + 10;
+                    else if (isnum(c))
+                        c = c - '0';
+                    else
+                        break;
+                    n = n * 16 + c;
+                    p++;
+                }
+                c = n;
+                goto add_char_nonext;
+            case 'a':
+                c = '\a';
+                break;
+            case 'b':
+                c = '\b';
+                break;
+            case 'f':
+                c = '\f';
+                break;
+            case 'n':
+                c = '\n';
+                break;
+            case 'r':
+                c = '\r';
+                break;
+            case 't':
+                c = '\t';
+                break;
+            case 'v':
+                c = '\v';
+                break;
+            case 'e':
+                if (!gnu_ext)
+                    goto invalid_escape;
+                c = 27;
+                break;
+            case '\'':
+            case '\"':
+            case '\\': 
+            case '?':
+                break;
+            default:
+            invalid_escape:
+                if (c >= '!' && c <= '~')
+                    warning("unknown escape sequence: \'\\%c\'", c);
+                else
+                    warning("unknown escape sequence: \'\\x%x\'", c);
+                break;
+            }
+        }
+        p++;
+    add_char_nonext:
+        if (!is_long)
+            cstr_ccat(outstr, c);
+        else
+            cstr_wccat(outstr, c);
+    }
+    /* add a trailing '\0' */
+    if (!is_long)
+        cstr_ccat(outstr, '\0');
+    else
+        cstr_wccat(outstr, '\0');
+}
+
+/* we use 64 bit numbers */
+#define BN_SIZE 2
+
+/* bn = (bn << shift) | or_val */
+void bn_lshift(unsigned int *bn, int shift, int or_val)
+{
+    int i;
+    unsigned int v;
+    for(i=0;i<BN_SIZE;i++) {
+        v = bn[i];
+        bn[i] = (v << shift) | or_val;
+        or_val = v >> (32 - shift);
+    }
+}
+
+void bn_zero(unsigned int *bn)
+{
+    int i;
+    for(i=0;i<BN_SIZE;i++) {
+        bn[i] = 0;
+    }
+}
+
+/* parse number in null terminated string 'p' and return it in the
+   current token */
+void parse_number(const char *p)
+{
+    int b, t, shift, frac_bits, s, exp_val, ch;
+    char *q;
+    unsigned int bn[BN_SIZE];
+    double d;
+
+    /* number */
+    q = token_buf;
+    ch = *p++;
+    t = ch;
+    ch = *p++;
+    *q++ = t;
+    b = 10;
+    if (t == '.') {
+        goto float_frac_parse;
+    } else if (t == '0') {
+        if (ch == 'x' || ch == 'X') {
+            q--;
+            ch = *p++;
+            b = 16;
+        } else if (tcc_ext && (ch == 'b' || ch == 'B')) {
+            q--;
+            ch = *p++;
+            b = 2;
+        }
+    }
+    /* parse all digits. cannot check octal numbers at this stage
+       because of floating point constants */
+    while (1) {
+        if (ch >= 'a' && ch <= 'f')
+            t = ch - 'a' + 10;
+        else if (ch >= 'A' && ch <= 'F')
+            t = ch - 'A' + 10;
+        else if (isnum(ch))
+            t = ch - '0';
+        else
+            break;
+        if (t >= b)
+            break;
+        if (q >= token_buf + STRING_MAX_SIZE) {
+        num_too_long:
+            error("number too long");
+        }
+        *q++ = ch;
+        ch = *p++;
+    }
+    if (ch == '.' ||
+        ((ch == 'e' || ch == 'E') && b == 10) ||
+        ((ch == 'p' || ch == 'P') && (b == 16 || b == 2))) {
+        if (b != 10) {
+            /* NOTE: strtox should support that for hexa numbers, but
+               non ISOC99 libcs do not support it, so we prefer to do
+               it by hand */
+            /* hexadecimal or binary floats */
+            /* XXX: handle overflows */
+            *q = '\0';
+            if (b == 16)
+                shift = 4;
+            else 
+                shift = 2;
+            bn_zero(bn);
+            q = token_buf;
+            while (1) {
+                t = *q++;
+                if (t == '\0') {
+                    break;
+                } else if (t >= 'a') {
+                    t = t - 'a' + 10;
+                } else if (t >= 'A') {
+                    t = t - 'A' + 10;
+                } else {
+                    t = t - '0';
+                }
+                bn_lshift(bn, shift, t);
+            }
+            frac_bits = 0;
+            if (ch == '.') {
+                ch = *p++;
+                while (1) {
+                    t = ch;
+                    if (t >= 'a' && t <= 'f') {
+                        t = t - 'a' + 10;
+                    } else if (t >= 'A' && t <= 'F') {
+                        t = t - 'A' + 10;
+                    } else if (t >= '0' && t <= '9') {
+                        t = t - '0';
+                    } else {
+                        break;
+                    }
+                    if (t >= b)
+                        error("invalid digit");
+                    bn_lshift(bn, shift, t);
+                    frac_bits += shift;
+                    ch = *p++;
+                }
+            }
+            if (ch != 'p' && ch != 'P')
+                expect("exponent");
+            ch = *p++;
+            s = 1;
+            exp_val = 0;
+            if (ch == '+') {
+                ch = *p++;
+            } else if (ch == '-') {
+                s = -1;
+                ch = *p++;
+            }
+            if (ch < '0' || ch > '9')
+                expect("exponent digits");
+            while (ch >= '0' && ch <= '9') {
+                exp_val = exp_val * 10 + ch - '0';
+                ch = *p++;
+            }
+            exp_val = exp_val * s;
+            
+            /* now we can generate the number */
+            /* XXX: should patch directly float number */
+            d = (double)bn[1] * 4294967296.0 + (double)bn[0];
+            d = ldexp(d, exp_val - frac_bits);
+            t = toup(ch);
+            if (t == 'F') {
+                ch = *p++;
+                tok = TOK_CFLOAT;
+                /* float : should handle overflow */
+                tokc.f = (float)d;
+            } else if (t == 'L') {
+                ch = *p++;
+                tok = TOK_CLDOUBLE;
+                /* XXX: not large enough */
+                tokc.ld = (long double)d;
+            } else {
+                tok = TOK_CDOUBLE;
+                tokc.d = d;
+            }
+        } else {
+            /* decimal floats */
+            if (ch == '.') {
+                if (q >= token_buf + STRING_MAX_SIZE)
+                    goto num_too_long;
+                *q++ = ch;
+                ch = *p++;
+            float_frac_parse:
+                while (ch >= '0' && ch <= '9') {
+                    if (q >= token_buf + STRING_MAX_SIZE)
+                        goto num_too_long;
+                    *q++ = ch;
+                    ch = *p++;
+                }
+            }
+            if (ch == 'e' || ch == 'E') {
+                if (q >= token_buf + STRING_MAX_SIZE)
+                    goto num_too_long;
+                *q++ = ch;
+                ch = *p++;
+                if (ch == '-' || ch == '+') {
+                    if (q >= token_buf + STRING_MAX_SIZE)
+                        goto num_too_long;
+                    *q++ = ch;
+                    ch = *p++;
+                }
+                if (ch < '0' || ch > '9')
+                    expect("exponent digits");
+                while (ch >= '0' && ch <= '9') {
+                    if (q >= token_buf + STRING_MAX_SIZE)
+                        goto num_too_long;
+                    *q++ = ch;
+                    ch = *p++;
+                }
+            }
+            *q = '\0';
+            t = toup(ch);
+            errno = 0;
+            if (t == 'F') {
+                ch = *p++;
+                tok = TOK_CFLOAT;
+                tokc.f = strtof(token_buf, NULL);
+            } else if (t == 'L') {
+                ch = *p++;
+                tok = TOK_CLDOUBLE;
+                tokc.ld = strtold(token_buf, NULL);
+            } else {
+                tok = TOK_CDOUBLE;
+                tokc.d = strtod(token_buf, NULL);
+            }
+        }
+    } else {
+        unsigned long long n, n1;
+        int lcount, ucount;
+
+        /* integer number */
+        *q = '\0';
+        q = token_buf;
+        if (b == 10 && *q == '0') {
+            b = 8;
+            q++;
+        }
+        n = 0;
+        while(1) {
+            t = *q++;
+            /* no need for checks except for base 10 / 8 errors */
+            if (t == '\0') {
+                break;
+            } else if (t >= 'a') {
+                t = t - 'a' + 10;
+            } else if (t >= 'A') {
+                t = t - 'A' + 10;
+            } else {
+                t = t - '0';
+                if (t >= b)
+                    error("invalid digit");
+            }
+            n1 = n;
+            n = n * b + t;
+            /* detect overflow */
+            /* XXX: this test is not reliable */
+            if (n < n1)
+                error("integer constant overflow");
+        }
+        
+        /* XXX: not exactly ANSI compliant */
+        if ((n & 0xffffffff00000000LL) != 0) {
+            if ((n >> 63) != 0)
+                tok = TOK_CULLONG;
+            else
+                tok = TOK_CLLONG;
+        } else if (n > 0x7fffffff) {
+            tok = TOK_CUINT;
+        } else {
+            tok = TOK_CINT;
+        }
+        lcount = 0;
+        ucount = 0;
+        for(;;) {
+            t = toup(ch);
+            if (t == 'L') {
+                if (lcount >= 2)
+                    error("three 'l's in integer constant");
+                lcount++;
+                if (lcount == 2) {
+                    if (tok == TOK_CINT)
+                        tok = TOK_CLLONG;
+                    else if (tok == TOK_CUINT)
+                        tok = TOK_CULLONG;
+                }
+                ch = *p++;
+            } else if (t == 'U') {
+                if (ucount >= 1)
+                    error("two 'u's in integer constant");
+                ucount++;
+                if (tok == TOK_CINT)
+                    tok = TOK_CUINT;
+                else if (tok == TOK_CLLONG)
+                    tok = TOK_CULLONG;
+                ch = *p++;
+            } else {
+                break;
+            }
+        }
+        if (tok == TOK_CINT || tok == TOK_CUINT)
+            tokc.ui = n;
+        else
+            tokc.ull = n;
+    }
+}
+
+
+#define PARSE2(c1, tok1, c2, tok2)              \
+    case c1:                                    \
+        PEEKC(c, p);                            \
+        if (c == c2) {                          \
+            p++;                                \
+            tok = tok2;                         \
+        } else {                                \
+            tok = tok1;                         \
+        }                                       \
+        break;
+
+/* return next token without macro substitution */
+static inline void next_nomacro1(void)
+{
+    int t, c, is_long;
+    TokenSym *ts;
+    uint8_t *p, *p1;
+    unsigned int h;
+
+    p = file->buf_ptr;
+ redo_no_start:
+    c = *p;
+    switch(c) {
+    case ' ':
+    case '\t':
+    case '\f':
+    case '\v':
+    case '\r':
+        p++;
+        goto redo_no_start;
+        
+    case '\\':
+        /* first look if it is in fact an end of buffer */
+        if (p >= file->buf_end) {
+            file->buf_ptr = p;
+            handle_eob();
+            p = file->buf_ptr;
+            if (p >= file->buf_end)
+                goto parse_eof;
+            else
+                goto redo_no_start;
+        } else {
+            file->buf_ptr = p;
+            ch = *p;
+            handle_stray();
+            p = file->buf_ptr;
+            goto redo_no_start;
+        }
+    parse_eof:
+        {
+            TCCState *s1 = tcc_state;
+            if (parse_flags & PARSE_FLAG_LINEFEED) {
+                tok = TOK_LINEFEED;
+            } else if (s1->include_stack_ptr == s1->include_stack ||
+                       !(parse_flags & PARSE_FLAG_PREPROCESS)) {
+                /* no include left : end of file. */
+                tok = TOK_EOF;
+            } else {
+                /* pop include file */
+                
+                /* test if previous '#endif' was after a #ifdef at
+                   start of file */
+                if (tok_flags & TOK_FLAG_ENDIF) {
+#ifdef INC_DEBUG
+                    printf("#endif %s\n", get_tok_str(file->ifndef_macro_saved, NULL));
+#endif
+                    add_cached_include(s1, file->inc_type, file->inc_filename,
+                                       file->ifndef_macro_saved);
+                }
+
+                /* add end of include file debug info */
+                if (do_debug) {
+                    put_stabd(N_EINCL, 0, 0);
+                }
+                /* pop include stack */
+                tcc_close(file);
+                s1->include_stack_ptr--;
+                file = *s1->include_stack_ptr;
+                p = file->buf_ptr;
+                goto redo_no_start;
+            }
+        }
+        break;
+
+    case '\n':
+        if (parse_flags & PARSE_FLAG_LINEFEED) {
+            tok = TOK_LINEFEED;
+        } else {
+            file->line_num++;
+            tok_flags |= TOK_FLAG_BOL;
+            p++;
+            goto redo_no_start;
+        }
+        break;
+
+    case '#':
+        /* XXX: simplify */
+        PEEKC(c, p);
+        if ((tok_flags & TOK_FLAG_BOL) && 
+            (parse_flags & PARSE_FLAG_PREPROCESS)) {
+            file->buf_ptr = p;
+            preprocess(tok_flags & TOK_FLAG_BOF);
+            p = file->buf_ptr;
+            goto redo_no_start;
+        } else {
+            if (c == '#') {
+                p++;
+                tok = TOK_TWOSHARPS;
+            } else {
+                if (parse_flags & PARSE_FLAG_ASM_COMMENTS) {
+                    p = parse_line_comment(p - 1);
+                    goto redo_no_start;
+                } else {
+                    tok = '#';
+                }
+            }
+        }
+        break;
+
+    case 'a': case 'b': case 'c': case 'd':
+    case 'e': case 'f': case 'g': case 'h':
+    case 'i': case 'j': case 'k': case 'l':
+    case 'm': case 'n': case 'o': case 'p':
+    case 'q': case 'r': case 's': case 't':
+    case 'u': case 'v': case 'w': case 'x':
+    case 'y': case 'z': 
+    case 'A': case 'B': case 'C': case 'D':
+    case 'E': case 'F': case 'G': case 'H':
+    case 'I': case 'J': case 'K': 
+    case 'M': case 'N': case 'O': case 'P':
+    case 'Q': case 'R': case 'S': case 'T':
+    case 'U': case 'V': case 'W': case 'X':
+    case 'Y': case 'Z': 
+    case '_':
+    parse_ident_fast:
+        p1 = p;
+        h = TOK_HASH_INIT;
+        h = TOK_HASH_FUNC(h, c);
+        p++;
+        for(;;) {
+            c = *p;
+            if (!isidnum_table[c])
+                break;
+            h = TOK_HASH_FUNC(h, c);
+            p++;
+        }
+        if (c != '\\') {
+            TokenSym **pts;
+            int len;
+
+            /* fast case : no stray found, so we have the full token
+               and we have already hashed it */
+            len = p - p1;
+            h &= (TOK_HASH_SIZE - 1);
+            pts = &hash_ident[h];
+            for(;;) {
+                ts = *pts;
+                if (!ts)
+                    break;
+                if (ts->len == len && !memcmp(ts->str, p1, len))
+                    goto token_found;
+                pts = &(ts->hash_next);
+            }
+            ts = tok_alloc_new(pts, p1, len);
+        token_found: ;
+        } else {
+            /* slower case */
+            cstr_reset(&tokcstr);
+
+            while (p1 < p) {
+                cstr_ccat(&tokcstr, *p1);
+                p1++;
+            }
+            p--;
+            PEEKC(c, p);
+        parse_ident_slow:
+            while (isidnum_table[c]) {
+                cstr_ccat(&tokcstr, c);
+                PEEKC(c, p);
+            }
+            ts = tok_alloc(tokcstr.data, tokcstr.size);
+        }
+        tok = ts->tok;
+        break;
+    case 'L':
+        t = p[1];
+        if (t != '\\' && t != '\'' && t != '\"') {
+            /* fast case */
+            goto parse_ident_fast;
+        } else {
+            PEEKC(c, p);
+            if (c == '\'' || c == '\"') {
+                is_long = 1;
+                goto str_const;
+            } else {
+                cstr_reset(&tokcstr);
+                cstr_ccat(&tokcstr, 'L');
+                goto parse_ident_slow;
+            }
+        }
+        break;
+    case '0': case '1': case '2': case '3':
+    case '4': case '5': case '6': case '7':
+    case '8': case '9':
+
+        cstr_reset(&tokcstr);
+        /* after the first digit, accept digits, alpha, '.' or sign if
+           prefixed by 'eEpP' */
+    parse_num:
+        for(;;) {
+            t = c;
+            cstr_ccat(&tokcstr, c);
+            PEEKC(c, p);
+            if (!(isnum(c) || isid(c) || c == '.' ||
+                  ((c == '+' || c == '-') && 
+                   (t == 'e' || t == 'E' || t == 'p' || t == 'P'))))
+                break;
+        }
+        /* We add a trailing '\0' to ease parsing */
+        cstr_ccat(&tokcstr, '\0');
+        tokc.cstr = &tokcstr;
+        tok = TOK_PPNUM;
+        break;
+    case '.':
+        /* special dot handling because it can also start a number */
+        PEEKC(c, p);
+        if (isnum(c)) {
+            cstr_reset(&tokcstr);
+            cstr_ccat(&tokcstr, '.');
+            goto parse_num;
+        } else if (c == '.') {
+            PEEKC(c, p);
+            if (c != '.')
+                expect("'.'");
+            PEEKC(c, p);
+            tok = TOK_DOTS;
+        } else {
+            tok = '.';
+        }
+        break;
+    case '\'':
+    case '\"':
+        is_long = 0;
+    str_const:
+        {
+            CString str;
+            int sep;
+
+            sep = c;
+
+            /* parse the string */
+            cstr_new(&str);
+            p = parse_pp_string(p, sep, &str);
+            cstr_ccat(&str, '\0');
+            
+            /* eval the escape (should be done as TOK_PPNUM) */
+            cstr_reset(&tokcstr);
+            parse_escape_string(&tokcstr, str.data, is_long);
+            cstr_free(&str);
+
+            if (sep == '\'') {
+                int char_size;
+                /* XXX: make it portable */
+                if (!is_long)
+                    char_size = 1;
+                else
+                    char_size = sizeof(int);
+                if (tokcstr.size <= char_size)
+                    error("empty character constant");
+                if (tokcstr.size > 2 * char_size)
+                    warning("multi-character character constant");
+                if (!is_long) {
+                    tokc.i = *(int8_t *)tokcstr.data;
+                    tok = TOK_CCHAR;
+                } else {
+                    tokc.i = *(int *)tokcstr.data;
+                    tok = TOK_LCHAR;
+                }
+            } else {
+                tokc.cstr = &tokcstr;
+                if (!is_long)
+                    tok = TOK_STR;
+                else
+                    tok = TOK_LSTR;
+            }
+        }
+        break;
+
+    case '<':
+        PEEKC(c, p);
+        if (c == '=') {
+            p++;
+            tok = TOK_LE;
+        } else if (c == '<') {
+            PEEKC(c, p);
+            if (c == '=') {
+                p++;
+                tok = TOK_A_SHL;
+            } else {
+                tok = TOK_SHL;
+            }
+        } else {
+            tok = TOK_LT;
+        }
+        break;
+        
+    case '>':
+        PEEKC(c, p);
+        if (c == '=') {
+            p++;
+            tok = TOK_GE;
+        } else if (c == '>') {
+            PEEKC(c, p);
+            if (c == '=') {
+                p++;
+                tok = TOK_A_SAR;
+            } else {
+                tok = TOK_SAR;
+            }
+        } else {
+            tok = TOK_GT;
+        }
+        break;
+        
+    case '&':
+        PEEKC(c, p);
+        if (c == '&') {
+            p++;
+            tok = TOK_LAND;
+        } else if (c == '=') {
+            p++;
+            tok = TOK_A_AND;
+        } else {
+            tok = '&';
+        }
+        break;
+        
+    case '|':
+        PEEKC(c, p);
+        if (c == '|') {
+            p++;
+            tok = TOK_LOR;
+        } else if (c == '=') {
+            p++;
+            tok = TOK_A_OR;
+        } else {
+            tok = '|';
+        }
+        break;
+
+    case '+':
+        PEEKC(c, p);
+        if (c == '+') {
+            p++;
+            tok = TOK_INC;
+        } else if (c == '=') {
+            p++;
+            tok = TOK_A_ADD;
+        } else {
+            tok = '+';
+        }
+        break;
+        
+    case '-':
+        PEEKC(c, p);
+        if (c == '-') {
+            p++;
+            tok = TOK_DEC;
+        } else if (c == '=') {
+            p++;
+            tok = TOK_A_SUB;
+        } else if (c == '>') {
+            p++;
+            tok = TOK_ARROW;
+        } else {
+            tok = '-';
+        }
+        break;
+
+    PARSE2('!', '!', '=', TOK_NE)
+    PARSE2('=', '=', '=', TOK_EQ)
+    PARSE2('*', '*', '=', TOK_A_MUL)
+    PARSE2('%', '%', '=', TOK_A_MOD)
+    PARSE2('^', '^', '=', TOK_A_XOR)
+        
+        /* comments or operator */
+    case '/':
+        PEEKC(c, p);
+        if (c == '*') {
+            p = parse_comment(p);
+            goto redo_no_start;
+        } else if (c == '/') {
+            p = parse_line_comment(p);
+            goto redo_no_start;
+        } else if (c == '=') {
+            p++;
+            tok = TOK_A_DIV;
+        } else {
+            tok = '/';
+        }
+        break;
+        
+        /* simple tokens */
+    case '(':
+    case ')':
+    case '[':
+    case ']':
+    case '{':
+    case '}':
+    case ',':
+    case ';':
+    case ':':
+    case '?':
+    case '~':
+    case '$': /* only used in assembler */
+    case '@': /* dito */
+        tok = c;
+        p++;
+        break;
+    default:
+        error("unrecognized character \\x%02x", c);
+        break;
+    }
+    file->buf_ptr = p;
+    tok_flags = 0;
+#if defined(PARSE_DEBUG)
+    printf("token = %s\n", get_tok_str(tok, &tokc));
+#endif
+}
+
+/* return next token without macro substitution. Can read input from
+   macro_ptr buffer */
+static void next_nomacro(void)
+{
+    if (macro_ptr) {
+    redo:
+        tok = *macro_ptr;
+        if (tok) {
+            TOK_GET(tok, macro_ptr, tokc);
+            if (tok == TOK_LINENUM) {
+                file->line_num = tokc.i;
+                goto redo;
+            }
+        }
+    } else {
+        next_nomacro1();
+    }
+}
+
+/* substitute args in macro_str and return allocated string */
+static int *macro_arg_subst(Sym **nested_list, int *macro_str, Sym *args)
+{
+    int *st, last_tok, t, notfirst;
+    Sym *s;
+    CValue cval;
+    TokenString str;
+    CString cstr;
+
+    tok_str_new(&str);
+    last_tok = 0;
+    while(1) {
+        TOK_GET(t, macro_str, cval);
+        if (!t)
+            break;
+        if (t == '#') {
+            /* stringize */
+            TOK_GET(t, macro_str, cval);
+            if (!t)
+                break;
+            s = sym_find2(args, t);
+            if (s) {
+                cstr_new(&cstr);
+                st = (int *)s->c;
+                notfirst = 0;
+                while (*st) {
+                    if (notfirst)
+                        cstr_ccat(&cstr, ' ');
+                    TOK_GET(t, st, cval);
+                    cstr_cat(&cstr, get_tok_str(t, &cval));
+                    notfirst = 1;
+                }
+                cstr_ccat(&cstr, '\0');
+#ifdef PP_DEBUG
+                printf("stringize: %s\n", (char *)cstr.data);
+#endif
+                /* add string */
+                cval.cstr = &cstr;
+                tok_str_add2(&str, TOK_STR, &cval);
+                cstr_free(&cstr);
+            } else {
+                tok_str_add2(&str, t, &cval);
+            }
+        } else if (t >= TOK_IDENT) {
+            s = sym_find2(args, t);
+            if (s) {
+                st = (int *)s->c;
+                /* if '##' is present before or after, no arg substitution */
+                if (*macro_str == TOK_TWOSHARPS || last_tok == TOK_TWOSHARPS) {
+                    /* special case for var arg macros : ## eats the
+                       ',' if empty VA_ARGS variable. */
+                    /* XXX: test of the ',' is not 100%
+                       reliable. should fix it to avoid security
+                       problems */
+                    if (gnu_ext && s->type.t &&
+                        last_tok == TOK_TWOSHARPS && 
+                        str.len >= 2 && str.str[str.len - 2] == ',') {
+                        if (*st == 0) {
+                            /* suppress ',' '##' */
+                            str.len -= 2;
+                        } else {
+                            /* suppress '##' and add variable */
+                            str.len--;
+                            goto add_var;
+                        }
+                    } else {
+                        int t1;
+                    add_var:
+                        for(;;) {
+                            TOK_GET(t1, st, cval);
+                            if (!t1)
+                                break;
+                            tok_str_add2(&str, t1, &cval);
+                        }
+                    }
+                } else {
+                    /* NOTE: the stream cannot be read when macro
+                       substituing an argument */
+                    macro_subst(&str, nested_list, st, NULL);
+                }
+            } else {
+                tok_str_add(&str, t);
+            }
+        } else {
+            tok_str_add2(&str, t, &cval);
+        }
+        last_tok = t;
+    }
+    tok_str_add(&str, 0);
+    return str.str;
+}
+
+static char const ab_month_name[12][4] =
+{
+    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+/* do macro substitution of current token with macro 's' and add
+   result to (tok_str,tok_len). 'nested_list' is the list of all
+   macros we got inside to avoid recursing. Return non zero if no
+   substitution needs to be done */
+static int macro_subst_tok(TokenString *tok_str,
+                           Sym **nested_list, Sym *s, struct macro_level **can_read_stream)
+{
+    Sym *args, *sa, *sa1;
+    int mstr_allocated, parlevel, *mstr, t, t1;
+    TokenString str;
+    char *cstrval;
+    CValue cval;
+    CString cstr;
+    char buf[32];
+    
+    /* if symbol is a macro, prepare substitution */
+    /* special macros */
+    if (tok == TOK___LINE__) {
+        snprintf(buf, sizeof(buf), "%d", file->line_num);
+        cstrval = buf;
+        t1 = TOK_PPNUM;
+        goto add_cstr1;
+    } else if (tok == TOK___FILE__) {
+        cstrval = file->filename;
+        goto add_cstr;
+    } else if (tok == TOK___DATE__ || tok == TOK___TIME__) {
+        time_t ti;
+        struct tm *tm;
+
+        time(&ti);
+        tm = localtime(&ti);
+        if (tok == TOK___DATE__) {
+            snprintf(buf, sizeof(buf), "%s %2d %d", 
+                     ab_month_name[tm->tm_mon], tm->tm_mday, tm->tm_year + 1900);
+        } else {
+            snprintf(buf, sizeof(buf), "%02d:%02d:%02d", 
+                     tm->tm_hour, tm->tm_min, tm->tm_sec);
+        }
+        cstrval = buf;
+    add_cstr:
+        t1 = TOK_STR;
+    add_cstr1:
+        cstr_new(&cstr);
+        cstr_cat(&cstr, cstrval);
+        cstr_ccat(&cstr, '\0');
+        cval.cstr = &cstr;
+        tok_str_add2(tok_str, t1, &cval);
+        cstr_free(&cstr);
+    } else {
+        mstr = (int *)s->c;
+        mstr_allocated = 0;
+        if (s->type.t == MACRO_FUNC) {
+            /* NOTE: we do not use next_nomacro to avoid eating the
+               next token. XXX: find better solution */
+        redo:
+            if (macro_ptr) {
+                t = *macro_ptr;
+                if (t == 0 && can_read_stream) {
+                    /* end of macro stream: we must look at the token
+                       after in the file */
+                    struct macro_level *ml = *can_read_stream;
+                    macro_ptr = NULL;
+                    if (ml)
+                    {
+                        macro_ptr = ml->p;
+                        ml->p = NULL;
+                        *can_read_stream = ml -> prev;
+                    }
+                    goto redo;
+                }
+            } else {
+                /* XXX: incorrect with comments */
+                ch = file->buf_ptr[0];
+                while (is_space(ch) || ch == '\n')
+                    cinp();
+                t = ch;
+            }
+            if (t != '(') /* no macro subst */
+                return -1;
+                    
+            /* argument macro */
+            next_nomacro();
+            next_nomacro();
+            args = NULL;
+            sa = s->next;
+            /* NOTE: empty args are allowed, except if no args */
+            for(;;) {
+                /* handle '()' case */
+                if (!args && !sa && tok == ')')
+                    break;
+                if (!sa)
+                    error("macro '%s' used with too many args",
+                          get_tok_str(s->v, 0));
+                tok_str_new(&str);
+                parlevel = 0;
+                /* NOTE: non zero sa->t indicates VA_ARGS */
+                while ((parlevel > 0 || 
+                        (tok != ')' && 
+                         (tok != ',' || sa->type.t))) && 
+                       tok != -1) {
+                    if (tok == '(')
+                        parlevel++;
+                    else if (tok == ')')
+                        parlevel--;
+                    tok_str_add2(&str, tok, &tokc);
+                    next_nomacro();
+                }
+                tok_str_add(&str, 0);
+                sym_push2(&args, sa->v & ~SYM_FIELD, sa->type.t, (int)str.str);
+                sa = sa->next;
+                if (tok == ')') {
+                    /* special case for gcc var args: add an empty
+                       var arg argument if it is omitted */
+                    if (sa && sa->type.t && gnu_ext)
+                        continue;
+                    else
+                        break;
+                }
+                if (tok != ',')
+                    expect(",");
+                next_nomacro();
+            }
+            if (sa) {
+                error("macro '%s' used with too few args",
+                      get_tok_str(s->v, 0));
+            }
+
+            /* now subst each arg */
+            mstr = macro_arg_subst(nested_list, mstr, args);
+            /* free memory */
+            sa = args;
+            while (sa) {
+                sa1 = sa->prev;
+                tok_str_free((int *)sa->c);
+                sym_free(sa);
+                sa = sa1;
+            }
+            mstr_allocated = 1;
+        }
+        sym_push2(nested_list, s->v, 0, 0);
+        macro_subst(tok_str, nested_list, mstr, can_read_stream);
+        /* pop nested defined symbol */
+        sa1 = *nested_list;
+        *nested_list = sa1->prev;
+        sym_free(sa1);
+        if (mstr_allocated)
+            tok_str_free(mstr);
+    }
+    return 0;
+}
+
+/* handle the '##' operator. Return NULL if no '##' seen. Otherwise
+   return the resulting string (which must be freed). */
+static inline int *macro_twosharps(const int *macro_str)
+{
+    TokenSym *ts;
+    const int *macro_ptr1, *start_macro_ptr, *ptr, *saved_macro_ptr;
+    int t;
+    const char *p1, *p2;
+    CValue cval;
+    TokenString macro_str1;
+    CString cstr;
+
+    start_macro_ptr = macro_str;
+    /* we search the first '##' */
+    for(;;) {
+        macro_ptr1 = macro_str;
+        TOK_GET(t, macro_str, cval);
+        /* nothing more to do if end of string */
+        if (t == 0)
+            return NULL;
+        if (*macro_str == TOK_TWOSHARPS)
+            break;
+    }
+
+    /* we saw '##', so we need more processing to handle it */
+    cstr_new(&cstr);
+    tok_str_new(&macro_str1);
+    tok = t;
+    tokc = cval;
+
+    /* add all tokens seen so far */
+    for(ptr = start_macro_ptr; ptr < macro_ptr1;) {
+        TOK_GET(t, ptr, cval);
+        tok_str_add2(&macro_str1, t, &cval);
+    }
+    saved_macro_ptr = macro_ptr;
+    /* XXX: get rid of the use of macro_ptr here */
+    macro_ptr = (int *)macro_str;
+    for(;;) {
+        while (*macro_ptr == TOK_TWOSHARPS) {
+            macro_ptr++;
+            macro_ptr1 = macro_ptr;
+            t = *macro_ptr;
+            if (t) {
+                TOK_GET(t, macro_ptr, cval);
+                /* We concatenate the two tokens if we have an
+                   identifier or a preprocessing number */
+                cstr_reset(&cstr);
+                p1 = get_tok_str(tok, &tokc);
+                cstr_cat(&cstr, p1);
+                p2 = get_tok_str(t, &cval);
+                cstr_cat(&cstr, p2);
+                cstr_ccat(&cstr, '\0');
+                
+                if ((tok >= TOK_IDENT || tok == TOK_PPNUM) && 
+                    (t >= TOK_IDENT || t == TOK_PPNUM)) {
+                    if (tok == TOK_PPNUM) {
+                        /* if number, then create a number token */
+                        /* NOTE: no need to allocate because
+                           tok_str_add2() does it */
+                        tokc.cstr = &cstr;
+                    } else {
+                        /* if identifier, we must do a test to
+                           validate we have a correct identifier */
+                        if (t == TOK_PPNUM) {
+                            const char *p;
+                            int c;
+
+                            p = p2;
+                            for(;;) {
+                                c = *p;
+                                if (c == '\0')
+                                    break;
+                                p++;
+                                if (!isnum(c) && !isid(c))
+                                    goto error_pasting;
+                            }
+                        }
+                        ts = tok_alloc(cstr.data, strlen(cstr.data));
+                        tok = ts->tok; /* modify current token */
+                    }
+                } else {
+                    const char *str = cstr.data;
+                    const unsigned char *q;
+
+                    /* we look for a valid token */
+                    /* XXX: do more extensive checks */
+                    if (!strcmp(str, ">>=")) {
+                        tok = TOK_A_SAR;
+                    } else if (!strcmp(str, "<<=")) {
+                        tok = TOK_A_SHL;
+                    } else if (strlen(str) == 2) {
+                        /* search in two bytes table */
+                        q = tok_two_chars;
+                        for(;;) {
+                            if (!*q)
+                                goto error_pasting;
+                            if (q[0] == str[0] && q[1] == str[1])
+                                break;
+                            q += 3;
+                        }
+                        tok = q[2];
+                    } else {
+                    error_pasting:
+                        /* NOTE: because get_tok_str use a static buffer,
+                           we must save it */
+                        cstr_reset(&cstr);
+                        p1 = get_tok_str(tok, &tokc);
+                        cstr_cat(&cstr, p1);
+                        cstr_ccat(&cstr, '\0');
+                        p2 = get_tok_str(t, &cval);
+                        warning("pasting \"%s\" and \"%s\" does not give a valid preprocessing token", cstr.data, p2);
+                        /* cannot merge tokens: just add them separately */
+                        tok_str_add2(&macro_str1, tok, &tokc);
+                        /* XXX: free associated memory ? */
+                        tok = t;
+                        tokc = cval;
+                    }
+                }
+            }
+        }
+        tok_str_add2(&macro_str1, tok, &tokc);
+        next_nomacro();
+        if (tok == 0)
+            break;
+    }
+    macro_ptr = (int *)saved_macro_ptr;
+    cstr_free(&cstr);
+    tok_str_add(&macro_str1, 0);
+    return macro_str1.str;
+}
+
+
+/* do macro substitution of macro_str and add result to
+   (tok_str,tok_len). 'nested_list' is the list of all macros we got
+   inside to avoid recursing. */
+static void macro_subst(TokenString *tok_str, Sym **nested_list, 
+                        const int *macro_str, struct macro_level ** can_read_stream)
+{
+    Sym *s;
+    int *macro_str1;
+    const int *ptr;
+    int t, ret;
+    CValue cval;
+    struct macro_level ml;
+    
+    /* first scan for '##' operator handling */
+    ptr = macro_str;
+    macro_str1 = macro_twosharps(ptr);
+    if (macro_str1) 
+        ptr = macro_str1;
+    while (1) {
+        /* NOTE: ptr == NULL can only happen if tokens are read from
+           file stream due to a macro function call */
+        if (ptr == NULL)
+            break;
+        TOK_GET(t, ptr, cval);
+        if (t == 0)
+            break;
+        s = define_find(t);
+        if (s != NULL) {
+            /* if nested substitution, do nothing */
+            if (sym_find2(*nested_list, t))
+                goto no_subst;
+            ml.p = macro_ptr;
+            if (can_read_stream)
+                ml.prev = *can_read_stream, *can_read_stream = &ml;
+            macro_ptr = (int *)ptr;
+            tok = t;
+            ret = macro_subst_tok(tok_str, nested_list, s, can_read_stream);
+            ptr = (int *)macro_ptr;
+            macro_ptr = ml.p;
+            if (can_read_stream && *can_read_stream == &ml)
+                    *can_read_stream = ml.prev;
+            if (ret != 0)
+                goto no_subst;
+        } else {
+        no_subst:
+            tok_str_add2(tok_str, t, &cval);
+        }
+    }
+    if (macro_str1)
+        tok_str_free(macro_str1);
+}
+
+/* return next token with macro substitution */
+static void next(void)
+{
+    Sym *nested_list, *s;
+    TokenString str;
+    struct macro_level *ml;
+
+ redo:
+    next_nomacro();
+    if (!macro_ptr) {
+        /* if not reading from macro substituted string, then try
+           to substitute macros */
+        if (tok >= TOK_IDENT &&
+            (parse_flags & PARSE_FLAG_PREPROCESS)) {
+            s = define_find(tok);
+            if (s) {
+                /* we have a macro: we try to substitute */
+                tok_str_new(&str);
+                nested_list = NULL;
+                ml = NULL;
+                if (macro_subst_tok(&str, &nested_list, s, &ml) == 0) {
+                    /* substitution done, NOTE: maybe empty */
+                    tok_str_add(&str, 0);
+                    macro_ptr = str.str;
+                    macro_ptr_allocated = str.str;
+                    goto redo;
+                }
+            }
+        }
+    } else {
+        if (tok == 0) {
+            /* end of macro or end of unget buffer */
+            if (unget_buffer_enabled) {
+                macro_ptr = unget_saved_macro_ptr;
+                unget_buffer_enabled = 0;
+            } else {
+                /* end of macro string: free it */
+                tok_str_free(macro_ptr_allocated);
+                macro_ptr = NULL;
+            }
+            goto redo;
+        }
+    }
+    
+    /* convert preprocessor tokens into C tokens */
+    if (tok == TOK_PPNUM &&
+        (parse_flags & PARSE_FLAG_TOK_NUM)) {
+        parse_number((char *)tokc.cstr->data);
+    }
+}
+
+/* push back current token and set current token to 'last_tok'. Only
+   identifier case handled for labels. */
+static inline void unget_tok(int last_tok)
+{
+    int i, n;
+    int *q;
+    unget_saved_macro_ptr = macro_ptr;
+    unget_buffer_enabled = 1;
+    q = unget_saved_buffer;
+    macro_ptr = q;
+    *q++ = tok;
+    n = tok_ext_size(tok) - 1;
+    for(i=0;i<n;i++)
+        *q++ = tokc.tab[i];
+    *q = 0; /* end of token string */
+    tok = last_tok;
+}
+
+
+void swap(int *p, int *q)
+{
+    int t;
+    t = *p;
+    *p = *q;
+    *q = t;
+}
+
+void vsetc(CType *type, int r, CValue *vc)
+{
+    int v;
+
+    if (vtop >= vstack + (VSTACK_SIZE - 1))
+        error("memory full");
+    /* cannot let cpu flags if other instruction are generated. Also
+       avoid leaving VT_JMP anywhere except on the top of the stack
+       because it would complicate the code generator. */
+    if (vtop >= vstack) {
+        v = vtop->r & VT_VALMASK;
+        if (v == VT_CMP || (v & ~1) == VT_JMP)
+            gv(RC_INT);
+    }
+    vtop++;
+    vtop->type = *type;
+    vtop->r = r;
+    vtop->r2 = VT_CONST;
+    vtop->c = *vc;
+}
+
+/* push integer constant */
+void vpushi(int v)
+{
+    CValue cval;
+    cval.i = v;
+    vsetc(&int_type, VT_CONST, &cval);
+}
+
+/* Return a static symbol pointing to a section */
+static Sym *get_sym_ref(CType *type, Section *sec, 
+                        unsigned long offset, unsigned long size)
+{
+    int v;
+    Sym *sym;
+
+    v = anon_sym++;
+    sym = global_identifier_push(v, type->t | VT_STATIC, 0);
+    sym->type.ref = type->ref;
+    sym->r = VT_CONST | VT_SYM;
+    put_extern_sym(sym, sec, offset, size);
+    return sym;
+}
+
+/* push a reference to a section offset by adding a dummy symbol */
+static void vpush_ref(CType *type, Section *sec, unsigned long offset, unsigned long size)
+{
+    CValue cval;
+
+    cval.ul = 0;
+    vsetc(type, VT_CONST | VT_SYM, &cval);
+    vtop->sym = get_sym_ref(type, sec, offset, size);
+}
+
+/* define a new external reference to a symbol 'v' of type 'u' */
+static Sym *external_global_sym(int v, CType *type, int r)
+{
+    Sym *s;
+
+    s = sym_find(v);
+    if (!s) {
+        /* push forward reference */
+        s = global_identifier_push(v, type->t | VT_EXTERN, 0);
+        s->type.ref = type->ref;
+        s->r = r | VT_CONST | VT_SYM;
+    }
+    return s;
+}
+
+/* define a new external reference to a symbol 'v' of type 'u' */
+static Sym *external_sym(int v, CType *type, int r)
+{
+    Sym *s;
+
+    s = sym_find(v);
+    if (!s) {
+        /* push forward reference */
+        s = sym_push(v, type, r | VT_CONST | VT_SYM, 0);
+        s->type.t |= VT_EXTERN;
+    } else {
+        if (!is_compatible_types(&s->type, type))
+            error("incompatible types for redefinition of '%s'", 
+                  get_tok_str(v, NULL));
+    }
+    return s;
+}
+
+/* push a reference to global symbol v */
+static void vpush_global_sym(CType *type, int v)
+{
+    Sym *sym;
+    CValue cval;
+
+    sym = external_global_sym(v, type, 0);
+    cval.ul = 0;
+    vsetc(type, VT_CONST | VT_SYM, &cval);
+    vtop->sym = sym;
+}
+
+void vset(CType *type, int r, int v)
+{
+    CValue cval;
+
+    cval.i = v;
+    vsetc(type, r, &cval);
+}
+
+void vseti(int r, int v)
+{
+    CType type;
+    type.t = VT_INT;
+    vset(&type, r, v);
+}
+
+void vswap(void)
+{
+    SValue tmp;
+
+    tmp = vtop[0];
+    vtop[0] = vtop[-1];
+    vtop[-1] = tmp;
+}
+
+void vpushv(SValue *v)
+{
+    if (vtop >= vstack + (VSTACK_SIZE - 1))
+        error("memory full");
+    vtop++;
+    *vtop = *v;
+}
+
+void vdup(void)
+{
+    vpushv(vtop);
+}
+
+/* save r to the memory stack, and mark it as being free */
+void save_reg(int r)
+{
+    int l, saved, size, align;
+    SValue *p, sv;
+    CType *type;
+
+    /* modify all stack values */
+    saved = 0;
+    l = 0;
+    for(p=vstack;p<=vtop;p++) {
+        if ((p->r & VT_VALMASK) == r ||
+            (p->r2 & VT_VALMASK) == r) {
+            /* must save value on stack if not already done */
+            if (!saved) {
+                /* NOTE: must reload 'r' because r might be equal to r2 */
+                r = p->r & VT_VALMASK;
+                /* store register in the stack */
+                type = &p->type;
+                if ((p->r & VT_LVAL) || 
+                    (!is_float(type->t) && (type->t & VT_BTYPE) != VT_LLONG))
+                    type = &int_type;
+                size = type_size(type, &align);
+                loc = (loc - size) & -align;
+                sv.type.t = type->t;
+                sv.r = VT_LOCAL | VT_LVAL;
+                sv.c.ul = loc;
+                store(r, &sv);
+#ifdef TCC_TARGET_I386
+                /* x86 specific: need to pop fp register ST0 if saved */
+                if (r == TREG_ST0) {
+                    o(0xd9dd); /* fstp %st(1) */
+                }
+#endif
+                /* special long long case */
+                if ((type->t & VT_BTYPE) == VT_LLONG) {
+                    sv.c.ul += 4;
+                    store(p->r2, &sv);
+                }
+                l = loc;
+                saved = 1;
+            }
+            /* mark that stack entry as being saved on the stack */
+            if (p->r & VT_LVAL) {
+                /* also clear the bounded flag because the
+                   relocation address of the function was stored in
+                   p->c.ul */
+                p->r = (p->r & ~(VT_VALMASK | VT_BOUNDED)) | VT_LLOCAL;
+            } else {
+                p->r = lvalue_type(p->type.t) | VT_LOCAL;
+            }
+            p->r2 = VT_CONST;
+            p->c.ul = l;
+        }
+    }
+}
+
+/* find a register of class 'rc2' with at most one reference on stack.
+ * If none, call get_reg(rc) */
+int get_reg_ex(int rc, int rc2) 
+{
+    int r;
+    SValue *p;
+    
+    for(r=0;r<NB_REGS;r++) {
+        if (reg_classes[r] & rc2) {
+            int n;
+            n=0;
+            for(p = vstack; p <= vtop; p++) {
+                if ((p->r & VT_VALMASK) == r ||
+                    (p->r2 & VT_VALMASK) == r)
+                    n++;
+            }
+            if (n <= 1)
+                return r;
+        }
+    }
+    return get_reg(rc);
+}
+
+/* find a free register of class 'rc'. If none, save one register */
+int get_reg(int rc)
+{
+    int r;
+    SValue *p;
+
+    /* find a free register */
+    for(r=0;r<NB_REGS;r++) {
+        if (reg_classes[r] & rc) {
+            for(p=vstack;p<=vtop;p++) {
+                if ((p->r & VT_VALMASK) == r ||
+                    (p->r2 & VT_VALMASK) == r)
+                    goto notfound;
+            }
+            return r;
+        }
+    notfound: ;
+    }
+    
+    /* no register left : free the first one on the stack (VERY
+       IMPORTANT to start from the bottom to ensure that we don't
+       spill registers used in gen_opi()) */
+    for(p=vstack;p<=vtop;p++) {
+        r = p->r & VT_VALMASK;
+        if (r < VT_CONST && (reg_classes[r] & rc))
+            goto save_found;
+        /* also look at second register (if long long) */
+        r = p->r2 & VT_VALMASK;
+        if (r < VT_CONST && (reg_classes[r] & rc)) {
+        save_found:
+            save_reg(r);
+            return r;
+        }
+    }
+    /* Should never comes here */
+    return -1;
+}
+
+/* save registers up to (vtop - n) stack entry */
+void save_regs(int n)
+{
+    int r;
+    SValue *p, *p1;
+    p1 = vtop - n;
+    for(p = vstack;p <= p1; p++) {
+        r = p->r & VT_VALMASK;
+        if (r < VT_CONST) {
+            save_reg(r);
+        }
+    }
+}
+
+/* move register 's' to 'r', and flush previous value of r to memory
+   if needed */
+void move_reg(int r, int s)
+{
+    SValue sv;
+
+    if (r != s) {
+        save_reg(r);
+        sv.type.t = VT_INT;
+        sv.r = s;
+        sv.c.ul = 0;
+        load(r, &sv);
+    }
+}
+
+/* get address of vtop (vtop MUST BE an lvalue) */
+void gaddrof(void)
+{
+    vtop->r &= ~VT_LVAL;
+    /* tricky: if saved lvalue, then we can go back to lvalue */
+    if ((vtop->r & VT_VALMASK) == VT_LLOCAL)
+        vtop->r = (vtop->r & ~(VT_VALMASK | VT_LVAL_TYPE)) | VT_LOCAL | VT_LVAL;
+}
+
+#ifdef CONFIG_TCC_BCHECK
+/* generate lvalue bound code */
+void gbound(void)
+{
+    int lval_type;
+    CType type1;
+
+    vtop->r &= ~VT_MUSTBOUND;
+    /* if lvalue, then use checking code before dereferencing */
+    if (vtop->r & VT_LVAL) {
+        /* if not VT_BOUNDED value, then make one */
+        if (!(vtop->r & VT_BOUNDED)) {
+            lval_type = vtop->r & (VT_LVAL_TYPE | VT_LVAL);
+            /* must save type because we must set it to int to get pointer */
+            type1 = vtop->type;
+            vtop->type.t = VT_INT;
+            gaddrof();
+            vpushi(0);
+            gen_bounded_ptr_add();
+            vtop->r |= lval_type;
+            vtop->type = type1;
+        }
+        /* then check for dereferencing */
+        gen_bounded_ptr_deref();
+    }
+}
+#endif
+
+/* store vtop a register belonging to class 'rc'. lvalues are
+   converted to values. Cannot be used if cannot be converted to
+   register value (such as structures). */
+int gv(int rc)
+{
+    int r, r2, rc2, bit_pos, bit_size, size, align, i;
+    unsigned long long ll;
+
+    /* NOTE: get_reg can modify vstack[] */
+    if (vtop->type.t & VT_BITFIELD) {
+        bit_pos = (vtop->type.t >> VT_STRUCT_SHIFT) & 0x3f;
+        bit_size = (vtop->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f;
+        /* remove bit field info to avoid loops */
+        vtop->type.t &= ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT));
+        /* generate shifts */
+        vpushi(32 - (bit_pos + bit_size));
+        gen_op(TOK_SHL);
+        vpushi(32 - bit_size);
+        /* NOTE: transformed to SHR if unsigned */
+        gen_op(TOK_SAR);
+        r = gv(rc);
+    } else {
+        if (is_float(vtop->type.t) && 
+            (vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
+            Sym *sym;
+            int *ptr;
+            unsigned long offset;
+            
+            /* XXX: unify with initializers handling ? */
+            /* CPUs usually cannot use float constants, so we store them
+               generically in data segment */
+            size = type_size(&vtop->type, &align);
+            offset = (data_section->data_offset + align - 1) & -align;
+            data_section->data_offset = offset;
+            /* XXX: not portable yet */
+            ptr = section_ptr_add(data_section, size);
+            size = size >> 2;
+            for(i=0;i<size;i++)
+                ptr[i] = vtop->c.tab[i];
+            sym = get_sym_ref(&vtop->type, data_section, offset, size << 2);
+            vtop->r |= VT_LVAL | VT_SYM;
+            vtop->sym = sym;
+            vtop->c.ul = 0;
+        }
+#ifdef CONFIG_TCC_BCHECK
+        if (vtop->r & VT_MUSTBOUND) 
+            gbound();
+#endif
+
+        r = vtop->r & VT_VALMASK;
+        /* need to reload if:
+           - constant
+           - lvalue (need to dereference pointer)
+           - already a register, but not in the right class */
+        if (r >= VT_CONST || 
+            (vtop->r & VT_LVAL) ||
+            !(reg_classes[r] & rc) ||
+            ((vtop->type.t & VT_BTYPE) == VT_LLONG && 
+             !(reg_classes[vtop->r2] & rc))) {
+            r = get_reg(rc);
+            if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
+                /* two register type load : expand to two words
+                   temporarily */
+                if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
+                    /* load constant */
+                    ll = vtop->c.ull;
+                    vtop->c.ui = ll; /* first word */
+                    load(r, vtop);
+                    vtop->r = r; /* save register value */
+                    vpushi(ll >> 32); /* second word */
+                } else if (r >= VT_CONST || /* XXX: test to VT_CONST incorrect ? */
+                           (vtop->r & VT_LVAL)) {
+                    /* We do not want to modifier the long long
+                       pointer here, so the safest (and less
+                       efficient) is to save all the other registers
+                       in the stack. XXX: totally inefficient. */
+                    save_regs(1);
+                    /* load from memory */
+                    load(r, vtop);
+                    vdup();
+                    vtop[-1].r = r; /* save register value */
+                    /* increment pointer to get second word */
+                    vtop->type.t = VT_INT;
+                    gaddrof();
+                    vpushi(4);
+                    gen_op('+');
+                    vtop->r |= VT_LVAL;
+                } else {
+                    /* move registers */
+                    load(r, vtop);
+                    vdup();
+                    vtop[-1].r = r; /* save register value */
+                    vtop->r = vtop[-1].r2;
+                }
+                /* allocate second register */
+                rc2 = RC_INT;
+                if (rc == RC_IRET)
+                    rc2 = RC_LRET;
+                r2 = get_reg(rc2);
+                load(r2, vtop);
+                vpop();
+                /* write second register */
+                vtop->r2 = r2;
+            } else if ((vtop->r & VT_LVAL) && !is_float(vtop->type.t)) {
+                int t1, t;
+                /* lvalue of scalar type : need to use lvalue type
+                   because of possible cast */
+                t = vtop->type.t;
+                t1 = t;
+                /* compute memory access type */
+                if (vtop->r & VT_LVAL_BYTE)
+                    t = VT_BYTE;
+                else if (vtop->r & VT_LVAL_SHORT)
+                    t = VT_SHORT;
+                if (vtop->r & VT_LVAL_UNSIGNED)
+                    t |= VT_UNSIGNED;
+                vtop->type.t = t;
+                load(r, vtop);
+                /* restore wanted type */
+                vtop->type.t = t1;
+            } else {
+                /* one register type load */
+                load(r, vtop);
+            }
+        }
+        vtop->r = r;
+#ifdef TCC_TARGET_C67
+        /* uses register pairs for doubles */
+        if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) 
+            vtop->r2 = r+1;
+#endif
+    }
+    return r;
+}
+
+/* generate vtop[-1] and vtop[0] in resp. classes rc1 and rc2 */
+void gv2(int rc1, int rc2)
+{
+    int v;
+
+    /* generate more generic register first. But VT_JMP or VT_CMP
+       values must be generated first in all cases to avoid possible
+       reload errors */
+    v = vtop[0].r & VT_VALMASK;
+    if (v != VT_CMP && (v & ~1) != VT_JMP && rc1 <= rc2) {
+        vswap();
+        gv(rc1);
+        vswap();
+        gv(rc2);
+        /* test if reload is needed for first register */
+        if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) {
+            vswap();
+            gv(rc1);
+            vswap();
+        }
+    } else {
+        gv(rc2);
+        vswap();
+        gv(rc1);
+        vswap();
+        /* test if reload is needed for first register */
+        if ((vtop[0].r & VT_VALMASK) >= VT_CONST) {
+            gv(rc2);
+        }
+    }
+}
+
+/* expand long long on stack in two int registers */
+void lexpand(void)
+{
+    int u;
+
+    u = vtop->type.t & VT_UNSIGNED;
+    gv(RC_INT);
+    vdup();
+    vtop[0].r = vtop[-1].r2;
+    vtop[0].r2 = VT_CONST;
+    vtop[-1].r2 = VT_CONST;
+    vtop[0].type.t = VT_INT | u;
+    vtop[-1].type.t = VT_INT | u;
+}
+
+#ifdef TCC_TARGET_ARM
+/* expand long long on stack */
+void lexpand_nr(void)
+{
+    int u,v;
+
+    u = vtop->type.t & VT_UNSIGNED;
+    vdup();
+    vtop->r2 = VT_CONST;
+    vtop->type.t = VT_INT | u;
+    v=vtop[-1].r & (VT_VALMASK | VT_LVAL);
+    if (v == VT_CONST) {
+      vtop[-1].c.ui = vtop->c.ull;
+      vtop->c.ui = vtop->c.ull >> 32;
+      vtop->r = VT_CONST;
+    } else if (v == (VT_LVAL|VT_CONST) || v == (VT_LVAL|VT_LOCAL)) {
+      vtop->c.ui += 4;
+      vtop->r = vtop[-1].r;
+    } else if (v > VT_CONST) {
+      vtop--;
+      lexpand();
+    } else
+      vtop->r = vtop[-1].r2;
+    vtop[-1].r2 = VT_CONST;
+    vtop[-1].type.t = VT_INT | u;
+}
+#endif
+
+/* build a long long from two ints */
+void lbuild(int t)
+{
+    gv2(RC_INT, RC_INT);
+    vtop[-1].r2 = vtop[0].r;
+    vtop[-1].type.t = t;
+    vpop();
+}
+
+/* rotate n first stack elements to the bottom 
+   I1 ... In -> I2 ... In I1 [top is right]
+*/
+void vrotb(int n)
+{
+    int i;
+    SValue tmp;
+
+    tmp = vtop[-n + 1];
+    for(i=-n+1;i!=0;i++)
+        vtop[i] = vtop[i+1];
+    vtop[0] = tmp;
+}
+
+/* rotate n first stack elements to the top 
+   I1 ... In -> In I1 ... I(n-1)  [top is right]
+ */
+void vrott(int n)
+{
+    int i;
+    SValue tmp;
+
+    tmp = vtop[0];
+    for(i = 0;i < n - 1; i++)
+        vtop[-i] = vtop[-i - 1];
+    vtop[-n + 1] = tmp;
+}
+
+#ifdef TCC_TARGET_ARM
+/* like vrott but in other direction
+   In ... I1 -> I(n-1) ... I1 In  [top is right]
+ */
+void vnrott(int n)
+{
+    int i;
+    SValue tmp;
+
+    tmp = vtop[-n + 1];
+    for(i = n - 1; i > 0; i--)
+        vtop[-i] = vtop[-i + 1];
+    vtop[0] = tmp;
+}
+#endif
+
+/* pop stack value */
+void vpop(void)
+{
+    int v;
+    v = vtop->r & VT_VALMASK;
+#ifdef TCC_TARGET_I386
+    /* for x86, we need to pop the FP stack */
+    if (v == TREG_ST0 && !nocode_wanted) {
+        o(0xd9dd); /* fstp %st(1) */
+    } else
+#endif
+    if (v == VT_JMP || v == VT_JMPI) {
+        /* need to put correct jump if && or || without test */
+        gsym(vtop->c.ul);
+    }
+    vtop--;
+}
+
+/* convert stack entry to register and duplicate its value in another
+   register */
+void gv_dup(void)
+{
+    int rc, t, r, r1;
+    SValue sv;
+
+    t = vtop->type.t;
+    if ((t & VT_BTYPE) == VT_LLONG) {
+        lexpand();
+        gv_dup();
+        vswap();
+        vrotb(3);
+        gv_dup();
+        vrotb(4);
+        /* stack: H L L1 H1 */
+        lbuild(t);
+        vrotb(3);
+        vrotb(3);
+        vswap();
+        lbuild(t);
+        vswap();
+    } else {
+        /* duplicate value */
+        rc = RC_INT;
+        sv.type.t = VT_INT;
+        if (is_float(t)) {
+            rc = RC_FLOAT;
+            sv.type.t = t;
+        }
+        r = gv(rc);
+        r1 = get_reg(rc);
+        sv.r = r;
+        sv.c.ul = 0;
+        load(r1, &sv); /* move r to r1 */
+        vdup();
+        /* duplicates value */
+        vtop->r = r1;
+    }
+}
+
+/* generate CPU independent (unsigned) long long operations */
+void gen_opl(int op)
+{
+    int t, a, b, op1, c, i;
+    int func;
+    SValue tmp;
+
+    switch(op) {
+    case '/':
+    case TOK_PDIV:
+        func = TOK___divdi3;
+        goto gen_func;
+    case TOK_UDIV:
+        func = TOK___udivdi3;
+        goto gen_func;
+    case '%':
+        func = TOK___moddi3;
+        goto gen_func;
+    case TOK_UMOD:
+        func = TOK___umoddi3;
+    gen_func:
+        /* call generic long long function */
+        vpush_global_sym(&func_old_type, func);
+        vrott(3);
+        gfunc_call(2);
+        vpushi(0);
+        vtop->r = REG_IRET;
+        vtop->r2 = REG_LRET;
+        break;
+    case '^':
+    case '&':
+    case '|':
+    case '*':
+    case '+':
+    case '-':
+        t = vtop->type.t;
+        vswap();
+        lexpand();
+        vrotb(3);
+        lexpand();
+        /* stack: L1 H1 L2 H2 */
+        tmp = vtop[0];
+        vtop[0] = vtop[-3];
+        vtop[-3] = tmp;
+        tmp = vtop[-2];
+        vtop[-2] = vtop[-3];
+        vtop[-3] = tmp;
+        vswap();
+        /* stack: H1 H2 L1 L2 */
+        if (op == '*') {
+            vpushv(vtop - 1);
+            vpushv(vtop - 1);
+            gen_op(TOK_UMULL);
+            lexpand();
+            /* stack: H1 H2 L1 L2 ML MH */
+            for(i=0;i<4;i++)
+                vrotb(6);
+            /* stack: ML MH H1 H2 L1 L2 */
+            tmp = vtop[0];
+            vtop[0] = vtop[-2];
+            vtop[-2] = tmp;
+            /* stack: ML MH H1 L2 H2 L1 */
+            gen_op('*');
+            vrotb(3);
+            vrotb(3);
+            gen_op('*');
+            /* stack: ML MH M1 M2 */
+            gen_op('+');
+            gen_op('+');
+        } else if (op == '+' || op == '-') {
+            /* XXX: add non carry method too (for MIPS or alpha) */
+            if (op == '+')
+                op1 = TOK_ADDC1;
+            else
+                op1 = TOK_SUBC1;
+            gen_op(op1);
+            /* stack: H1 H2 (L1 op L2) */
+            vrotb(3);
+            vrotb(3);
+            gen_op(op1 + 1); /* TOK_xxxC2 */
+        } else {
+            gen_op(op);
+            /* stack: H1 H2 (L1 op L2) */
+            vrotb(3);
+            vrotb(3);
+            /* stack: (L1 op L2) H1 H2 */
+            gen_op(op);
+            /* stack: (L1 op L2) (H1 op H2) */
+        }
+        /* stack: L H */
+        lbuild(t);
+        break;
+    case TOK_SAR:
+    case TOK_SHR:
+    case TOK_SHL:
+        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
+            t = vtop[-1].type.t;
+            vswap();
+            lexpand();
+            vrotb(3);
+            /* stack: L H shift */
+            c = (int)vtop->c.i;
+            /* constant: simpler */
+            /* NOTE: all comments are for SHL. the other cases are
+               done by swaping words */
+            vpop();
+            if (op != TOK_SHL)
+                vswap();
+            if (c >= 32) {
+                /* stack: L H */
+                vpop();
+                if (c > 32) {
+                    vpushi(c - 32);
+                    gen_op(op);
+                }
+                if (op != TOK_SAR) {
+                    vpushi(0);
+                } else {
+                    gv_dup();
+                    vpushi(31);
+                    gen_op(TOK_SAR);
+                }
+                vswap();
+            } else {
+                vswap();
+                gv_dup();
+                /* stack: H L L */
+                vpushi(c);
+                gen_op(op);
+                vswap();
+                vpushi(32 - c);
+                if (op == TOK_SHL)
+                    gen_op(TOK_SHR);
+                else
+                    gen_op(TOK_SHL);
+                vrotb(3);
+                /* stack: L L H */
+                vpushi(c);
+                if (op == TOK_SHL)
+                    gen_op(TOK_SHL);
+                else
+                    gen_op(TOK_SHR);
+                gen_op('|');
+            }
+            if (op != TOK_SHL)
+                vswap();
+            lbuild(t);
+        } else {
+            /* XXX: should provide a faster fallback on x86 ? */
+            switch(op) {
+            case TOK_SAR:
+                func = TOK___sardi3;
+                goto gen_func;
+            case TOK_SHR:
+                func = TOK___shrdi3;
+                goto gen_func;
+            case TOK_SHL:
+                func = TOK___shldi3;
+                goto gen_func;
+            }
+        }
+        break;
+    default:
+        /* compare operations */
+        t = vtop->type.t;
+        vswap();
+        lexpand();
+        vrotb(3);
+        lexpand();
+        /* stack: L1 H1 L2 H2 */
+        tmp = vtop[-1];
+        vtop[-1] = vtop[-2];
+        vtop[-2] = tmp;
+        /* stack: L1 L2 H1 H2 */
+        /* compare high */
+        op1 = op;
+        /* when values are equal, we need to compare low words. since
+           the jump is inverted, we invert the test too. */
+        if (op1 == TOK_LT)
+            op1 = TOK_LE;
+        else if (op1 == TOK_GT)
+            op1 = TOK_GE;
+        else if (op1 == TOK_ULT)
+            op1 = TOK_ULE;
+        else if (op1 == TOK_UGT)
+            op1 = TOK_UGE;
+        a = 0;
+        b = 0;
+        gen_op(op1);
+        if (op1 != TOK_NE) {
+            a = gtst(1, 0);
+        }
+        if (op != TOK_EQ) {
+            /* generate non equal test */
+            /* XXX: NOT PORTABLE yet */
+            if (a == 0) {
+                b = gtst(0, 0);
+            } else {
+#if defined(TCC_TARGET_I386)
+                b = psym(0x850f, 0);
+#elif defined(TCC_TARGET_ARM)
+               b = ind;
+               o(0x1A000000 | encbranch(ind, 0, 1));
+#elif defined(TCC_TARGET_C67)
+                error("not implemented");
+#else
+#error not supported
+#endif
+            }
+        }
+        /* compare low. Always unsigned */
+        op1 = op;
+        if (op1 == TOK_LT)
+            op1 = TOK_ULT;
+        else if (op1 == TOK_LE)
+            op1 = TOK_ULE;
+        else if (op1 == TOK_GT)
+            op1 = TOK_UGT;
+        else if (op1 == TOK_GE)
+            op1 = TOK_UGE;
+        gen_op(op1);
+        a = gtst(1, a);
+        gsym(b);
+        vseti(VT_JMPI, a);
+        break;
+    }
+}
+
+/* handle integer constant optimizations and various machine
+   independent opt */
+void gen_opic(int op)
+{
+    int fc, c1, c2, n;
+    SValue *v1, *v2;
+
+    v1 = vtop - 1;
+    v2 = vtop;
+    /* currently, we cannot do computations with forward symbols */
+    c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
+    c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
+    if (c1 && c2) {
+        fc = v2->c.i;
+        switch(op) {
+        case '+': v1->c.i += fc; break;
+        case '-': v1->c.i -= fc; break;
+        case '&': v1->c.i &= fc; break;
+        case '^': v1->c.i ^= fc; break;
+        case '|': v1->c.i |= fc; break;
+        case '*': v1->c.i *= fc; break;
+
+        case TOK_PDIV:
+        case '/':
+        case '%':
+        case TOK_UDIV:
+        case TOK_UMOD:
+            /* if division by zero, generate explicit division */
+            if (fc == 0) {
+                if (const_wanted)
+                    error("division by zero in constant");
+                goto general_case;
+            }
+            switch(op) {
+            default: v1->c.i /= fc; break;
+            case '%': v1->c.i %= fc; break;
+            case TOK_UDIV: v1->c.i = (unsigned)v1->c.i / fc; break;
+            case TOK_UMOD: v1->c.i = (unsigned)v1->c.i % fc; break;
+            }
+            break;
+        case TOK_SHL: v1->c.i <<= fc; break;
+        case TOK_SHR: v1->c.i = (unsigned)v1->c.i >> fc; break;
+        case TOK_SAR: v1->c.i >>= fc; break;
+            /* tests */
+        case TOK_ULT: v1->c.i = (unsigned)v1->c.i < (unsigned)fc; break;
+        case TOK_UGE: v1->c.i = (unsigned)v1->c.i >= (unsigned)fc; break;
+        case TOK_EQ: v1->c.i = v1->c.i == fc; break;
+        case TOK_NE: v1->c.i = v1->c.i != fc; break;
+        case TOK_ULE: v1->c.i = (unsigned)v1->c.i <= (unsigned)fc; break;
+        case TOK_UGT: v1->c.i = (unsigned)v1->c.i > (unsigned)fc; break;
+        case TOK_LT: v1->c.i = v1->c.i < fc; break;
+        case TOK_GE: v1->c.i = v1->c.i >= fc; break;
+        case TOK_LE: v1->c.i = v1->c.i <= fc; break;
+        case TOK_GT: v1->c.i = v1->c.i > fc; break;
+            /* logical */
+        case TOK_LAND: v1->c.i = v1->c.i && fc; break;
+        case TOK_LOR: v1->c.i = v1->c.i || fc; break;
+        default:
+            goto general_case;
+        }
+        vtop--;
+    } else {
+        /* if commutative ops, put c2 as constant */
+        if (c1 && (op == '+' || op == '&' || op == '^' || 
+                   op == '|' || op == '*')) {
+            vswap();
+            swap(&c1, &c2);
+        }
+        fc = vtop->c.i;
+        if (c2 && (((op == '*' || op == '/' || op == TOK_UDIV || 
+                     op == TOK_PDIV) && 
+                    fc == 1) ||
+                   ((op == '+' || op == '-' || op == '|' || op == '^' || 
+                     op == TOK_SHL || op == TOK_SHR || op == TOK_SAR) && 
+                    fc == 0) ||
+                   (op == '&' && 
+                    fc == -1))) {
+            /* nothing to do */
+            vtop--;
+        } else if (c2 && (op == '*' || op == TOK_PDIV || op == TOK_UDIV)) {
+            /* try to use shifts instead of muls or divs */
+            if (fc > 0 && (fc & (fc - 1)) == 0) {
+                n = -1;
+                while (fc) {
+                    fc >>= 1;
+                    n++;
+                }
+                vtop->c.i = n;
+                if (op == '*')
+                    op = TOK_SHL;
+                else if (op == TOK_PDIV)
+                    op = TOK_SAR;
+                else
+                    op = TOK_SHR;
+            }
+            goto general_case;
+        } else if (c2 && (op == '+' || op == '-') &&
+                   (vtop[-1].r & (VT_VALMASK | VT_LVAL | VT_SYM)) == 
+                   (VT_CONST | VT_SYM)) {
+            /* symbol + constant case */
+            if (op == '-')
+                fc = -fc;
+            vtop--;
+            vtop->c.i += fc;
+        } else {
+        general_case:
+            if (!nocode_wanted) {
+                /* call low level op generator */
+                gen_opi(op);
+            } else {
+                vtop--;
+            }
+        }
+    }
+}
+
+/* generate a floating point operation with constant propagation */
+void gen_opif(int op)
+{
+    int c1, c2;
+    SValue *v1, *v2;
+    long double f1, f2;
+
+    v1 = vtop - 1;
+    v2 = vtop;
+    /* currently, we cannot do computations with forward symbols */
+    c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
+    c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
+    if (c1 && c2) {
+        if (v1->type.t == VT_FLOAT) {
+            f1 = v1->c.f;
+            f2 = v2->c.f;
+        } else if (v1->type.t == VT_DOUBLE) {
+            f1 = v1->c.d;
+            f2 = v2->c.d;
+        } else {
+            f1 = v1->c.ld;
+            f2 = v2->c.ld;
+        }
+
+        /* NOTE: we only do constant propagation if finite number (not
+           NaN or infinity) (ANSI spec) */
+        if (!ieee_finite(f1) || !ieee_finite(f2))
+            goto general_case;
+
+        switch(op) {
+        case '+': f1 += f2; break;
+        case '-': f1 -= f2; break;
+        case '*': f1 *= f2; break;
+        case '/': 
+            if (f2 == 0.0) {
+                if (const_wanted)
+                    error("division by zero in constant");
+                goto general_case;
+            }
+            f1 /= f2; 
+            break;
+            /* XXX: also handles tests ? */
+        default:
+            goto general_case;
+        }
+        /* XXX: overflow test ? */
+        if (v1->type.t == VT_FLOAT) {
+            v1->c.f = f1;
+        } else if (v1->type.t == VT_DOUBLE) {
+            v1->c.d = f1;
+        } else {
+            v1->c.ld = f1;
+        }
+        vtop--;
+    } else {
+    general_case:
+        if (!nocode_wanted) {
+            gen_opf(op);
+        } else {
+            vtop--;
+        }
+    }
+}
+
+static int pointed_size(CType *type)
+{
+    int align;
+    return type_size(pointed_type(type), &align);
+}
+
+static inline int is_null_pointer(SValue *p)
+{
+    if ((p->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
+        return 0;
+    return ((p->type.t & VT_BTYPE) == VT_INT && p->c.i == 0) ||
+        ((p->type.t & VT_BTYPE) == VT_LLONG && p->c.ll == 0);
+}
+
+static inline int is_integer_btype(int bt)
+{
+    return (bt == VT_BYTE || bt == VT_SHORT || 
+            bt == VT_INT || bt == VT_LLONG);
+}
+
+/* check types for comparison or substraction of pointers */
+static void check_comparison_pointer_types(SValue *p1, SValue *p2, int op)
+{
+    CType *type1, *type2, tmp_type1, tmp_type2;
+    int bt1, bt2;
+    
+    /* null pointers are accepted for all comparisons as gcc */
+    if (is_null_pointer(p1) || is_null_pointer(p2))
+        return;
+    type1 = &p1->type;
+    type2 = &p2->type;
+    bt1 = type1->t & VT_BTYPE;
+    bt2 = type2->t & VT_BTYPE;
+    /* accept comparison between pointer and integer with a warning */
+    if ((is_integer_btype(bt1) || is_integer_btype(bt2)) && op != '-') {
+        warning("comparison between pointer and integer");
+        return;
+    }
+
+    /* both must be pointers or implicit function pointers */
+    if (bt1 == VT_PTR) {
+        type1 = pointed_type(type1);
+    } else if (bt1 != VT_FUNC) 
+        goto invalid_operands;
+
+    if (bt2 == VT_PTR) {
+        type2 = pointed_type(type2);
+    } else if (bt2 != VT_FUNC) { 
+    invalid_operands:
+        error("invalid operands to binary %s", get_tok_str(op, NULL));
+    }
+    if ((type1->t & VT_BTYPE) == VT_VOID || 
+        (type2->t & VT_BTYPE) == VT_VOID)
+        return;
+    tmp_type1 = *type1;
+    tmp_type2 = *type2;
+    tmp_type1.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE);
+    tmp_type2.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE);
+    if (!is_compatible_types(&tmp_type1, &tmp_type2)) {
+        /* gcc-like error if '-' is used */
+        if (op == '-')
+            goto invalid_operands;
+        else
+            warning("comparison of distinct pointer types lacks a cast");
+    }
+}
+
+/* generic gen_op: handles types problems */
+void gen_op(int op)
+{
+    int u, t1, t2, bt1, bt2, t;
+    CType type1;
+
+    t1 = vtop[-1].type.t;
+    t2 = vtop[0].type.t;
+    bt1 = t1 & VT_BTYPE;
+    bt2 = t2 & VT_BTYPE;
+        
+    if (bt1 == VT_PTR || bt2 == VT_PTR) {
+        /* at least one operand is a pointer */
+        /* relationnal op: must be both pointers */
+        if (op >= TOK_ULT && op <= TOK_GT) {
+            check_comparison_pointer_types(vtop - 1, vtop, op);
+            /* pointers are handled are unsigned */
+            t = VT_INT | VT_UNSIGNED;
+            goto std_op;
+        }
+        /* if both pointers, then it must be the '-' op */
+        if (bt1 == VT_PTR && bt2 == VT_PTR) {
+            if (op != '-')
+                error("cannot use pointers here");
+            check_comparison_pointer_types(vtop - 1, vtop, op);
+            /* XXX: check that types are compatible */
+            u = pointed_size(&vtop[-1].type);
+            gen_opic(op);
+            /* set to integer type */
+            vtop->type.t = VT_INT; 
+            vpushi(u);
+            gen_op(TOK_PDIV);
+        } else {
+            /* exactly one pointer : must be '+' or '-'. */
+            if (op != '-' && op != '+')
+                error("cannot use pointers here");
+            /* Put pointer as first operand */
+            if (bt2 == VT_PTR) {
+                vswap();
+                swap(&t1, &t2);
+            }
+            type1 = vtop[-1].type;
+            /* XXX: cast to int ? (long long case) */
+            vpushi(pointed_size(&vtop[-1].type));
+            gen_op('*');
+#ifdef CONFIG_TCC_BCHECK
+            /* if evaluating constant expression, no code should be
+               generated, so no bound check */
+            if (do_bounds_check && !const_wanted) {
+                /* if bounded pointers, we generate a special code to
+                   test bounds */
+                if (op == '-') {
+                    vpushi(0);
+                    vswap();
+                    gen_op('-');
+                }
+                gen_bounded_ptr_add();
+            } else
+#endif
+            {
+                gen_opic(op);
+            }
+            /* put again type if gen_opic() swaped operands */
+            vtop->type = type1;
+        }
+    } else if (is_float(bt1) || is_float(bt2)) {
+        /* compute bigger type and do implicit casts */
+        if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) {
+            t = VT_LDOUBLE;
+        } else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) {
+            t = VT_DOUBLE;
+        } else {
+            t = VT_FLOAT;
+        }
+        /* floats can only be used for a few operations */
+        if (op != '+' && op != '-' && op != '*' && op != '/' &&
+            (op < TOK_ULT || op > TOK_GT))
+            error("invalid operands for binary operation");
+        goto std_op;
+    } else if (bt1 == VT_LLONG || bt2 == VT_LLONG) {
+        /* cast to biggest op */
+        t = VT_LLONG;
+        /* convert to unsigned if it does not fit in a long long */
+        if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED) ||
+            (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED))
+            t |= VT_UNSIGNED;
+        goto std_op;
+    } else {
+        /* integer operations */
+        t = VT_INT;
+        /* convert to unsigned if it does not fit in an integer */
+        if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED) ||
+            (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED))
+            t |= VT_UNSIGNED;
+    std_op:
+        /* XXX: currently, some unsigned operations are explicit, so
+           we modify them here */
+        if (t & VT_UNSIGNED) {
+            if (op == TOK_SAR)
+                op = TOK_SHR;
+            else if (op == '/')
+                op = TOK_UDIV;
+            else if (op == '%')
+                op = TOK_UMOD;
+            else if (op == TOK_LT)
+                op = TOK_ULT;
+            else if (op == TOK_GT)
+                op = TOK_UGT;
+            else if (op == TOK_LE)
+                op = TOK_ULE;
+            else if (op == TOK_GE)
+                op = TOK_UGE;
+        }
+        vswap();
+        type1.t = t;
+        gen_cast(&type1);
+        vswap();
+        /* special case for shifts and long long: we keep the shift as
+           an integer */
+        if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL)
+            type1.t = VT_INT;
+        gen_cast(&type1);
+        if (is_float(t))
+            gen_opif(op);
+        else if ((t & VT_BTYPE) == VT_LLONG)
+            gen_opl(op);
+        else
+            gen_opic(op);
+        if (op >= TOK_ULT && op <= TOK_GT) {
+            /* relationnal op: the result is an int */
+            vtop->type.t = VT_INT;
+        } else {
+            vtop->type.t = t;
+        }
+    }
+}
+
+/* generic itof for unsigned long long case */
+void gen_cvt_itof1(int t)
+{
+    if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == 
+        (VT_LLONG | VT_UNSIGNED)) {
+
+        if (t == VT_FLOAT)
+            vpush_global_sym(&func_old_type, TOK___ulltof);
+        else if (t == VT_DOUBLE)
+            vpush_global_sym(&func_old_type, TOK___ulltod);
+        else
+            vpush_global_sym(&func_old_type, TOK___ulltold);
+        vrott(2);
+        gfunc_call(1);
+        vpushi(0);
+        vtop->r = REG_FRET;
+    } else {
+        gen_cvt_itof(t);
+    }
+}
+
+/* generic ftoi for unsigned long long case */
+void gen_cvt_ftoi1(int t)
+{
+    int st;
+
+    if (t == (VT_LLONG | VT_UNSIGNED)) {
+        /* not handled natively */
+        st = vtop->type.t & VT_BTYPE;
+        if (st == VT_FLOAT)
+            vpush_global_sym(&func_old_type, TOK___fixunssfdi);
+        else if (st == VT_DOUBLE)
+            vpush_global_sym(&func_old_type, TOK___fixunsdfdi);
+        else
+            vpush_global_sym(&func_old_type, TOK___fixunsxfdi);
+        vrott(2);
+        gfunc_call(1);
+        vpushi(0);
+        vtop->r = REG_IRET;
+        vtop->r2 = REG_LRET;
+    } else {
+        gen_cvt_ftoi(t);
+    }
+}
+
+/* force char or short cast */
+void force_charshort_cast(int t)
+{
+    int bits, dbt;
+    dbt = t & VT_BTYPE;
+    /* XXX: add optimization if lvalue : just change type and offset */
+    if (dbt == VT_BYTE)
+        bits = 8;
+    else
+        bits = 16;
+    if (t & VT_UNSIGNED) {
+        vpushi((1 << bits) - 1);
+        gen_op('&');
+    } else {
+        bits = 32 - bits;
+        vpushi(bits);
+        gen_op(TOK_SHL);
+        vpushi(bits);
+        gen_op(TOK_SAR);
+    }
+}
+
+/* cast 'vtop' to 'type'. Casting to bitfields is forbidden. */
+static void gen_cast(CType *type)
+{
+    int sbt, dbt, sf, df, c;
+
+    /* special delayed cast for char/short */
+    /* XXX: in some cases (multiple cascaded casts), it may still
+       be incorrect */
+    if (vtop->r & VT_MUSTCAST) {
+        vtop->r &= ~VT_MUSTCAST;
+        force_charshort_cast(vtop->type.t);
+    }
+
+    /* bitfields first get cast to ints */
+    if (vtop->type.t & VT_BITFIELD) {
+        gv(RC_INT);
+    }
+
+    dbt = type->t & (VT_BTYPE | VT_UNSIGNED);
+    sbt = vtop->type.t & (VT_BTYPE | VT_UNSIGNED);
+
+    if (sbt != dbt && !nocode_wanted) {
+        sf = is_float(sbt);
+        df = is_float(dbt);
+        c = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
+        if (sf && df) {
+            /* convert from fp to fp */
+            if (c) {
+                /* constant case: we can do it now */
+                /* XXX: in ISOC, cannot do it if error in convert */
+                if (dbt == VT_FLOAT && sbt == VT_DOUBLE) 
+                    vtop->c.f = (float)vtop->c.d;
+                else if (dbt == VT_FLOAT && sbt == VT_LDOUBLE) 
+                    vtop->c.f = (float)vtop->c.ld;
+                else if (dbt == VT_DOUBLE && sbt == VT_FLOAT) 
+                    vtop->c.d = (double)vtop->c.f;
+                else if (dbt == VT_DOUBLE && sbt == VT_LDOUBLE) 
+                    vtop->c.d = (double)vtop->c.ld;
+                else if (dbt == VT_LDOUBLE && sbt == VT_FLOAT) 
+                    vtop->c.ld = (long double)vtop->c.f;
+                else if (dbt == VT_LDOUBLE && sbt == VT_DOUBLE) 
+                    vtop->c.ld = (long double)vtop->c.d;
+            } else {
+                /* non constant case: generate code */
+                gen_cvt_ftof(dbt);
+            }
+        } else if (df) {
+            /* convert int to fp */
+            if (c) {
+                switch(sbt) {
+                case VT_LLONG | VT_UNSIGNED:
+                case VT_LLONG:
+                    /* XXX: add const cases for long long */
+                    goto do_itof;
+                case VT_INT | VT_UNSIGNED:
+                    switch(dbt) {
+                    case VT_FLOAT: vtop->c.f = (float)vtop->c.ui; break;
+                    case VT_DOUBLE: vtop->c.d = (double)vtop->c.ui; break;
+                    case VT_LDOUBLE: vtop->c.ld = (long double)vtop->c.ui; break;
+                    }
+                    break;
+                default:
+                    switch(dbt) {
+                    case VT_FLOAT: vtop->c.f = (float)vtop->c.i; break;
+                    case VT_DOUBLE: vtop->c.d = (double)vtop->c.i; break;
+                    case VT_LDOUBLE: vtop->c.ld = (long double)vtop->c.i; break;
+                    }
+                    break;
+                }
+            } else {
+            do_itof:
+#if !defined(TCC_TARGET_ARM)
+                gen_cvt_itof1(dbt);
+#else
+                gen_cvt_itof(dbt);
+#endif
+            }
+        } else if (sf) {
+            /* convert fp to int */
+            /* we handle char/short/etc... with generic code */
+            if (dbt != (VT_INT | VT_UNSIGNED) &&
+                dbt != (VT_LLONG | VT_UNSIGNED) &&
+                dbt != VT_LLONG)
+                dbt = VT_INT;
+            if (c) {
+                switch(dbt) {
+                case VT_LLONG | VT_UNSIGNED:
+                case VT_LLONG:
+                    /* XXX: add const cases for long long */
+                    goto do_ftoi;
+                case VT_INT | VT_UNSIGNED:
+                    switch(sbt) {
+                    case VT_FLOAT: vtop->c.ui = (unsigned int)vtop->c.d; break;
+                    case VT_DOUBLE: vtop->c.ui = (unsigned int)vtop->c.d; break;
+                    case VT_LDOUBLE: vtop->c.ui = (unsigned int)vtop->c.d; break;
+                    }
+                    break;
+                default:
+                    /* int case */
+                    switch(sbt) {
+                    case VT_FLOAT: vtop->c.i = (int)vtop->c.d; break;
+                    case VT_DOUBLE: vtop->c.i = (int)vtop->c.d; break;
+                    case VT_LDOUBLE: vtop->c.i = (int)vtop->c.d; break;
+                    }
+                    break;
+                }
+            } else {
+            do_ftoi:
+                gen_cvt_ftoi1(dbt);
+            }
+            if (dbt == VT_INT && (type->t & (VT_BTYPE | VT_UNSIGNED)) != dbt) {
+                /* additional cast for char/short/bool... */
+                vtop->type.t = dbt;
+                gen_cast(type);
+            }
+        } else if ((dbt & VT_BTYPE) == VT_LLONG) {
+            if ((sbt & VT_BTYPE) != VT_LLONG) {
+                /* scalar to long long */
+                if (c) {
+                    if (sbt == (VT_INT | VT_UNSIGNED))
+                        vtop->c.ll = vtop->c.ui;
+                    else
+                        vtop->c.ll = vtop->c.i;
+                } else {
+                    /* machine independent conversion */
+                    gv(RC_INT);
+                    /* generate high word */
+                    if (sbt == (VT_INT | VT_UNSIGNED)) {
+                        vpushi(0);
+                        gv(RC_INT);
+                    } else {
+                        gv_dup();
+                        vpushi(31);
+                        gen_op(TOK_SAR);
+                    }
+                    /* patch second register */
+                    vtop[-1].r2 = vtop->r;
+                    vpop();
+                }
+            }
+        } else if (dbt == VT_BOOL) {
+            /* scalar to bool */
+            vpushi(0);
+            gen_op(TOK_NE);
+        } else if ((dbt & VT_BTYPE) == VT_BYTE || 
+                   (dbt & VT_BTYPE) == VT_SHORT) {
+            force_charshort_cast(dbt);
+        } else if ((dbt & VT_BTYPE) == VT_INT) {
+            /* scalar to int */
+            if (sbt == VT_LLONG) {
+                /* from long long: just take low order word */
+                lexpand();
+                vpop();
+            } 
+            /* if lvalue and single word type, nothing to do because
+               the lvalue already contains the real type size (see
+               VT_LVAL_xxx constants) */
+        }
+    }
+    vtop->type = *type;
+}
+
+/* return type size. Put alignment at 'a' */
+static int type_size(CType *type, int *a)
+{
+    Sym *s;
+    int bt;
+
+    bt = type->t & VT_BTYPE;
+    if (bt == VT_STRUCT) {
+        /* struct/union */
+        s = type->ref;
+        *a = s->r;
+        return s->c;
+    } else if (bt == VT_PTR) {
+        if (type->t & VT_ARRAY) {
+            s = type->ref;
+            return type_size(&s->type, a) * s->c;
+        } else {
+            *a = PTR_SIZE;
+            return PTR_SIZE;
+        }
+    } else if (bt == VT_LDOUBLE) {
+        *a = LDOUBLE_ALIGN;
+        return LDOUBLE_SIZE;
+    } else if (bt == VT_DOUBLE || bt == VT_LLONG) {
+#ifdef TCC_TARGET_I386
+        *a = 4;
+#else
+        *a = 8;
+#endif
+        return 8;
+    } else if (bt == VT_INT || bt == VT_ENUM || bt == VT_FLOAT) {
+        *a = 4;
+        return 4;
+    } else if (bt == VT_SHORT) {
+        *a = 2;
+        return 2;
+    } else {
+        /* char, void, function, _Bool */
+        *a = 1;
+        return 1;
+    }
+}
+
+/* return the pointed type of t */
+static inline CType *pointed_type(CType *type)
+{
+    return &type->ref->type;
+}
+
+/* modify type so that its it is a pointer to type. */
+static void mk_pointer(CType *type)
+{
+    Sym *s;
+    s = sym_push(SYM_FIELD, type, 0, -1);
+    type->t = VT_PTR | (type->t & ~VT_TYPE);
+    type->ref = s;
+}
+
+/* compare function types. OLD functions match any new functions */
+static int is_compatible_func(CType *type1, CType *type2)
+{
+    Sym *s1, *s2;
+
+    s1 = type1->ref;
+    s2 = type2->ref;
+    if (!is_compatible_types(&s1->type, &s2->type))
+        return 0;
+    /* check func_call */
+    if (s1->r != s2->r)
+        return 0;
+    /* XXX: not complete */
+    if (s1->c == FUNC_OLD || s2->c == FUNC_OLD)
+        return 1;
+    if (s1->c != s2->c)
+        return 0;
+    while (s1 != NULL) {
+        if (s2 == NULL)
+            return 0;
+        if (!is_compatible_types(&s1->type, &s2->type))
+            return 0;
+        s1 = s1->next;
+        s2 = s2->next;
+    }
+    if (s2)
+        return 0;
+    return 1;
+}
+
+/* return true if type1 and type2 are exactly the same (including
+   qualifiers). 
+
+   - enums are not checked as gcc __builtin_types_compatible_p () 
+ */
+static int is_compatible_types(CType *type1, CType *type2)
+{
+    int bt1, t1, t2;
+
+    t1 = type1->t & VT_TYPE;
+    t2 = type2->t & VT_TYPE;
+    /* XXX: bitfields ? */
+    if (t1 != t2)
+        return 0;
+    /* test more complicated cases */
+    bt1 = t1 & VT_BTYPE;
+    if (bt1 == VT_PTR) {
+        type1 = pointed_type(type1);
+        type2 = pointed_type(type2);
+        return is_compatible_types(type1, type2);
+    } else if (bt1 == VT_STRUCT) {
+        return (type1->ref == type2->ref);
+    } else if (bt1 == VT_FUNC) {
+        return is_compatible_func(type1, type2);
+    } else {
+        return 1;
+    }
+}
+
+/* print a type. If 'varstr' is not NULL, then the variable is also
+   printed in the type */
+/* XXX: union */
+/* XXX: add array and function pointers */
+void type_to_str(char *buf, int buf_size, 
+                 CType *type, const char *varstr)
+{
+    int bt, v, t;
+    Sym *s, *sa;
+    char buf1[256];
+    const char *tstr;
+
+    t = type->t & VT_TYPE;
+    bt = t & VT_BTYPE;
+    buf[0] = '\0';
+    if (t & VT_CONSTANT)
+        pstrcat(buf, buf_size, "const ");
+    if (t & VT_VOLATILE)
+        pstrcat(buf, buf_size, "volatile ");
+    if (t & VT_UNSIGNED)
+        pstrcat(buf, buf_size, "unsigned ");
+    switch(bt) {
+    case VT_VOID:
+        tstr = "void";
+        goto add_tstr;
+    case VT_BOOL:
+        tstr = "_Bool";
+        goto add_tstr;
+    case VT_BYTE:
+        tstr = "char";
+        goto add_tstr;
+    case VT_SHORT:
+        tstr = "short";
+        goto add_tstr;
+    case VT_INT:
+        tstr = "int";
+        goto add_tstr;
+    case VT_LONG:
+        tstr = "long";
+        goto add_tstr;
+    case VT_LLONG:
+        tstr = "long long";
+        goto add_tstr;
+    case VT_FLOAT:
+        tstr = "float";
+        goto add_tstr;
+    case VT_DOUBLE:
+        tstr = "double";
+        goto add_tstr;
+    case VT_LDOUBLE:
+        tstr = "long double";
+    add_tstr:
+        pstrcat(buf, buf_size, tstr);
+        break;
+    case VT_ENUM:
+    case VT_STRUCT:
+        if (bt == VT_STRUCT)
+            tstr = "struct ";
+        else
+            tstr = "enum ";
+        pstrcat(buf, buf_size, tstr);
+        v = type->ref->v & ~SYM_STRUCT;
+        if (v >= SYM_FIRST_ANOM)
+            pstrcat(buf, buf_size, "<anonymous>");
+        else
+            pstrcat(buf, buf_size, get_tok_str(v, NULL));
+        break;
+    case VT_FUNC:
+        s = type->ref;
+        type_to_str(buf, buf_size, &s->type, varstr);
+        pstrcat(buf, buf_size, "(");
+        sa = s->next;
+        while (sa != NULL) {
+            type_to_str(buf1, sizeof(buf1), &sa->type, NULL);
+            pstrcat(buf, buf_size, buf1);
+            sa = sa->next;
+            if (sa)
+                pstrcat(buf, buf_size, ", ");
+        }
+        pstrcat(buf, buf_size, ")");
+        goto no_var;
+    case VT_PTR:
+        s = type->ref;
+        pstrcpy(buf1, sizeof(buf1), "*");
+        if (varstr)
+            pstrcat(buf1, sizeof(buf1), varstr);
+        type_to_str(buf, buf_size, &s->type, buf1);
+        goto no_var;
+    }
+    if (varstr) {
+        pstrcat(buf, buf_size, " ");
+        pstrcat(buf, buf_size, varstr);
+    }
+ no_var: ;
+}
+
+/* verify type compatibility to store vtop in 'dt' type, and generate
+   casts if needed. */
+static void gen_assign_cast(CType *dt)
+{
+    CType *st, *type1, *type2, tmp_type1, tmp_type2;
+    char buf1[256], buf2[256];
+    int dbt, sbt;
+
+    st = &vtop->type; /* source type */
+    dbt = dt->t & VT_BTYPE;
+    sbt = st->t & VT_BTYPE;
+    if (dt->t & VT_CONSTANT)
+        warning("assignment of read-only location");
+    switch(dbt) {
+    case VT_PTR:
+        /* special cases for pointers */
+        /* '0' can also be a pointer */
+        if (is_null_pointer(vtop))
+            goto type_ok;
+        /* accept implicit pointer to integer cast with warning */
+        if (is_integer_btype(sbt)) {
+            warning("assignment makes pointer from integer without a cast");
+            goto type_ok;
+        }
+        type1 = pointed_type(dt);
+        /* a function is implicitely a function pointer */
+        if (sbt == VT_FUNC) {
+            if ((type1->t & VT_BTYPE) != VT_VOID &&
+                !is_compatible_types(pointed_type(dt), st))
+                goto error;
+            else
+                goto type_ok;
+        }
+        if (sbt != VT_PTR)
+            goto error;
+        type2 = pointed_type(st);
+        if ((type1->t & VT_BTYPE) == VT_VOID || 
+            (type2->t & VT_BTYPE) == VT_VOID) {
+            /* void * can match anything */
+        } else {
+            /* exact type match, except for unsigned */
+            tmp_type1 = *type1;
+            tmp_type2 = *type2;
+            tmp_type1.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE);
+            tmp_type2.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE);
+            if (!is_compatible_types(&tmp_type1, &tmp_type2))
+                goto error;
+        }
+        /* check const and volatile */
+        if ((!(type1->t & VT_CONSTANT) && (type2->t & VT_CONSTANT)) ||
+            (!(type1->t & VT_VOLATILE) && (type2->t & VT_VOLATILE)))
+            warning("assignment discards qualifiers from pointer target type");
+        break;
+    case VT_BYTE:
+    case VT_SHORT:
+    case VT_INT:
+    case VT_LLONG:
+        if (sbt == VT_PTR || sbt == VT_FUNC) {
+            warning("assignment makes integer from pointer without a cast");
+        }
+        /* XXX: more tests */
+        break;
+    case VT_STRUCT:
+        tmp_type1 = *dt;
+        tmp_type2 = *st;
+        tmp_type1.t &= ~(VT_CONSTANT | VT_VOLATILE);
+        tmp_type2.t &= ~(VT_CONSTANT | VT_VOLATILE);
+        if (!is_compatible_types(&tmp_type1, &tmp_type2)) {
+        error:
+            type_to_str(buf1, sizeof(buf1), st, NULL);
+            type_to_str(buf2, sizeof(buf2), dt, NULL);
+            error("cannot cast '%s' to '%s'", buf1, buf2);
+        }
+        break;
+    }
+ type_ok:
+    gen_cast(dt);
+}
+
+/* store vtop in lvalue pushed on stack */
+void vstore(void)
+{
+    int sbt, dbt, ft, r, t, size, align, bit_size, bit_pos, rc, delayed_cast;
+
+    ft = vtop[-1].type.t;
+    sbt = vtop->type.t & VT_BTYPE;
+    dbt = ft & VT_BTYPE;
+    if (((sbt == VT_INT || sbt == VT_SHORT) && dbt == VT_BYTE) ||
+        (sbt == VT_INT && dbt == VT_SHORT)) {
+        /* optimize char/short casts */
+        delayed_cast = VT_MUSTCAST;
+        vtop->type.t = ft & VT_TYPE;
+        /* XXX: factorize */
+        if (ft & VT_CONSTANT)
+            warning("assignment of read-only location");
+    } else {
+        delayed_cast = 0;
+        if (!(ft & VT_BITFIELD))
+            gen_assign_cast(&vtop[-1].type);
+    }
+
+    if (sbt == VT_STRUCT) {
+        /* if structure, only generate pointer */
+        /* structure assignment : generate memcpy */
+        /* XXX: optimize if small size */
+        if (!nocode_wanted) {
+            size = type_size(&vtop->type, &align);
+
+            vpush_global_sym(&func_old_type, TOK_memcpy);
+
+            /* destination */
+            vpushv(vtop - 2);
+            vtop->type.t = VT_INT;
+            gaddrof();
+            /* source */
+            vpushv(vtop - 2);
+            vtop->type.t = VT_INT;
+            gaddrof();
+            /* type size */
+            vpushi(size);
+            gfunc_call(3);
+            
+            vswap();
+            vpop();
+        } else {
+            vswap();
+            vpop();
+        }
+        /* leave source on stack */
+    } else if (ft & VT_BITFIELD) {
+        /* bitfield store handling */
+        bit_pos = (ft >> VT_STRUCT_SHIFT) & 0x3f;
+        bit_size = (ft >> (VT_STRUCT_SHIFT + 6)) & 0x3f;
+        /* remove bit field info to avoid loops */
+        vtop[-1].type.t = ft & ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT));
+
+        /* duplicate destination */
+        vdup();
+        vtop[-1] = vtop[-2];
+
+        /* mask and shift source */
+        vpushi((1 << bit_size) - 1);
+        gen_op('&');
+        vpushi(bit_pos);
+        gen_op(TOK_SHL);
+        /* load destination, mask and or with source */
+        vswap();
+        vpushi(~(((1 << bit_size) - 1) << bit_pos));
+        gen_op('&');
+        gen_op('|');
+        /* store result */
+        vstore();
+    } else {
+#ifdef CONFIG_TCC_BCHECK
+        /* bound check case */
+        if (vtop[-1].r & VT_MUSTBOUND) {
+            vswap();
+            gbound();
+            vswap();
+        }
+#endif
+        if (!nocode_wanted) {
+            rc = RC_INT;
+            if (is_float(ft))
+                rc = RC_FLOAT;
+            r = gv(rc);  /* generate value */
+            /* if lvalue was saved on stack, must read it */
+            if ((vtop[-1].r & VT_VALMASK) == VT_LLOCAL) {
+                SValue sv;
+                t = get_reg(RC_INT);
+                sv.type.t = VT_INT;
+                sv.r = VT_LOCAL | VT_LVAL;
+                sv.c.ul = vtop[-1].c.ul;
+                load(t, &sv);
+                vtop[-1].r = t | VT_LVAL;
+            }
+            store(r, vtop - 1);
+            /* two word case handling : store second register at word + 4 */
+            if ((ft & VT_BTYPE) == VT_LLONG) {
+                vswap();
+                /* convert to int to increment easily */
+                vtop->type.t = VT_INT;
+                gaddrof();
+                vpushi(4);
+                gen_op('+');
+                vtop->r |= VT_LVAL;
+                vswap();
+                /* XXX: it works because r2 is spilled last ! */
+                store(vtop->r2, vtop - 1);
+            }
+        }
+        vswap();
+        vtop--; /* NOT vpop() because on x86 it would flush the fp stack */
+        vtop->r |= delayed_cast;
+    }
+}
+
+/* post defines POST/PRE add. c is the token ++ or -- */
+void inc(int post, int c)
+{
+    test_lvalue();
+    vdup(); /* save lvalue */
+    if (post) {
+        gv_dup(); /* duplicate value */
+        vrotb(3);
+        vrotb(3);
+    }
+    /* add constant */
+    vpushi(c - TOK_MID); 
+    gen_op('+');
+    vstore(); /* store value */
+    if (post)
+        vpop(); /* if post op, return saved value */
+}
+
+/* Parse GNUC __attribute__ extension. Currently, the following
+   extensions are recognized:
+   - aligned(n) : set data/function alignment.
+   - packed : force data alignment to 1
+   - section(x) : generate data/code in this section.
+   - unused : currently ignored, but may be used someday.
+   - regparm(n) : pass function parameters in registers (i386 only)
+ */
+static void parse_attribute(AttributeDef *ad)
+{
+    int t, n;
+    
+    while (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) {
+    next();
+    skip('(');
+    skip('(');
+    while (tok != ')') {
+        if (tok < TOK_IDENT)
+            expect("attribute name");
+        t = tok;
+        next();
+        switch(t) {
+        case TOK_SECTION1:
+        case TOK_SECTION2:
+            skip('(');
+            if (tok != TOK_STR)
+                expect("section name");
+            ad->section = find_section(tcc_state, (char *)tokc.cstr->data);
+            next();
+            skip(')');
+            break;
+        case TOK_ALIGNED1:
+        case TOK_ALIGNED2:
+            if (tok == '(') {
+                next();
+                n = expr_const();
+                if (n <= 0 || (n & (n - 1)) != 0) 
+                    error("alignment must be a positive power of two");
+                skip(')');
+            } else {
+                n = MAX_ALIGN;
+            }
+            ad->aligned = n;
+            break;
+        case TOK_PACKED1:
+        case TOK_PACKED2:
+            ad->packed = 1;
+            break;
+        case TOK_UNUSED1:
+        case TOK_UNUSED2:
+            /* currently, no need to handle it because tcc does not
+               track unused objects */
+            break;
+        case TOK_NORETURN1:
+        case TOK_NORETURN2:
+            /* currently, no need to handle it because tcc does not
+               track unused objects */
+            break;
+        case TOK_CDECL1:
+        case TOK_CDECL2:
+        case TOK_CDECL3:
+            ad->func_call = FUNC_CDECL;
+            break;
+        case TOK_STDCALL1:
+        case TOK_STDCALL2:
+        case TOK_STDCALL3:
+            ad->func_call = FUNC_STDCALL;
+            break;
+#ifdef TCC_TARGET_I386
+        case TOK_REGPARM1:
+        case TOK_REGPARM2:
+            skip('(');
+            n = expr_const();
+            if (n > 3) 
+                n = 3;
+            else if (n < 0)
+                n = 0;
+            if (n > 0)
+                ad->func_call = FUNC_FASTCALL1 + n - 1;
+            skip(')');
+            break;
+#endif
+        case TOK_DLLEXPORT:
+            ad->dllexport = 1;
+            break;
+        default:
+            if (tcc_state->warn_unsupported)
+                warning("'%s' attribute ignored", get_tok_str(t, NULL));
+            /* skip parameters */
+            /* XXX: skip parenthesis too */
+            if (tok == '(') {
+                next();
+                while (tok != ')' && tok != -1)
+                    next();
+                next();
+            }
+            break;
+        }
+        if (tok != ',')
+            break;
+        next();
+    }
+    skip(')');
+    skip(')');
+    }
+}
+
+/* enum/struct/union declaration. u is either VT_ENUM or VT_STRUCT */
+static void struct_decl(CType *type, int u)
+{
+    int a, v, size, align, maxalign, c, offset;
+    int bit_size, bit_pos, bsize, bt, lbit_pos;
+    Sym *s, *ss, **ps;
+    AttributeDef ad;
+    CType type1, btype;
+
+    a = tok; /* save decl type */
+    next();
+    if (tok != '{') {
+        v = tok;
+        next();
+        /* struct already defined ? return it */
+        if (v < TOK_IDENT)
+            expect("struct/union/enum name");
+        s = struct_find(v);
+        if (s) {
+            if (s->type.t != a)
+                error("invalid type");
+            goto do_decl;
+        }
+    } else {
+        v = anon_sym++;
+    }
+    type1.t = a;
+    /* we put an undefined size for struct/union */
+    s = sym_push(v | SYM_STRUCT, &type1, 0, -1);
+    s->r = 0; /* default alignment is zero as gcc */
+    /* put struct/union/enum name in type */
+ do_decl:
+    type->t = u;
+    type->ref = s;
+    
+    if (tok == '{') {
+        next();
+        if (s->c != -1)
+            error("struct/union/enum already defined");
+        /* cannot be empty */
+        c = 0;
+        /* non empty enums are not allowed */
+        if (a == TOK_ENUM) {
+            for(;;) {
+                v = tok;
+                if (v < TOK_UIDENT)
+                    expect("identifier");
+                next();
+                if (tok == '=') {
+                    next();
+                    c = expr_const();
+                }
+                /* enum symbols have static storage */
+                ss = sym_push(v, &int_type, VT_CONST, c);
+                ss->type.t |= VT_STATIC;
+                if (tok != ',')
+                    break;
+                next();
+                c++;
+                /* NOTE: we accept a trailing comma */
+                if (tok == '}')
+                    break;
+            }
+            skip('}');
+        } else {
+            maxalign = 1;
+            ps = &s->next;
+            bit_pos = 0;
+            offset = 0;
+            while (tok != '}') {
+                parse_btype(&btype, &ad);
+                while (1) {
+                    bit_size = -1;
+                    v = 0;
+                    type1 = btype;
+                    if (tok != ':') {
+                        type_decl(&type1, &ad, &v, TYPE_DIRECT);
+                        if ((type1.t & VT_BTYPE) == VT_FUNC ||
+                            (type1.t & (VT_TYPEDEF | VT_STATIC | VT_EXTERN | VT_INLINE)))
+                            error("invalid type for '%s'", 
+                                  get_tok_str(v, NULL));
+                    }
+                    if (tok == ':') {
+                        next();
+                        bit_size = expr_const();
+                        /* XXX: handle v = 0 case for messages */
+                        if (bit_size < 0)
+                            error("negative width in bit-field '%s'", 
+                                  get_tok_str(v, NULL));
+                        if (v && bit_size == 0)
+                            error("zero width for bit-field '%s'", 
+                                  get_tok_str(v, NULL));
+                    }
+                    size = type_size(&type1, &align);
+                    if (ad.aligned) {
+                        if (align < ad.aligned)
+                            align = ad.aligned;
+                    } else if (ad.packed) {
+                        align = 1;
+                    } else if (*tcc_state->pack_stack_ptr) {
+                        if (align > *tcc_state->pack_stack_ptr)
+                            align = *tcc_state->pack_stack_ptr;
+                    }
+                    lbit_pos = 0;
+                    if (bit_size >= 0) {
+                        bt = type1.t & VT_BTYPE;
+                        if (bt != VT_INT && 
+                            bt != VT_BYTE && 
+                            bt != VT_SHORT &&
+                            bt != VT_BOOL &&
+                            bt != VT_ENUM)
+                            error("bitfields must have scalar type");
+                        bsize = size * 8;
+                        if (bit_size > bsize) {
+                            error("width of '%s' exceeds its type",
+                                  get_tok_str(v, NULL));
+                        } else if (bit_size == bsize) {
+                            /* no need for bit fields */
+                            bit_pos = 0;
+                        } else if (bit_size == 0) {
+                            /* XXX: what to do if only padding in a
+                               structure ? */
+                            /* zero size: means to pad */
+                            if (bit_pos > 0)
+                                bit_pos = bsize;
+                        } else {
+                            /* we do not have enough room ? */
+                            if ((bit_pos + bit_size) > bsize)
+                                bit_pos = 0;
+                            lbit_pos = bit_pos;
+                            /* XXX: handle LSB first */
+                            type1.t |= VT_BITFIELD | 
+                                (bit_pos << VT_STRUCT_SHIFT) |
+                                (bit_size << (VT_STRUCT_SHIFT + 6));
+                            bit_pos += bit_size;
+                        }
+                    } else {
+                        bit_pos = 0;
+                    }
+                    if (v) {
+                        /* add new memory data only if starting
+                           bit field */
+                        if (lbit_pos == 0) {
+                            if (a == TOK_STRUCT) {
+                                c = (c + align - 1) & -align;
+                                offset = c;
+                                c += size;
+                            } else {
+                                offset = 0;
+                                if (size > c)
+                                    c = size;
+                            }
+                            if (align > maxalign)
+                                maxalign = align;
+                        }
+#if 0
+                        printf("add field %s offset=%d", 
+                               get_tok_str(v, NULL), offset);
+                        if (type1.t & VT_BITFIELD) {
+                            printf(" pos=%d size=%d", 
+                                   (type1.t >> VT_STRUCT_SHIFT) & 0x3f,
+                                   (type1.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f);
+                        }
+                        printf("\n");
+#endif
+                        ss = sym_push(v | SYM_FIELD, &type1, 0, offset);
+                        *ps = ss;
+                        ps = &ss->next;
+                    }
+                    if (tok == ';' || tok == TOK_EOF)
+                        break;
+                    skip(',');
+                }
+                skip(';');
+            }
+            skip('}');
+            /* store size and alignment */
+            s->c = (c + maxalign - 1) & -maxalign; 
+            s->r = maxalign;
+        }
+    }
+}
+
+/* return 0 if no type declaration. otherwise, return the basic type
+   and skip it. 
+ */
+static int parse_btype(CType *type, AttributeDef *ad)
+{
+    int t, u, type_found, typespec_found;
+    Sym *s;
+    CType type1;
+
+    memset(ad, 0, sizeof(AttributeDef));
+    type_found = 0;
+    typespec_found = 0;
+    t = 0;
+    while(1) {
+        switch(tok) {
+        case TOK_EXTENSION:
+            /* currently, we really ignore extension */
+            next();
+            continue;
+
+            /* basic types */
+        case TOK_CHAR:
+            u = VT_BYTE;
+        basic_type:
+            next();
+        basic_type1:
+            if ((t & VT_BTYPE) != 0)
+                error("too many basic types");
+            t |= u;
+            typespec_found = 1;
+            break;
+        case TOK_VOID:
+            u = VT_VOID;
+            goto basic_type;
+        case TOK_SHORT:
+            u = VT_SHORT;
+            goto basic_type;
+        case TOK_INT:
+            next();
+            typespec_found = 1;
+            break;
+        case TOK_LONG:
+            next();
+            if ((t & VT_BTYPE) == VT_DOUBLE) {
+                t = (t & ~VT_BTYPE) | VT_LDOUBLE;
+            } else if ((t & VT_BTYPE) == VT_LONG) {
+                t = (t & ~VT_BTYPE) | VT_LLONG;
+            } else {
+                u = VT_LONG;
+                goto basic_type1;
+            }
+            break;
+        case TOK_BOOL:
+            u = VT_BOOL;
+            goto basic_type;
+        case TOK_FLOAT:
+            u = VT_FLOAT;
+            goto basic_type;
+        case TOK_DOUBLE:
+            next();
+            if ((t & VT_BTYPE) == VT_LONG) {
+                t = (t & ~VT_BTYPE) | VT_LDOUBLE;
+            } else {
+                u = VT_DOUBLE;
+                goto basic_type1;
+            }
+            break;
+        case TOK_ENUM:
+            struct_decl(&type1, VT_ENUM);
+        basic_type2:
+            u = type1.t;
+            type->ref = type1.ref;
+            goto basic_type1;
+        case TOK_STRUCT:
+        case TOK_UNION:
+            struct_decl(&type1, VT_STRUCT);
+            goto basic_type2;
+
+            /* type modifiers */
+        case TOK_CONST1:
+        case TOK_CONST2:
+        case TOK_CONST3:
+            t |= VT_CONSTANT;
+            next();
+            break;
+        case TOK_VOLATILE1:
+        case TOK_VOLATILE2:
+        case TOK_VOLATILE3:
+            t |= VT_VOLATILE;
+            next();
+            break;
+        case TOK_SIGNED1:
+        case TOK_SIGNED2:
+        case TOK_SIGNED3:
+            typespec_found = 1;
+           t |= VT_SIGNED;
+           next();
+           break;
+        case TOK_REGISTER:
+        case TOK_AUTO:
+        case TOK_RESTRICT1:
+        case TOK_RESTRICT2:
+        case TOK_RESTRICT3:
+            next();
+            break;
+        case TOK_UNSIGNED:
+            t |= VT_UNSIGNED;
+            next();
+            typespec_found = 1;
+            break;
+
+            /* storage */
+        case TOK_EXTERN:
+            t |= VT_EXTERN;
+            next();
+            break;
+        case TOK_STATIC:
+            t |= VT_STATIC;
+            next();
+            break;
+        case TOK_TYPEDEF:
+            t |= VT_TYPEDEF;
+            next();
+            break;
+        case TOK_INLINE1:
+        case TOK_INLINE2:
+        case TOK_INLINE3:
+            t |= VT_INLINE;
+            next();
+            break;
+
+            /* GNUC attribute */
+        case TOK_ATTRIBUTE1:
+        case TOK_ATTRIBUTE2:
+            parse_attribute(ad);
+            break;
+            /* GNUC typeof */
+        case TOK_TYPEOF1:
+        case TOK_TYPEOF2:
+        case TOK_TYPEOF3:
+            next();
+            parse_expr_type(&type1);
+            goto basic_type2;
+        default:
+            if (typespec_found)
+                goto the_end;
+            s = sym_find(tok);
+            if (!s || !(s->type.t & VT_TYPEDEF))
+                goto the_end;
+            t |= (s->type.t & ~VT_TYPEDEF);
+            type->ref = s->type.ref;
+            next();
+            break;
+        }
+        type_found = 1;
+    }
+the_end:
+    if ((t & (VT_SIGNED|VT_UNSIGNED)) == (VT_SIGNED|VT_UNSIGNED))
+      error("signed and unsigned modifier");
+    if (tcc_state->char_is_unsigned) {
+        if ((t & (VT_SIGNED|VT_UNSIGNED|VT_BTYPE)) == VT_BYTE)
+            t |= VT_UNSIGNED;
+    }
+    t &= ~VT_SIGNED;
+
+    /* long is never used as type */
+    if ((t & VT_BTYPE) == VT_LONG)
+        t = (t & ~VT_BTYPE) | VT_INT;
+    type->t = t;
+    return type_found;
+}
+
+/* convert a function parameter type (array to pointer and function to
+   function pointer) */
+static inline void convert_parameter_type(CType *pt)
+{
+    /* remove const and volatile qualifiers (XXX: const could be used
+       to indicate a const function parameter */
+    pt->t &= ~(VT_CONSTANT | VT_VOLATILE);
+    /* array must be transformed to pointer according to ANSI C */
+    pt->t &= ~VT_ARRAY;
+    if ((pt->t & VT_BTYPE) == VT_FUNC) {
+        mk_pointer(pt);
+    }
+}
+
+static void post_type(CType *type, AttributeDef *ad)
+{
+    int n, l, t1;
+    Sym **plast, *s, *first;
+    AttributeDef ad1;
+    CType pt;
+
+    if (tok == '(') {
+        /* function declaration */
+        next();
+        l = 0;
+        first = NULL;
+        plast = &first;
+        while (tok != ')') {
+            /* read param name and compute offset */
+            if (l != FUNC_OLD) {
+                if (!parse_btype(&pt, &ad1)) {
+                    if (l) {
+                        error("invalid type");
+                    } else {
+                        l = FUNC_OLD;
+                        goto old_proto;
+                    }
+                }
+                l = FUNC_NEW;
+                if ((pt.t & VT_BTYPE) == VT_VOID && tok == ')')
+                    break;
+                type_decl(&pt, &ad1, &n, TYPE_DIRECT | TYPE_ABSTRACT);
+                if ((pt.t & VT_BTYPE) == VT_VOID)
+                    error("parameter declared as void");
+            } else {
+            old_proto:
+                n = tok;
+                pt.t = VT_INT;
+                next();
+            }
+            convert_parameter_type(&pt);
+            s = sym_push(n | SYM_FIELD, &pt, 0, 0);
+            *plast = s;
+            plast = &s->next;
+            if (tok == ',') {
+                next();
+                if (l == FUNC_NEW && tok == TOK_DOTS) {
+                    l = FUNC_ELLIPSIS;
+                    next();
+                    break;
+                }
+            }
+        }
+        /* if no parameters, then old type prototype */
+        if (l == 0)
+            l = FUNC_OLD;
+        skip(')');
+        t1 = type->t & VT_STORAGE;
+        /* NOTE: const is ignored in returned type as it has a special
+           meaning in gcc / C++ */
+        type->t &= ~(VT_STORAGE | VT_CONSTANT); 
+        post_type(type, ad);
+        /* we push a anonymous symbol which will contain the function prototype */
+        s = sym_push(SYM_FIELD, type, ad->func_call, l);
+        s->next = first;
+        type->t = t1 | VT_FUNC;
+        type->ref = s;
+    } else if (tok == '[') {
+        /* array definition */
+        next();
+        n = -1;
+        if (tok != ']') {
+            n = expr_const();
+            if (n < 0)
+                error("invalid array size");    
+        }
+        skip(']');
+        /* parse next post type */
+        t1 = type->t & VT_STORAGE;
+        type->t &= ~VT_STORAGE;
+        post_type(type, ad);
+        
+        /* we push a anonymous symbol which will contain the array
+           element type */
+        s = sym_push(SYM_FIELD, type, 0, n);
+        type->t = t1 | VT_ARRAY | VT_PTR;
+        type->ref = s;
+    }
+}
+
+/* Parse a type declaration (except basic type), and return the type
+   in 'type'. 'td' is a bitmask indicating which kind of type decl is
+   expected. 'type' should contain the basic type. 'ad' is the
+   attribute definition of the basic type. It can be modified by
+   type_decl(). 
+ */
+static void type_decl(CType *type, AttributeDef *ad, int *v, int td)
+{
+    Sym *s;
+    CType type1, *type2;
+    int qualifiers;
+    
+    while (tok == '*') {
+        qualifiers = 0;
+    redo:
+        next();
+        switch(tok) {
+        case TOK_CONST1:
+        case TOK_CONST2:
+        case TOK_CONST3:
+            qualifiers |= VT_CONSTANT;
+            goto redo;
+        case TOK_VOLATILE1:
+        case TOK_VOLATILE2:
+        case TOK_VOLATILE3:
+            qualifiers |= VT_VOLATILE;
+            goto redo;
+        case TOK_RESTRICT1:
+        case TOK_RESTRICT2:
+        case TOK_RESTRICT3:
+            goto redo;
+        }
+        mk_pointer(type);
+        type->t |= qualifiers;
+    }
+    
+    /* XXX: clarify attribute handling */
+    if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
+        parse_attribute(ad);
+
+    /* recursive type */
+    /* XXX: incorrect if abstract type for functions (e.g. 'int ()') */
+    type1.t = 0; /* XXX: same as int */
+    if (tok == '(') {
+        next();
+        /* XXX: this is not correct to modify 'ad' at this point, but
+           the syntax is not clear */
+        if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
+            parse_attribute(ad);
+        type_decl(&type1, ad, v, td);
+        skip(')');
+    } else {
+        /* type identifier */
+        if (tok >= TOK_IDENT && (td & TYPE_DIRECT)) {
+            *v = tok;
+            next();
+        } else {
+            if (!(td & TYPE_ABSTRACT))
+                expect("identifier");
+            *v = 0;
+        }
+    }
+    post_type(type, ad);
+    if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
+        parse_attribute(ad);
+    if (!type1.t)
+        return;
+    /* append type at the end of type1 */
+    type2 = &type1;
+    for(;;) {
+        s = type2->ref;
+        type2 = &s->type;
+        if (!type2->t) {
+            *type2 = *type;
+            break;
+        }
+    }
+    *type = type1;
+}
+
+/* compute the lvalue VT_LVAL_xxx needed to match type t. */
+static int lvalue_type(int t)
+{
+    int bt, r;
+    r = VT_LVAL;
+    bt = t & VT_BTYPE;
+    if (bt == VT_BYTE || bt == VT_BOOL)
+        r |= VT_LVAL_BYTE;
+    else if (bt == VT_SHORT)
+        r |= VT_LVAL_SHORT;
+    else
+        return r;
+    if (t & VT_UNSIGNED)
+        r |= VT_LVAL_UNSIGNED;
+    return r;
+}
+
+/* indirection with full error checking and bound check */
+static void indir(void)
+{
+    if ((vtop->type.t & VT_BTYPE) != VT_PTR)
+        expect("pointer");
+    if ((vtop->r & VT_LVAL) && !nocode_wanted)
+        gv(RC_INT);
+    vtop->type = *pointed_type(&vtop->type);
+    /* an array is never an lvalue */
+    if (!(vtop->type.t & VT_ARRAY)) {
+        vtop->r |= lvalue_type(vtop->type.t);
+        /* if bound checking, the referenced pointer must be checked */
+        if (do_bounds_check) 
+            vtop->r |= VT_MUSTBOUND;
+    }
+}
+
+/* pass a parameter to a function and do type checking and casting */
+static void gfunc_param_typed(Sym *func, Sym *arg)
+{
+    int func_type;
+    CType type;
+
+    func_type = func->c;
+    if (func_type == FUNC_OLD ||
+        (func_type == FUNC_ELLIPSIS && arg == NULL)) {
+        /* default casting : only need to convert float to double */
+        if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) {
+            type.t = VT_DOUBLE;
+            gen_cast(&type);
+        }
+    } else if (arg == NULL) {
+        error("too many arguments to function");
+    } else {
+        type = arg->type;
+        type.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */
+        gen_assign_cast(&type);
+    }
+}
+
+/* parse an expression of the form '(type)' or '(expr)' and return its
+   type */
+static void parse_expr_type(CType *type)
+{
+    int n;
+    AttributeDef ad;
+
+    skip('(');
+    if (parse_btype(type, &ad)) {
+        type_decl(type, &ad, &n, TYPE_ABSTRACT);
+    } else {
+        expr_type(type);
+    }
+    skip(')');
+}
+
+static void parse_type(CType *type)
+{
+    AttributeDef ad;
+    int n;
+
+    if (!parse_btype(type, &ad)) {
+        expect("type");
+    }
+    type_decl(type, &ad, &n, TYPE_ABSTRACT);
+}
+
+static void vpush_tokc(int t)
+{
+    CType type;
+    type.t = t;
+    vsetc(&type, VT_CONST, &tokc);
+}
+
+static void unary(void)
+{
+    int n, t, align, size, r;
+    CType type;
+    Sym *s;
+    AttributeDef ad;
+
+    /* XXX: GCC 2.95.3 does not generate a table although it should be
+       better here */
+ tok_next:
+    switch(tok) {
+    case TOK_EXTENSION:
+        next();
+        goto tok_next;
+    case TOK_CINT:
+    case TOK_CCHAR: 
+    case TOK_LCHAR:
+        vpushi(tokc.i);
+        next();
+        break;
+    case TOK_CUINT:
+        vpush_tokc(VT_INT | VT_UNSIGNED);
+        next();
+        break;
+    case TOK_CLLONG:
+        vpush_tokc(VT_LLONG);
+        next();
+        break;
+    case TOK_CULLONG:
+        vpush_tokc(VT_LLONG | VT_UNSIGNED);
+        next();
+        break;
+    case TOK_CFLOAT:
+        vpush_tokc(VT_FLOAT);
+        next();
+        break;
+    case TOK_CDOUBLE:
+        vpush_tokc(VT_DOUBLE);
+        next();
+        break;
+    case TOK_CLDOUBLE:
+        vpush_tokc(VT_LDOUBLE);
+        next();
+        break;
+    case TOK___FUNCTION__:
+        if (!gnu_ext)
+            goto tok_identifier;
+        /* fall thru */
+    case TOK___FUNC__:
+        {
+            void *ptr;
+            int len;
+            /* special function name identifier */
+            len = strlen(funcname) + 1;
+            /* generate char[len] type */
+            type.t = VT_BYTE;
+            mk_pointer(&type);
+            type.t |= VT_ARRAY;
+            type.ref->c = len;
+            vpush_ref(&type, data_section, data_section->data_offset, len);
+            ptr = section_ptr_add(data_section, len);
+            memcpy(ptr, funcname, len);
+            next();
+        }
+        break;
+    case TOK_LSTR:
+        t = VT_INT;
+        goto str_init;
+    case TOK_STR:
+        /* string parsing */
+        t = VT_BYTE;
+    str_init:
+        if (tcc_state->warn_write_strings)
+            t |= VT_CONSTANT;
+        type.t = t;
+        mk_pointer(&type);
+        type.t |= VT_ARRAY;
+        memset(&ad, 0, sizeof(AttributeDef));
+        decl_initializer_alloc(&type, &ad, VT_CONST, 2, 0, 0);
+        break;
+    case '(':
+        next();
+        /* cast ? */
+        if (parse_btype(&type, &ad)) {
+            type_decl(&type, &ad, &n, TYPE_ABSTRACT);
+            skip(')');
+            /* check ISOC99 compound literal */
+            if (tok == '{') {
+                    /* data is allocated locally by default */
+                if (global_expr)
+                    r = VT_CONST;
+                else
+                    r = VT_LOCAL;
+                /* all except arrays are lvalues */
+                if (!(type.t & VT_ARRAY))
+                    r |= lvalue_type(type.t);
+                memset(&ad, 0, sizeof(AttributeDef));
+                decl_initializer_alloc(&type, &ad, r, 1, 0, 0);
+            } else {
+                unary();
+                gen_cast(&type);
+            }
+        } else if (tok == '{') {
+            /* save all registers */
+            save_regs(0); 
+            /* statement expression : we do not accept break/continue
+               inside as GCC does */
+            block(NULL, NULL, NULL, NULL, 0, 1);
+            skip(')');
+        } else {
+            gexpr();
+            skip(')');
+        }
+        break;
+    case '*':
+        next();
+        unary();
+        indir();
+        break;
+    case '&':
+        next();
+        unary();
+        /* functions names must be treated as function pointers,
+           except for unary '&' and sizeof. Since we consider that
+           functions are not lvalues, we only have to handle it
+           there and in function calls. */
+        /* arrays can also be used although they are not lvalues */
+        if ((vtop->type.t & VT_BTYPE) != VT_FUNC &&
+            !(vtop->type.t & VT_ARRAY))
+            test_lvalue();
+        mk_pointer(&vtop->type);
+        gaddrof();
+        break;
+    case '!':
+        next();
+        unary();
+        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST)
+            vtop->c.i = !vtop->c.i;
+        else if ((vtop->r & VT_VALMASK) == VT_CMP)
+            vtop->c.i = vtop->c.i ^ 1;
+        else
+            vseti(VT_JMP, gtst(1, 0));
+        break;
+    case '~':
+        next();
+        unary();
+        vpushi(-1);
+        gen_op('^');
+        break;
+    case '+':
+        next();
+        /* in order to force cast, we add zero */
+        unary();
+        if ((vtop->type.t & VT_BTYPE) == VT_PTR)
+            error("pointer not accepted for unary plus");
+        vpushi(0);
+        gen_op('+');
+        break;
+    case TOK_SIZEOF:
+    case TOK_ALIGNOF1:
+    case TOK_ALIGNOF2:
+        t = tok;
+        next();
+        if (tok == '(') {
+            parse_expr_type(&type);
+        } else {
+            unary_type(&type);
+        }
+        size = type_size(&type, &align);
+        if (t == TOK_SIZEOF) {
+            if (size < 0)
+                error("sizeof applied to an incomplete type");
+            vpushi(size);
+        } else {
+            vpushi(align);
+        }
+        break;
+
+    case TOK_builtin_types_compatible_p:
+        {
+            CType type1, type2;
+            next();
+            skip('(');
+            parse_type(&type1);
+            skip(',');
+            parse_type(&type2);
+            skip(')');
+            type1.t &= ~(VT_CONSTANT | VT_VOLATILE);
+            type2.t &= ~(VT_CONSTANT | VT_VOLATILE);
+            vpushi(is_compatible_types(&type1, &type2));
+        }
+        break;
+    case TOK_builtin_constant_p:
+        {
+            int saved_nocode_wanted, res;
+            next();
+            skip('(');
+            saved_nocode_wanted = nocode_wanted;
+            nocode_wanted = 1;
+            gexpr();
+            res = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
+            vpop();
+            nocode_wanted = saved_nocode_wanted;
+            skip(')');
+            vpushi(res);
+        }
+        break;
+    case TOK_INC:
+    case TOK_DEC:
+        t = tok;
+        next();
+        unary();
+        inc(0, t);
+        break;
+    case '-':
+        next();
+        vpushi(0);
+        unary();
+        gen_op('-');
+        break;
+    case TOK_LAND:
+        if (!gnu_ext)
+            goto tok_identifier;
+        next();
+        /* allow to take the address of a label */
+        if (tok < TOK_UIDENT)
+            expect("label identifier");
+        s = label_find(tok);
+        if (!s) {
+            s = label_push(&global_label_stack, tok, LABEL_FORWARD);
+        } else {
+            if (s->r == LABEL_DECLARED)
+                s->r = LABEL_FORWARD;
+        }
+        if (!s->type.t) {
+            s->type.t = VT_VOID;
+            mk_pointer(&s->type);
+            s->type.t |= VT_STATIC;
+        }
+        vset(&s->type, VT_CONST | VT_SYM, 0);
+        vtop->sym = s;
+        next();
+        break;
+    default:
+    tok_identifier:
+        t = tok;
+        next();
+        if (t < TOK_UIDENT)
+            expect("identifier");
+        s = sym_find(t);
+        if (!s) {
+            if (tok != '(')
+                error("'%s' undeclared", get_tok_str(t, NULL));
+            /* for simple function calls, we tolerate undeclared
+               external reference to int() function */
+            if (tcc_state->warn_implicit_function_declaration)
+                warning("implicit declaration of function '%s'",
+                        get_tok_str(t, NULL));
+            s = external_global_sym(t, &func_old_type, 0); 
+        }
+        if ((s->type.t & (VT_STATIC | VT_INLINE | VT_BTYPE)) ==
+            (VT_STATIC | VT_INLINE | VT_FUNC)) {
+            /* if referencing an inline function, then we generate a
+               symbol to it if not already done. It will have the
+               effect to generate code for it at the end of the
+               compilation unit. Inline function as always
+               generated in the text section. */
+            if (!s->c)
+                put_extern_sym(s, text_section, 0, 0);
+            r = VT_SYM | VT_CONST;
+        } else {
+            r = s->r;
+        }
+        vset(&s->type, r, s->c);
+        /* if forward reference, we must point to s */
+        if (vtop->r & VT_SYM) {
+            vtop->sym = s;
+            vtop->c.ul = 0;
+        }
+        break;
+    }
+    
+    /* post operations */
+    while (1) {
+        if (tok == TOK_INC || tok == TOK_DEC) {
+            inc(1, tok);
+            next();
+        } else if (tok == '.' || tok == TOK_ARROW) {
+            /* field */ 
+            if (tok == TOK_ARROW) 
+                indir();
+            test_lvalue();
+            gaddrof();
+            next();
+            /* expect pointer on structure */
+            if ((vtop->type.t & VT_BTYPE) != VT_STRUCT)
+                expect("struct or union");
+            s = vtop->type.ref;
+            /* find field */
+            tok |= SYM_FIELD;
+            while ((s = s->next) != NULL) {
+                if (s->v == tok)
+                    break;
+            }
+            if (!s)
+                error("field not found");
+            /* add field offset to pointer */
+            vtop->type = char_pointer_type; /* change type to 'char *' */
+            vpushi(s->c);
+            gen_op('+');
+            /* change type to field type, and set to lvalue */
+            vtop->type = s->type;
+            /* an array is never an lvalue */
+            if (!(vtop->type.t & VT_ARRAY)) {
+                vtop->r |= lvalue_type(vtop->type.t);
+                /* if bound checking, the referenced pointer must be checked */
+                if (do_bounds_check) 
+                    vtop->r |= VT_MUSTBOUND;
+            }
+            next();
+        } else if (tok == '[') {
+            next();
+            gexpr();
+            gen_op('+');
+            indir();
+            skip(']');
+        } else if (tok == '(') {
+            SValue ret;
+            Sym *sa;
+            int nb_args;
+
+            /* function call  */
+            if ((vtop->type.t & VT_BTYPE) != VT_FUNC) {
+                /* pointer test (no array accepted) */
+                if ((vtop->type.t & (VT_BTYPE | VT_ARRAY)) == VT_PTR) {
+                    vtop->type = *pointed_type(&vtop->type);
+                    if ((vtop->type.t & VT_BTYPE) != VT_FUNC)
+                        goto error_func;
+                } else {
+                error_func:
+                    expect("function pointer");
+                }
+            } else {
+                vtop->r &= ~VT_LVAL; /* no lvalue */
+            }
+            /* get return type */
+            s = vtop->type.ref;
+            next();
+            sa = s->next; /* first parameter */
+            nb_args = 0;
+            /* compute first implicit argument if a structure is returned */
+            if ((s->type.t & VT_BTYPE) == VT_STRUCT) {
+                /* get some space for the returned structure */
+                size = type_size(&s->type, &align);
+                loc = (loc - size) & -align;
+                ret.type = s->type;
+                ret.r = VT_LOCAL | VT_LVAL;
+                /* pass it as 'int' to avoid structure arg passing
+                   problems */
+                vseti(VT_LOCAL, loc);
+                ret.c = vtop->c;
+                nb_args++;
+            } else {
+                ret.type = s->type; 
+                ret.r2 = VT_CONST;
+                /* return in register */
+                if (is_float(ret.type.t)) {
+                    ret.r = REG_FRET; 
+                } else {
+                    if ((ret.type.t & VT_BTYPE) == VT_LLONG)
+                        ret.r2 = REG_LRET;
+                    ret.r = REG_IRET;
+                }
+                ret.c.i = 0;
+            }
+            if (tok != ')') {
+                for(;;) {
+                    expr_eq();
+                    gfunc_param_typed(s, sa);
+                    nb_args++;
+                    if (sa)
+                        sa = sa->next;
+                    if (tok == ')')
+                        break;
+                    skip(',');
+                }
+            }
+            if (sa)
+                error("too few arguments to function");
+            skip(')');
+            if (!nocode_wanted) {
+                gfunc_call(nb_args);
+            } else {
+                vtop -= (nb_args + 1);
+            }
+            /* return value */
+            vsetc(&ret.type, ret.r, &ret.c);
+            vtop->r2 = ret.r2;
+        } else {
+            break;
+        }
+    }
+}
+
+static void uneq(void)
+{
+    int t;
+    
+    unary();
+    if (tok == '=' ||
+        (tok >= TOK_A_MOD && tok <= TOK_A_DIV) ||
+        tok == TOK_A_XOR || tok == TOK_A_OR ||
+        tok == TOK_A_SHL || tok == TOK_A_SAR) {
+        test_lvalue();
+        t = tok;
+        next();
+        if (t == '=') {
+            expr_eq();
+        } else {
+            vdup();
+            expr_eq();
+            gen_op(t & 0x7f);
+        }
+        vstore();
+    }
+}
+
+static void expr_prod(void)
+{
+    int t;
+
+    uneq();
+    while (tok == '*' || tok == '/' || tok == '%') {
+        t = tok;
+        next();
+        uneq();
+        gen_op(t);
+    }
+}
+
+static void expr_sum(void)
+{
+    int t;
+
+    expr_prod();
+    while (tok == '+' || tok == '-') {
+        t = tok;
+        next();
+        expr_prod();
+        gen_op(t);
+    }
+}
+
+static void expr_shift(void)
+{
+    int t;
+
+    expr_sum();
+    while (tok == TOK_SHL || tok == TOK_SAR) {
+        t = tok;
+        next();
+        expr_sum();
+        gen_op(t);
+    }
+}
+
+static void expr_cmp(void)
+{
+    int t;
+
+    expr_shift();
+    while ((tok >= TOK_ULE && tok <= TOK_GT) ||
+           tok == TOK_ULT || tok == TOK_UGE) {
+        t = tok;
+        next();
+        expr_shift();
+        gen_op(t);
+    }
+}
+
+static void expr_cmpeq(void)
+{
+    int t;
+
+    expr_cmp();
+    while (tok == TOK_EQ || tok == TOK_NE) {
+        t = tok;
+        next();
+        expr_cmp();
+        gen_op(t);
+    }
+}
+
+static void expr_and(void)
+{
+    expr_cmpeq();
+    while (tok == '&') {
+        next();
+        expr_cmpeq();
+        gen_op('&');
+    }
+}
+
+static void expr_xor(void)
+{
+    expr_and();
+    while (tok == '^') {
+        next();
+        expr_and();
+        gen_op('^');
+    }
+}
+
+static void expr_or(void)
+{
+    expr_xor();
+    while (tok == '|') {
+        next();
+        expr_xor();
+        gen_op('|');
+    }
+}
+
+/* XXX: fix this mess */
+static void expr_land_const(void)
+{
+    expr_or();
+    while (tok == TOK_LAND) {
+        next();
+        expr_or();
+        gen_op(TOK_LAND);
+    }
+}
+
+/* XXX: fix this mess */
+static void expr_lor_const(void)
+{
+    expr_land_const();
+    while (tok == TOK_LOR) {
+        next();
+        expr_land_const();
+        gen_op(TOK_LOR);
+    }
+}
+
+/* only used if non constant */
+static void expr_land(void)
+{
+    int t;
+
+    expr_or();
+    if (tok == TOK_LAND) {
+        t = 0;
+        for(;;) {
+            t = gtst(1, t);
+            if (tok != TOK_LAND) {
+                vseti(VT_JMPI, t);
+                break;
+            }
+            next();
+            expr_or();
+        }
+    }
+}
+
+static void expr_lor(void)
+{
+    int t;
+
+    expr_land();
+    if (tok == TOK_LOR) {
+        t = 0;
+        for(;;) {
+            t = gtst(0, t);
+            if (tok != TOK_LOR) {
+                vseti(VT_JMP, t);
+                break;
+            }
+            next();
+            expr_land();
+        }
+    }
+}
+
+/* XXX: better constant handling */
+static void expr_eq(void)
+{
+    int tt, u, r1, r2, rc, t1, t2, bt1, bt2;
+    SValue sv;
+    CType type, type1, type2;
+
+    if (const_wanted) {
+        int c1, c;
+        expr_lor_const();
+        if (tok == '?') {
+            c = vtop->c.i;
+            vpop();
+            next();
+            if (tok == ':' && gnu_ext) {
+                c1 = c;
+            } else {
+                gexpr();
+                c1 = vtop->c.i;
+                vpop();
+            }
+            skip(':');
+            expr_eq();
+            if (c)
+                vtop->c.i = c1;
+        }
+    } else {
+        expr_lor();
+        if (tok == '?') {
+            next();
+            if (vtop != vstack) {
+                /* needed to avoid having different registers saved in
+                   each branch */
+                if (is_float(vtop->type.t))
+                    rc = RC_FLOAT;
+                else
+                    rc = RC_INT;
+                    gv(rc);
+                    save_regs(1);
+            }
+            if (tok == ':' && gnu_ext) {
+                gv_dup();
+                tt = gtst(1, 0);
+            } else {
+                tt = gtst(1, 0);
+                gexpr();
+            }
+            type1 = vtop->type;
+            sv = *vtop; /* save value to handle it later */
+            vtop--; /* no vpop so that FP stack is not flushed */
+            skip(':');
+            u = gjmp(0);
+            gsym(tt);
+            expr_eq();
+            type2 = vtop->type;
+
+            t1 = type1.t;
+            bt1 = t1 & VT_BTYPE;
+            t2 = type2.t;
+            bt2 = t2 & VT_BTYPE;
+            /* cast operands to correct type according to ISOC rules */
+            if (is_float(bt1) || is_float(bt2)) {
+                if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) {
+                    type.t = VT_LDOUBLE;
+                } else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) {
+                    type.t = VT_DOUBLE;
+                } else {
+                    type.t = VT_FLOAT;
+                }
+            } else if (bt1 == VT_LLONG || bt2 == VT_LLONG) {
+                /* cast to biggest op */
+                type.t = VT_LLONG;
+                /* convert to unsigned if it does not fit in a long long */
+                if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED) ||
+                    (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED))
+                    type.t |= VT_UNSIGNED;
+            } else if (bt1 == VT_PTR || bt2 == VT_PTR) {
+                /* XXX: test pointer compatibility */
+                type = type1;
+            } else if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) {
+                /* XXX: test structure compatibility */
+                type = type1;
+            } else if (bt1 == VT_VOID || bt2 == VT_VOID) {
+                /* NOTE: as an extension, we accept void on only one side */
+                type.t = VT_VOID;
+            } else {
+                /* integer operations */
+                type.t = VT_INT;
+                /* convert to unsigned if it does not fit in an integer */
+                if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED) ||
+                    (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED))
+                    type.t |= VT_UNSIGNED;
+            }
+                
+            /* now we convert second operand */
+            gen_cast(&type);
+            rc = RC_INT;
+            if (is_float(type.t)) {
+                rc = RC_FLOAT;
+            } else if ((type.t & VT_BTYPE) == VT_LLONG) {
+                /* for long longs, we use fixed registers to avoid having
+                   to handle a complicated move */
+                rc = RC_IRET; 
+            }
+            
+            r2 = gv(rc);
+            /* this is horrible, but we must also convert first
+               operand */
+            tt = gjmp(0);
+            gsym(u);
+            /* put again first value and cast it */
+            *vtop = sv;
+            gen_cast(&type);
+            r1 = gv(rc);
+            move_reg(r2, r1);
+            vtop->r = r2;
+            gsym(tt);
+        }
+    }
+}
+
+static void gexpr(void)
+{
+    while (1) {
+        expr_eq();
+        if (tok != ',')
+            break;
+        vpop();
+        next();
+    }
+}
+
+/* parse an expression and return its type without any side effect. */
+static void expr_type(CType *type)
+{
+    int saved_nocode_wanted;
+
+    saved_nocode_wanted = nocode_wanted;
+    nocode_wanted = 1;
+    gexpr();
+    *type = vtop->type;
+    vpop();
+    nocode_wanted = saved_nocode_wanted;
+}
+
+/* parse a unary expression and return its type without any side
+   effect. */
+static void unary_type(CType *type)
+{
+    int a;
+
+    a = nocode_wanted;
+    nocode_wanted = 1;
+    unary();
+    *type = vtop->type;
+    vpop();
+    nocode_wanted = a;
+}
+
+/* parse a constant expression and return value in vtop.  */
+static void expr_const1(void)
+{
+    int a;
+    a = const_wanted;
+    const_wanted = 1;
+    expr_eq();
+    const_wanted = a;
+}
+
+/* parse an integer constant and return its value. */
+static int expr_const(void)
+{
+    int c;
+    expr_const1();
+    if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
+        expect("constant expression");
+    c = vtop->c.i;
+    vpop();
+    return c;
+}
+
+/* return the label token if current token is a label, otherwise
+   return zero */
+static int is_label(void)
+{
+    int last_tok;
+
+    /* fast test first */
+    if (tok < TOK_UIDENT)
+        return 0;
+    /* no need to save tokc because tok is an identifier */
+    last_tok = tok;
+    next();
+    if (tok == ':') {
+        next();
+        return last_tok;
+    } else {
+        unget_tok(last_tok);
+        return 0;
+    }
+}
+
+static void block(int *bsym, int *csym, int *case_sym, int *def_sym, 
+                  int case_reg, int is_expr)
+{
+    int a, b, c, d;
+    Sym *s;
+
+    /* generate line number info */
+    if (do_debug && 
+        (last_line_num != file->line_num || last_ind != ind)) {
+        put_stabn(N_SLINE, 0, file->line_num, ind - func_ind);
+        last_ind = ind;
+        last_line_num = file->line_num;
+    }
+
+    if (is_expr) {
+        /* default return value is (void) */
+        vpushi(0);
+        vtop->type.t = VT_VOID;
+    }
+
+    if (tok == TOK_IF) {
+        /* if test */
+        next();
+        skip('(');
+        gexpr();
+        skip(')');
+        a = gtst(1, 0);
+        block(bsym, csym, case_sym, def_sym, case_reg, 0);
+        c = tok;
+        if (c == TOK_ELSE) {
+            next();
+            d = gjmp(0);
+            gsym(a);
+            block(bsym, csym, case_sym, def_sym, case_reg, 0);
+            gsym(d); /* patch else jmp */
+        } else
+            gsym(a);
+    } else if (tok == TOK_WHILE) {
+        next();
+        d = ind;
+        skip('(');
+        gexpr();
+        skip(')');
+        a = gtst(1, 0);
+        b = 0;
+        block(&a, &b, case_sym, def_sym, case_reg, 0);
+        gjmp_addr(d);
+        gsym(a);
+        gsym_addr(b, d);
+    } else if (tok == '{') {
+        Sym *llabel;
+        
+        next();
+        /* record local declaration stack position */
+        s = local_stack;
+        llabel = local_label_stack;
+        /* handle local labels declarations */
+        if (tok == TOK_LABEL) {
+            next();
+            for(;;) {
+                if (tok < TOK_UIDENT)
+                    expect("label identifier");
+                label_push(&local_label_stack, tok, LABEL_DECLARED);
+                next();
+                if (tok == ',') {
+                    next();
+                } else {
+                    skip(';');
+                    break;
+                }
+            }
+        }
+        while (tok != '}') {
+            decl(VT_LOCAL);
+            if (tok != '}') {
+                if (is_expr)
+                    vpop();
+                block(bsym, csym, case_sym, def_sym, case_reg, is_expr);
+            }
+        }
+        /* pop locally defined labels */
+        label_pop(&local_label_stack, llabel);
+        /* pop locally defined symbols */
+        sym_pop(&local_stack, s);
+        next();
+    } else if (tok == TOK_RETURN) {
+        next();
+        if (tok != ';') {
+            gexpr();
+            gen_assign_cast(&func_vt);
+            if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
+                CType type;
+                /* if returning structure, must copy it to implicit
+                   first pointer arg location */
+                type = func_vt;
+                mk_pointer(&type);
+                vset(&type, VT_LOCAL | VT_LVAL, func_vc);
+                indir();
+                vswap();
+                /* copy structure value to pointer */
+                vstore();
+            } else if (is_float(func_vt.t)) {
+                gv(RC_FRET);
+            } else {
+                gv(RC_IRET);
+            }
+            vtop--; /* NOT vpop() because on x86 it would flush the fp stack */
+        }
+        skip(';');
+        rsym = gjmp(rsym); /* jmp */
+    } else if (tok == TOK_BREAK) {
+        /* compute jump */
+        if (!bsym)
+            error("cannot break");
+        *bsym = gjmp(*bsym);
+        next();
+        skip(';');
+    } else if (tok == TOK_CONTINUE) {
+        /* compute jump */
+        if (!csym)
+            error("cannot continue");
+        *csym = gjmp(*csym);
+        next();
+        skip(';');
+    } else if (tok == TOK_FOR) {
+        int e;
+        next();
+        skip('(');
+        if (tok != ';') {
+            gexpr();
+            vpop();
+        }
+        skip(';');
+        d = ind;
+        c = ind;
+        a = 0;
+        b = 0;
+        if (tok != ';') {
+            gexpr();
+            a = gtst(1, 0);
+        }
+        skip(';');
+        if (tok != ')') {
+            e = gjmp(0);
+            c = ind;
+            gexpr();
+            vpop();
+            gjmp_addr(d);
+            gsym(e);
+        }
+        skip(')');
+        block(&a, &b, case_sym, def_sym, case_reg, 0);
+        gjmp_addr(c);
+        gsym(a);
+        gsym_addr(b, c);
+    } else 
+    if (tok == TOK_DO) {
+        next();
+        a = 0;
+        b = 0;
+        d = ind;
+        block(&a, &b, case_sym, def_sym, case_reg, 0);
+        skip(TOK_WHILE);
+        skip('(');
+        gsym(b);
+        gexpr();
+        c = gtst(0, 0);
+        gsym_addr(c, d);
+        skip(')');
+        gsym(a);
+        skip(';');
+    } else
+    if (tok == TOK_SWITCH) {
+        next();
+        skip('(');
+        gexpr();
+        /* XXX: other types than integer */
+        case_reg = gv(RC_INT);
+        vpop();
+        skip(')');
+        a = 0;
+        b = gjmp(0); /* jump to first case */
+        c = 0;
+        block(&a, csym, &b, &c, case_reg, 0);
+        /* if no default, jmp after switch */
+        if (c == 0)
+            c = ind;
+        /* default label */
+        gsym_addr(b, c);
+        /* break label */
+        gsym(a);
+    } else
+    if (tok == TOK_CASE) {
+        int v1, v2;
+        if (!case_sym)
+            expect("switch");
+        next();
+        v1 = expr_const();
+        v2 = v1;
+        if (gnu_ext && tok == TOK_DOTS) {
+            next();
+            v2 = expr_const();
+            if (v2 < v1)
+                warning("empty case range");
+        }
+        /* since a case is like a label, we must skip it with a jmp */
+        b = gjmp(0);
+        gsym(*case_sym);
+        vseti(case_reg, 0);
+        vpushi(v1);
+        if (v1 == v2) {
+            gen_op(TOK_EQ);
+            *case_sym = gtst(1, 0);
+        } else {
+            gen_op(TOK_GE);
+            *case_sym = gtst(1, 0);
+            vseti(case_reg, 0);
+            vpushi(v2);
+            gen_op(TOK_LE);
+            *case_sym = gtst(1, *case_sym);
+        }
+        gsym(b);
+        skip(':');
+        is_expr = 0;
+        goto block_after_label;
+    } else 
+    if (tok == TOK_DEFAULT) {
+        next();
+        skip(':');
+        if (!def_sym)
+            expect("switch");
+        if (*def_sym)
+            error("too many 'default'");
+        *def_sym = ind;
+        is_expr = 0;
+        goto block_after_label;
+    } else
+    if (tok == TOK_GOTO) {
+        next();
+        if (tok == '*' && gnu_ext) {
+            /* computed goto */
+            next();
+            gexpr();
+            if ((vtop->type.t & VT_BTYPE) != VT_PTR)
+                expect("pointer");
+            ggoto();
+        } else if (tok >= TOK_UIDENT) {
+            s = label_find(tok);
+            /* put forward definition if needed */
+            if (!s) {
+                s = label_push(&global_label_stack, tok, LABEL_FORWARD);
+            } else {
+                if (s->r == LABEL_DECLARED)
+                    s->r = LABEL_FORWARD;
+            }
+            /* label already defined */
+            if (s->r & LABEL_FORWARD) 
+                s->next = (void *)gjmp((long)s->next);
+            else
+                gjmp_addr((long)s->next);
+            next();
+        } else {
+            expect("label identifier");
+        }
+        skip(';');
+    } else if (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3) {
+        asm_instr();
+    } else {
+        b = is_label();
+        if (b) {
+            /* label case */
+            s = label_find(b);
+            if (s) {
+                if (s->r == LABEL_DEFINED)
+                    error("duplicate label '%s'", get_tok_str(s->v, NULL));
+                gsym((long)s->next);
+                s->r = LABEL_DEFINED;
+            } else {
+                s = label_push(&global_label_stack, b, LABEL_DEFINED);
+            }
+            s->next = (void *)ind;
+            /* we accept this, but it is a mistake */
+        block_after_label:
+            if (tok == '}') {
+                warning("deprecated use of label at end of compound statement");
+            } else {
+                if (is_expr)
+                    vpop();
+                block(bsym, csym, case_sym, def_sym, case_reg, is_expr);
+            }
+        } else {
+            /* expression case */
+            if (tok != ';') {
+                if (is_expr) {
+                    vpop();
+                    gexpr();
+                } else {
+                    gexpr();
+                    vpop();
+                }
+            }
+            skip(';');
+        }
+    }
+}
+
+/* t is the array or struct type. c is the array or struct
+   address. cur_index/cur_field is the pointer to the current
+   value. 'size_only' is true if only size info is needed (only used
+   in arrays) */
+static void decl_designator(CType *type, Section *sec, unsigned long c, 
+                            int *cur_index, Sym **cur_field, 
+                            int size_only)
+{
+    Sym *s, *f;
+    int notfirst, index, index_last, align, l, nb_elems, elem_size;
+    CType type1;
+
+    notfirst = 0;
+    elem_size = 0;
+    nb_elems = 1;
+    if (gnu_ext && (l = is_label()) != 0)
+        goto struct_field;
+    while (tok == '[' || tok == '.') {
+        if (tok == '[') {
+            if (!(type->t & VT_ARRAY))
+                expect("array type");
+            s = type->ref;
+            next();
+            index = expr_const();
+            if (index < 0 || (s->c >= 0 && index >= s->c))
+                expect("invalid index");
+            if (tok == TOK_DOTS && gnu_ext) {
+                next();
+                index_last = expr_const();
+                if (index_last < 0 || 
+                    (s->c >= 0 && index_last >= s->c) ||
+                    index_last < index)
+                    expect("invalid index");
+            } else {
+                index_last = index;
+            }
+            skip(']');
+            if (!notfirst)
+                *cur_index = index_last;
+            type = pointed_type(type);
+            elem_size = type_size(type, &align);
+            c += index * elem_size;
+            /* NOTE: we only support ranges for last designator */
+            nb_elems = index_last - index + 1;
+            if (nb_elems != 1) {
+                notfirst = 1;
+                break;
+            }
+        } else {
+            next();
+            l = tok;
+            next();
+        struct_field:
+            if ((type->t & VT_BTYPE) != VT_STRUCT)
+                expect("struct/union type");
+            s = type->ref;
+            l |= SYM_FIELD;
+            f = s->next;
+            while (f) {
+                if (f->v == l)
+                    break;
+                f = f->next;
+            }
+            if (!f)
+                expect("field");
+            if (!notfirst)
+                *cur_field = f;
+            /* XXX: fix this mess by using explicit storage field */
+            type1 = f->type;
+            type1.t |= (type->t & ~VT_TYPE);
+            type = &type1;
+            c += f->c;
+        }
+        notfirst = 1;
+    }
+    if (notfirst) {
+        if (tok == '=') {
+            next();
+        } else {
+            if (!gnu_ext)
+                expect("=");
+        }
+    } else {
+        if (type->t & VT_ARRAY) {
+            index = *cur_index;
+            type = pointed_type(type);
+            c += index * type_size(type, &align);
+        } else {
+            f = *cur_field;
+            if (!f)
+                error("too many field init");
+            /* XXX: fix this mess by using explicit storage field */
+            type1 = f->type;
+            type1.t |= (type->t & ~VT_TYPE);
+            type = &type1;
+            c += f->c;
+        }
+    }
+    decl_initializer(type, sec, c, 0, size_only);
+
+    /* XXX: make it more general */
+    if (!size_only && nb_elems > 1) {
+        unsigned long c_end;
+        uint8_t *src, *dst;
+        int i;
+
+        if (!sec)
+            error("range init not supported yet for dynamic storage");
+        c_end = c + nb_elems * elem_size;
+        if (c_end > sec->data_allocated)
+            section_realloc(sec, c_end);
+        src = sec->data + c;
+        dst = src;
+        for(i = 1; i < nb_elems; i++) {
+            dst += elem_size;
+            memcpy(dst, src, elem_size);
+        }
+    }
+}
+
+#define EXPR_VAL   0
+#define EXPR_CONST 1
+#define EXPR_ANY   2
+
+/* store a value or an expression directly in global data or in local array */
+static void init_putv(CType *type, Section *sec, unsigned long c, 
+                      int v, int expr_type)
+{
+    int saved_global_expr, bt, bit_pos, bit_size;
+    void *ptr;
+    unsigned long long bit_mask;
+    CType dtype;
+
+    switch(expr_type) {
+    case EXPR_VAL:
+        vpushi(v);
+        break;
+    case EXPR_CONST:
+        /* compound literals must be allocated globally in this case */
+        saved_global_expr = global_expr;
+        global_expr = 1;
+        expr_const1();
+        global_expr = saved_global_expr;
+        /* NOTE: symbols are accepted */
+        if ((vtop->r & (VT_VALMASK | VT_LVAL)) != VT_CONST)
+            error("initializer element is not constant");
+        break;
+    case EXPR_ANY:
+        expr_eq();
+        break;
+    }
+    
+    dtype = *type;
+    dtype.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */
+
+    if (sec) {
+        /* XXX: not portable */
+        /* XXX: generate error if incorrect relocation */
+        gen_assign_cast(&dtype);
+        bt = type->t & VT_BTYPE;
+        ptr = sec->data + c;
+        /* XXX: make code faster ? */
+        if (!(type->t & VT_BITFIELD)) {
+            bit_pos = 0;
+            bit_size = 32;
+            bit_mask = -1LL;
+        } else {
+            bit_pos = (vtop->type.t >> VT_STRUCT_SHIFT) & 0x3f;
+            bit_size = (vtop->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f;
+            bit_mask = (1LL << bit_size) - 1;
+        }
+        if ((vtop->r & VT_SYM) &&
+            (bt == VT_BYTE ||
+             bt == VT_SHORT ||
+             bt == VT_DOUBLE ||
+             bt == VT_LDOUBLE ||
+             bt == VT_LLONG ||
+             (bt == VT_INT && bit_size != 32)))
+            error("initializer element is not computable at load time");
+        switch(bt) {
+        case VT_BYTE:
+            *(char *)ptr |= (vtop->c.i & bit_mask) << bit_pos;
+            break;
+        case VT_SHORT:
+            *(short *)ptr |= (vtop->c.i & bit_mask) << bit_pos;
+            break;
+        case VT_DOUBLE:
+            *(double *)ptr = vtop->c.d;
+            break;
+        case VT_LDOUBLE:
+            *(long double *)ptr = vtop->c.ld;
+            break;
+        case VT_LLONG:
+            *(long long *)ptr |= (vtop->c.ll & bit_mask) << bit_pos;
+            break;
+        default:
+            if (vtop->r & VT_SYM) {
+                greloc(sec, vtop->sym, c, R_DATA_32);
+            }
+            *(int *)ptr |= (vtop->c.i & bit_mask) << bit_pos;
+            break;
+        }
+        vtop--;
+    } else {
+        vset(&dtype, VT_LOCAL, c);
+        vswap();
+        vstore();
+        vpop();
+    }
+}
+
+/* put zeros for variable based init */
+static void init_putz(CType *t, Section *sec, unsigned long c, int size)
+{
+    if (sec) {
+        /* nothing to do because globals are already set to zero */
+    } else {
+        vpush_global_sym(&func_old_type, TOK_memset);
+        vseti(VT_LOCAL, c);
+        vpushi(0);
+        vpushi(size);
+        gfunc_call(3);
+    }
+}
+
+/* 't' contains the type and storage info. 'c' is the offset of the
+   object in section 'sec'. If 'sec' is NULL, it means stack based
+   allocation. 'first' is true if array '{' must be read (multi
+   dimension implicit array init handling). 'size_only' is true if
+   size only evaluation is wanted (only for arrays). */
+static void decl_initializer(CType *type, Section *sec, unsigned long c, 
+                             int first, int size_only)
+{
+    int index, array_length, n, no_oblock, nb, parlevel, i;
+    int size1, align1, expr_type;
+    Sym *s, *f;
+    CType *t1;
+
+    if (type->t & VT_ARRAY) {
+        s = type->ref;
+        n = s->c;
+        array_length = 0;
+        t1 = pointed_type(type);
+        size1 = type_size(t1, &align1);
+
+        no_oblock = 1;
+        if ((first && tok != TOK_LSTR && tok != TOK_STR) || 
+            tok == '{') {
+            skip('{');
+            no_oblock = 0;
+        }
+
+        /* only parse strings here if correct type (otherwise: handle
+           them as ((w)char *) expressions */
+        if ((tok == TOK_LSTR && 
+             (t1->t & VT_BTYPE) == VT_INT) ||
+            (tok == TOK_STR &&
+             (t1->t & VT_BTYPE) == VT_BYTE)) {
+            while (tok == TOK_STR || tok == TOK_LSTR) {
+                int cstr_len, ch;
+                CString *cstr;
+
+                cstr = tokc.cstr;
+                /* compute maximum number of chars wanted */
+                if (tok == TOK_STR)
+                    cstr_len = cstr->size;
+                else
+                    cstr_len = cstr->size / sizeof(int);
+                cstr_len--;
+                nb = cstr_len;
+                if (n >= 0 && nb > (n - array_length))
+                    nb = n - array_length;
+                if (!size_only) {
+                    if (cstr_len > nb)
+                        warning("initializer-string for array is too long");
+                    /* in order to go faster for common case (char
+                       string in global variable, we handle it
+                       specifically */
+                    if (sec && tok == TOK_STR && size1 == 1) {
+                        memcpy(sec->data + c + array_length, cstr->data, nb);
+                    } else {
+                        for(i=0;i<nb;i++) {
+                            if (tok == TOK_STR)
+                                ch = ((unsigned char *)cstr->data)[i];
+                            else
+                                ch = ((int *)cstr->data)[i];
+                            init_putv(t1, sec, c + (array_length + i) * size1,
+                                      ch, EXPR_VAL);
+                        }
+                    }
+                }
+                array_length += nb;
+                next();
+            }
+            /* only add trailing zero if enough storage (no
+               warning in this case since it is standard) */
+            if (n < 0 || array_length < n) {
+                if (!size_only) {
+                    init_putv(t1, sec, c + (array_length * size1), 0, EXPR_VAL);
+                }
+                array_length++;
+            }
+        } else {
+            index = 0;
+            while (tok != '}') {
+                decl_designator(type, sec, c, &index, NULL, size_only);
+                if (n >= 0 && index >= n)
+                    error("index too large");
+                /* must put zero in holes (note that doing it that way
+                   ensures that it even works with designators) */
+                if (!size_only && array_length < index) {
+                    init_putz(t1, sec, c + array_length * size1, 
+                              (index - array_length) * size1);
+                }
+                index++;
+                if (index > array_length)
+                    array_length = index;
+                /* special test for multi dimensional arrays (may not
+                   be strictly correct if designators are used at the
+                   same time) */
+                if (index >= n && no_oblock)
+                    break;
+                if (tok == '}')
+                    break;
+                skip(',');
+            }
+        }
+        if (!no_oblock)
+            skip('}');
+        /* put zeros at the end */
+        if (!size_only && n >= 0 && array_length < n) {
+            init_putz(t1, sec, c + array_length * size1, 
+                      (n - array_length) * size1);
+        }
+        /* patch type size if needed */
+        if (n < 0)
+            s->c = array_length;
+    } else if ((type->t & VT_BTYPE) == VT_STRUCT &&
+               (sec || !first || tok == '{')) {
+        int par_count;
+
+        /* NOTE: the previous test is a specific case for automatic
+           struct/union init */
+        /* XXX: union needs only one init */
+
+        /* XXX: this test is incorrect for local initializers
+           beginning with ( without {. It would be much more difficult
+           to do it correctly (ideally, the expression parser should
+           be used in all cases) */
+        par_count = 0;
+        if (tok == '(') {
+            AttributeDef ad1;
+            CType type1;
+            next();
+            while (tok == '(') {
+                par_count++;
+                next();
+            }
+            if (!parse_btype(&type1, &ad1))
+                expect("cast");
+            type_decl(&type1, &ad1, &n, TYPE_ABSTRACT);
+#if 0
+            if (!is_assignable_types(type, &type1))
+                error("invalid type for cast");
+#endif
+            skip(')');
+        }
+        no_oblock = 1;
+        if (first || tok == '{') {
+            skip('{');
+            no_oblock = 0;
+        }
+        s = type->ref;
+        f = s->next;
+        array_length = 0;
+        index = 0;
+        n = s->c;
+        while (tok != '}') {
+            decl_designator(type, sec, c, NULL, &f, size_only);
+            index = f->c;
+            if (!size_only && array_length < index) {
+                init_putz(type, sec, c + array_length, 
+                          index - array_length);
+            }
+            index = index + type_size(&f->type, &align1);
+            if (index > array_length)
+                array_length = index;
+            f = f->next;
+            if (no_oblock && f == NULL)
+                break;
+            if (tok == '}')
+                break;
+            skip(',');
+        }
+        /* put zeros at the end */
+        if (!size_only && array_length < n) {
+            init_putz(type, sec, c + array_length, 
+                      n - array_length);
+        }
+        if (!no_oblock)
+            skip('}');
+        while (par_count) {
+            skip(')');
+            par_count--;
+        }
+    } else if (tok == '{') {
+        next();
+        decl_initializer(type, sec, c, first, size_only);
+        skip('}');
+    } else if (size_only) {
+        /* just skip expression */
+        parlevel = 0;
+        while ((parlevel > 0 || (tok != '}' && tok != ',')) && 
+               tok != -1) {
+            if (tok == '(')
+                parlevel++;
+            else if (tok == ')')
+                parlevel--;
+            next();
+        }
+    } else {
+        /* currently, we always use constant expression for globals
+           (may change for scripting case) */
+        expr_type = EXPR_CONST;
+        if (!sec)
+            expr_type = EXPR_ANY;
+        init_putv(type, sec, c, 0, expr_type);
+    }
+}
+
+/* parse an initializer for type 't' if 'has_init' is non zero, and
+   allocate space in local or global data space ('r' is either
+   VT_LOCAL or VT_CONST). If 'v' is non zero, then an associated
+   variable 'v' of scope 'scope' is declared before initializers are
+   parsed. If 'v' is zero, then a reference to the new object is put
+   in the value stack. If 'has_init' is 2, a special parsing is done
+   to handle string constants. */
+static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, 
+                                   int has_init, int v, int scope)
+{
+    int size, align, addr, data_offset;
+    int level;
+    ParseState saved_parse_state;
+    TokenString init_str;
+    Section *sec;
+
+    size = type_size(type, &align);
+    /* If unknown size, we must evaluate it before
+       evaluating initializers because
+       initializers can generate global data too
+       (e.g. string pointers or ISOC99 compound
+       literals). It also simplifies local
+       initializers handling */
+    tok_str_new(&init_str);
+    if (size < 0) {
+        if (!has_init) 
+            error("unknown type size");
+        /* get all init string */
+        if (has_init == 2) {
+            /* only get strings */
+            while (tok == TOK_STR || tok == TOK_LSTR) {
+                tok_str_add_tok(&init_str);
+                next();
+            }
+        } else {
+            level = 0;
+            while (level > 0 || (tok != ',' && tok != ';')) {
+                if (tok < 0)
+                    error("unexpected end of file in initializer");
+                tok_str_add_tok(&init_str);
+                if (tok == '{')
+                    level++;
+                else if (tok == '}') {
+                    if (level == 0)
+                        break;
+                    level--;
+                }
+                next();
+            }
+        }
+        tok_str_add(&init_str, -1);
+        tok_str_add(&init_str, 0);
+        
+        /* compute size */
+        save_parse_state(&saved_parse_state);
+
+        macro_ptr = init_str.str;
+        next();
+        decl_initializer(type, NULL, 0, 1, 1);
+        /* prepare second initializer parsing */
+        macro_ptr = init_str.str;
+        next();
+        
+        /* if still unknown size, error */
+        size = type_size(type, &align);
+        if (size < 0) 
+            error("unknown type size");
+    }
+    /* take into account specified alignment if bigger */
+    if (ad->aligned) {
+        if (ad->aligned > align)
+            align = ad->aligned;
+    } else if (ad->packed) {
+        align = 1;
+    }
+    if ((r & VT_VALMASK) == VT_LOCAL) {
+        sec = NULL;
+        if (do_bounds_check && (type->t & VT_ARRAY)) 
+            loc--;
+        loc = (loc - size) & -align;
+        addr = loc;
+        /* handles bounds */
+        /* XXX: currently, since we do only one pass, we cannot track
+           '&' operators, so we add only arrays */
+        if (do_bounds_check && (type->t & VT_ARRAY)) {
+            unsigned long *bounds_ptr;
+            /* add padding between regions */
+            loc--;
+            /* then add local bound info */
+            bounds_ptr = section_ptr_add(lbounds_section, 2 * sizeof(unsigned long));
+            bounds_ptr[0] = addr;
+            bounds_ptr[1] = size;
+        }
+        if (v) {
+            /* local variable */
+            sym_push(v, type, r, addr);
+        } else {
+            /* push local reference */
+            vset(type, r, addr);
+        }
+    } else {
+        Sym *sym;
+
+        sym = NULL;
+        if (v && scope == VT_CONST) {
+            /* see if the symbol was already defined */
+            sym = sym_find(v);
+            if (sym) {
+                if (!is_compatible_types(&sym->type, type))
+                    error("incompatible types for redefinition of '%s'", 
+                          get_tok_str(v, NULL));
+                if (sym->type.t & VT_EXTERN) {
+                    /* if the variable is extern, it was not allocated */
+                    sym->type.t &= ~VT_EXTERN;
+                    /* set array size if it was ommited in extern
+                       declaration */
+                    if ((sym->type.t & VT_ARRAY) && 
+                        sym->type.ref->c < 0 &&
+                        type->ref->c >= 0)
+                        sym->type.ref->c = type->ref->c;
+                } else {
+                    /* we accept several definitions of the same
+                       global variable. this is tricky, because we
+                       must play with the SHN_COMMON type of the symbol */
+                    /* XXX: should check if the variable was already
+                       initialized. It is incorrect to initialized it
+                       twice */
+                    /* no init data, we won't add more to the symbol */
+                    if (!has_init)
+                        goto no_alloc;
+                }
+            }
+        }
+
+        /* allocate symbol in corresponding section */
+        sec = ad->section;
+        if (!sec) {
+            if (has_init)
+                sec = data_section;
+            else if (tcc_state->nocommon)
+                sec = bss_section;
+        }
+        if (sec) {
+            data_offset = sec->data_offset;
+            data_offset = (data_offset + align - 1) & -align;
+            addr = data_offset;
+            /* very important to increment global pointer at this time
+               because initializers themselves can create new initializers */
+            data_offset += size;
+            /* add padding if bound check */
+            if (do_bounds_check)
+                data_offset++;
+            sec->data_offset = data_offset;
+            /* allocate section space to put the data */
+            if (sec->sh_type != SHT_NOBITS && 
+                data_offset > sec->data_allocated)
+                section_realloc(sec, data_offset);
+            /* align section if needed */
+            if (align > sec->sh_addralign)
+                sec->sh_addralign = align;
+        } else {
+            addr = 0; /* avoid warning */
+        }
+
+        if (v) {
+            if (scope == VT_CONST) {
+                if (!sym)
+                    goto do_def;
+            } else {
+            do_def:
+                sym = sym_push(v, type, r | VT_SYM, 0);
+            }
+            /* update symbol definition */
+            if (sec) {
+                put_extern_sym(sym, sec, addr, size);
+            } else {
+                Elf32_Sym *esym;
+                /* put a common area */
+                put_extern_sym(sym, NULL, align, size);
+                /* XXX: find a nicer way */
+                esym = &((Elf32_Sym *)symtab_section->data)[sym->c];
+                esym->st_shndx = SHN_COMMON;
+            }
+        } else {
+            CValue cval;
+
+            /* push global reference */
+            sym = get_sym_ref(type, sec, addr, size);
+            cval.ul = 0;
+            vsetc(type, VT_CONST | VT_SYM, &cval);
+            vtop->sym = sym;
+        }
+
+        /* handles bounds now because the symbol must be defined
+           before for the relocation */
+        if (do_bounds_check) {
+            unsigned long *bounds_ptr;
+
+            greloc(bounds_section, sym, bounds_section->data_offset, R_DATA_32);
+            /* then add global bound info */
+            bounds_ptr = section_ptr_add(bounds_section, 2 * sizeof(long));
+            bounds_ptr[0] = 0; /* relocated */
+            bounds_ptr[1] = size;
+        }
+    }
+    if (has_init) {
+        decl_initializer(type, sec, addr, 1, 0);
+        /* restore parse state if needed */
+        if (init_str.str) {
+            tok_str_free(init_str.str);
+            restore_parse_state(&saved_parse_state);
+        }
+    }
+ no_alloc: ;
+}
+
+void put_func_debug(Sym *sym)
+{
+    char buf[512];
+
+    /* stabs info */
+    /* XXX: we put here a dummy type */
+    snprintf(buf, sizeof(buf), "%s:%c1", 
+             funcname, sym->type.t & VT_STATIC ? 'f' : 'F');
+    put_stabs_r(buf, N_FUN, 0, file->line_num, 0,
+                cur_text_section, sym->c);
+    last_ind = 0;
+    last_line_num = 0;
+}
+
+/* parse an old style function declaration list */
+/* XXX: check multiple parameter */
+static void func_decl_list(Sym *func_sym)
+{
+    AttributeDef ad;
+    int v;
+    Sym *s;
+    CType btype, type;
+
+    /* parse each declaration */
+    while (tok != '{' && tok != ';' && tok != ',' && tok != TOK_EOF) {
+        if (!parse_btype(&btype, &ad)) 
+            expect("declaration list");
+        if (((btype.t & VT_BTYPE) == VT_ENUM ||
+             (btype.t & VT_BTYPE) == VT_STRUCT) && 
+            tok == ';') {
+            /* we accept no variable after */
+        } else {
+            for(;;) {
+                type = btype;
+                type_decl(&type, &ad, &v, TYPE_DIRECT);
+                /* find parameter in function parameter list */
+                s = func_sym->next;
+                while (s != NULL) {
+                    if ((s->v & ~SYM_FIELD) == v)
+                        goto found;
+                    s = s->next;
+                }
+                error("declaration for parameter '%s' but no such parameter",
+                      get_tok_str(v, NULL));
+            found:
+                /* check that no storage specifier except 'register' was given */
+                if (type.t & VT_STORAGE)
+                    error("storage class specified for '%s'", get_tok_str(v, NULL)); 
+                convert_parameter_type(&type);
+                /* we can add the type (NOTE: it could be local to the function) */
+                s->type = type;
+                /* accept other parameters */
+                if (tok == ',')
+                    next();
+                else
+                    break;
+            }
+        }
+        skip(';');
+    }
+}
+
+/* parse a function defined by symbol 'sym' and generate its code in
+   'cur_text_section' */
+static void gen_function(Sym *sym)
+{
+    ind = cur_text_section->data_offset;
+    /* NOTE: we patch the symbol size later */
+    put_extern_sym(sym, cur_text_section, ind, 0);
+    funcname = get_tok_str(sym->v, NULL);
+    func_ind = ind;
+    /* put debug symbol */
+    if (do_debug)
+        put_func_debug(sym);
+    /* push a dummy symbol to enable local sym storage */
+    sym_push2(&local_stack, SYM_FIELD, 0, 0);
+    gfunc_prolog(&sym->type);
+    rsym = 0;
+    block(NULL, NULL, NULL, NULL, 0, 0);
+    gsym(rsym);
+    gfunc_epilog();
+    cur_text_section->data_offset = ind;
+    label_pop(&global_label_stack, NULL);
+    sym_pop(&local_stack, NULL); /* reset local stack */
+    /* end of function */
+    /* patch symbol size */
+    ((Elf32_Sym *)symtab_section->data)[sym->c].st_size = 
+        ind - func_ind;
+    if (do_debug) {
+        put_stabn(N_FUN, 0, 0, ind - func_ind);
+    }
+    funcname = ""; /* for safety */
+    func_vt.t = VT_VOID; /* for safety */
+    ind = 0; /* for safety */
+}
+
+static void gen_inline_functions(void)
+{
+    Sym *sym;
+    CType *type;
+    int *str, inline_generated;
+
+    /* iterate while inline function are referenced */
+    for(;;) {
+        inline_generated = 0;
+        for(sym = global_stack; sym != NULL; sym = sym->prev) {
+            type = &sym->type;
+            if (((type->t & VT_BTYPE) == VT_FUNC) &&
+                (type->t & (VT_STATIC | VT_INLINE)) == 
+                (VT_STATIC | VT_INLINE) &&
+                sym->c != 0) {
+                /* the function was used: generate its code and
+                   convert it to a normal function */
+                str = (int *)sym->r;
+                sym->r = VT_SYM | VT_CONST;
+                type->t &= ~VT_INLINE;
+
+                macro_ptr = str;
+                next();
+                cur_text_section = text_section;
+                gen_function(sym);
+                macro_ptr = NULL; /* fail safe */
+
+                tok_str_free(str);
+                inline_generated = 1;
+            }
+        }
+        if (!inline_generated)
+            break;
+    }
+
+    /* free all remaining inline function tokens */
+    for(sym = global_stack; sym != NULL; sym = sym->prev) {
+        type = &sym->type;
+        if (((type->t & VT_BTYPE) == VT_FUNC) &&
+            (type->t & (VT_STATIC | VT_INLINE)) == 
+            (VT_STATIC | VT_INLINE)) {
+            str = (int *)sym->r;
+            tok_str_free(str);
+            sym->r = 0; /* fail safe */
+        }
+    }
+}
+
+/* 'l' is VT_LOCAL or VT_CONST to define default storage type */
+static void decl(int l)
+{
+    int v, has_init, r;
+    CType type, btype;
+    Sym *sym;
+    AttributeDef ad;
+    
+    while (1) {
+        if (!parse_btype(&btype, &ad)) {
+            /* skip redundant ';' */
+            /* XXX: find more elegant solution */
+            if (tok == ';') {
+                next();
+                continue;
+            }
+            if (l == VT_CONST &&
+                (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3)) {
+                /* global asm block */
+                asm_global_instr();
+                continue;
+            }
+            /* special test for old K&R protos without explicit int
+               type. Only accepted when defining global data */
+            if (l == VT_LOCAL || tok < TOK_DEFINE)
+                break;
+            btype.t = VT_INT;
+        }
+        if (((btype.t & VT_BTYPE) == VT_ENUM ||
+             (btype.t & VT_BTYPE) == VT_STRUCT) && 
+            tok == ';') {
+            /* we accept no variable after */
+            next();
+            continue;
+        }
+        while (1) { /* iterate thru each declaration */
+            type = btype;
+            type_decl(&type, &ad, &v, TYPE_DIRECT);
+#if 0
+            {
+                char buf[500];
+                type_to_str(buf, sizeof(buf), t, get_tok_str(v, NULL));
+                printf("type = '%s'\n", buf);
+            }
+#endif
+            if ((type.t & VT_BTYPE) == VT_FUNC) {
+                /* if old style function prototype, we accept a
+                   declaration list */
+                sym = type.ref;
+                if (sym->c == FUNC_OLD)
+                    func_decl_list(sym);
+            }
+
+            if (tok == '{') {
+                if (l == VT_LOCAL)
+                    error("cannot use local functions");
+                if (!(type.t & VT_FUNC))
+                    expect("function definition");
+
+                /* reject abstract declarators in function definition */
+                sym = type.ref;
+                while ((sym = sym->next) != NULL)
+                    if (!(sym->v & ~SYM_FIELD))
+                       expect("identifier");
+                
+                /* XXX: cannot do better now: convert extern line to static inline */
+                if ((type.t & (VT_EXTERN | VT_INLINE)) == (VT_EXTERN | VT_INLINE))
+                    type.t = (type.t & ~VT_EXTERN) | VT_STATIC;
+                
+                sym = sym_find(v);
+                if (sym) {
+                    if ((sym->type.t & VT_BTYPE) != VT_FUNC)
+                        goto func_error1;
+                    /* specific case: if not func_call defined, we put
+                       the one of the prototype */
+                    /* XXX: should have default value */
+                    if (sym->type.ref->r != FUNC_CDECL &&
+                        type.ref->r == FUNC_CDECL)
+                        type.ref->r = sym->type.ref->r;
+                    if (!is_compatible_types(&sym->type, &type)) {
+                    func_error1:
+                        error("incompatible types for redefinition of '%s'", 
+                              get_tok_str(v, NULL));
+                    }
+                    /* if symbol is already defined, then put complete type */
+                    sym->type = type;
+                } else {
+                    /* put function symbol */
+                    sym = global_identifier_push(v, type.t, 0);
+                    sym->type.ref = type.ref;
+                }
+
+                /* static inline functions are just recorded as a kind
+                   of macro. Their code will be emitted at the end of
+                   the compilation unit only if they are used */
+                if ((type.t & (VT_INLINE | VT_STATIC)) == 
+                    (VT_INLINE | VT_STATIC)) {
+                    TokenString func_str;
+                    int block_level;
+                           
+                    tok_str_new(&func_str);
+                    
+                    block_level = 0;
+                    for(;;) {
+                        int t;
+                        if (tok == TOK_EOF)
+                            error("unexpected end of file");
+                        tok_str_add_tok(&func_str);
+                        t = tok;
+                        next();
+                        if (t == '{') {
+                            block_level++;
+                        } else if (t == '}') {
+                            block_level--;
+                            if (block_level == 0)
+                                break;
+                        }
+                    }
+                    tok_str_add(&func_str, -1);
+                    tok_str_add(&func_str, 0);
+                    sym->r = (int)func_str.str;
+                } else {
+                    /* compute text section */
+                    cur_text_section = ad.section;
+                    if (!cur_text_section)
+                        cur_text_section = text_section;
+                    sym->r = VT_SYM | VT_CONST;
+                    gen_function(sym);
+#ifdef TCC_TARGET_PE
+                    if (ad.dllexport) {
+                        ((Elf32_Sym *)symtab_section->data)[sym->c].st_other |= 1;
+                    }
+#endif
+                }
+                break;
+            } else {
+                if (btype.t & VT_TYPEDEF) {
+                    /* save typedefed type  */
+                    /* XXX: test storage specifiers ? */
+                    sym = sym_push(v, &type, 0, 0);
+                    sym->type.t |= VT_TYPEDEF;
+                } else if ((type.t & VT_BTYPE) == VT_FUNC) {
+                    /* external function definition */
+                    /* specific case for func_call attribute */
+                    if (ad.func_call)
+                        type.ref->r = ad.func_call;
+                    external_sym(v, &type, 0);
+                } else {
+                    /* not lvalue if array */
+                    r = 0;
+                    if (!(type.t & VT_ARRAY))
+                        r |= lvalue_type(type.t);
+                    has_init = (tok == '=');
+                    if ((btype.t & VT_EXTERN) || 
+                        ((type.t & VT_ARRAY) && (type.t & VT_STATIC) &&
+                         !has_init && l == VT_CONST && type.ref->c < 0)) {
+                        /* external variable */
+                        /* NOTE: as GCC, uninitialized global static
+                           arrays of null size are considered as
+                           extern */
+                        external_sym(v, &type, r);
+                    } else {
+                        if (type.t & VT_STATIC)
+                            r |= VT_CONST;
+                        else
+                            r |= l;
+                        if (has_init)
+                            next();
+                        decl_initializer_alloc(&type, &ad, r, 
+                                               has_init, v, l);
+                    }
+                }
+                if (tok != ',') {
+                    skip(';');
+                    break;
+                }
+                next();
+            }
+        }
+    }
+}
+
+/* better than nothing, but needs extension to handle '-E' option
+   correctly too */
+static void preprocess_init(TCCState *s1)
+{
+    s1->include_stack_ptr = s1->include_stack;
+    /* XXX: move that before to avoid having to initialize
+       file->ifdef_stack_ptr ? */
+    s1->ifdef_stack_ptr = s1->ifdef_stack;
+    file->ifdef_stack_ptr = s1->ifdef_stack_ptr;
+
+    /* XXX: not ANSI compliant: bound checking says error */
+    vtop = vstack - 1;
+    s1->pack_stack[0] = 0;
+    s1->pack_stack_ptr = s1->pack_stack;
+}
+
+/* compile the C file opened in 'file'. Return non zero if errors. */
+static int tcc_compile(TCCState *s1)
+{
+    Sym *define_start;
+    char buf[512];
+    volatile int section_sym;
+
+#ifdef INC_DEBUG
+    printf("%s: **** new file\n", file->filename);
+#endif
+    preprocess_init(s1);
+
+    funcname = "";
+    anon_sym = SYM_FIRST_ANOM; 
+
+    /* file info: full path + filename */
+    section_sym = 0; /* avoid warning */
+    if (do_debug) {
+        section_sym = put_elf_sym(symtab_section, 0, 0, 
+                                  ELF32_ST_INFO(STB_LOCAL, STT_SECTION), 0, 
+                                  text_section->sh_num, NULL);
+        getcwd(buf, sizeof(buf));
+        pstrcat(buf, sizeof(buf), "/");
+        put_stabs_r(buf, N_SO, 0, 0, 
+                    text_section->data_offset, text_section, section_sym);
+        put_stabs_r(file->filename, N_SO, 0, 0, 
+                    text_section->data_offset, text_section, section_sym);
+    }
+    /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
+       symbols can be safely used */
+    put_elf_sym(symtab_section, 0, 0, 
+                ELF32_ST_INFO(STB_LOCAL, STT_FILE), 0, 
+                SHN_ABS, file->filename);
+
+    /* define some often used types */
+    int_type.t = VT_INT;
+
+    char_pointer_type.t = VT_BYTE;
+    mk_pointer(&char_pointer_type);
+
+    func_old_type.t = VT_FUNC;
+    func_old_type.ref = sym_push(SYM_FIELD, &int_type, FUNC_CDECL, FUNC_OLD);
+
+#if 0
+    /* define 'void *alloca(unsigned int)' builtin function */
+    {
+        Sym *s1;
+
+        p = anon_sym++;
+        sym = sym_push(p, mk_pointer(VT_VOID), FUNC_CDECL, FUNC_NEW);
+        s1 = sym_push(SYM_FIELD, VT_UNSIGNED | VT_INT, 0, 0);
+        s1->next = NULL;
+        sym->next = s1;
+        sym_push(TOK_alloca, VT_FUNC | (p << VT_STRUCT_SHIFT), VT_CONST, 0);
+    }
+#endif
+
+    define_start = define_stack;
+
+    if (setjmp(s1->error_jmp_buf) == 0) {
+        s1->nb_errors = 0;
+        s1->error_set_jmp_enabled = 1;
+
+        ch = file->buf_ptr[0];
+        tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
+        parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM;
+        next();
+        decl(VT_CONST);
+        if (tok != TOK_EOF)
+            expect("declaration");
+
+        /* end of translation unit info */
+        if (do_debug) {
+            put_stabs_r(NULL, N_SO, 0, 0, 
+                        text_section->data_offset, text_section, section_sym);
+        }
+    }
+    s1->error_set_jmp_enabled = 0;
+
+    /* reset define stack, but leave -Dsymbols (may be incorrect if
+       they are undefined) */
+    free_defines(define_start); 
+
+    gen_inline_functions();
+
+    sym_pop(&global_stack, NULL);
+
+    return s1->nb_errors != 0 ? -1 : 0;
+}
+
+#ifdef LIBTCC
+int tcc_compile_string(TCCState *s, const char *str)
+{
+    BufferedFile bf1, *bf = &bf1;
+    int ret, len;
+    char *buf;
+
+    /* init file structure */
+    bf->fd = -1;
+    /* XXX: avoid copying */
+    len = strlen(str);
+    buf = tcc_malloc(len + 1);
+    if (!buf)
+        return -1;
+    memcpy(buf, str, len);
+    buf[len] = CH_EOB;
+    bf->buf_ptr = buf;
+    bf->buf_end = buf + len;
+    pstrcpy(bf->filename, sizeof(bf->filename), "<string>");
+    bf->line_num = 1;
+    file = bf;
+    
+    ret = tcc_compile(s);
+    
+    tcc_free(buf);
+
+    /* currently, no need to close */
+    return ret;
+}
+#endif
+
+/* define a preprocessor symbol. A value can also be provided with the '=' operator */
+void tcc_define_symbol(TCCState *s1, const char *sym, const char *value)
+{
+    BufferedFile bf1, *bf = &bf1;
+
+    pstrcpy(bf->buffer, IO_BUF_SIZE, sym);
+    pstrcat(bf->buffer, IO_BUF_SIZE, " ");
+    /* default value */
+    if (!value) 
+        value = "1";
+    pstrcat(bf->buffer, IO_BUF_SIZE, value);
+    
+    /* init file structure */
+    bf->fd = -1;
+    bf->buf_ptr = bf->buffer;
+    bf->buf_end = bf->buffer + strlen(bf->buffer);
+    *bf->buf_end = CH_EOB;
+    bf->filename[0] = '\0';
+    bf->line_num = 1;
+    file = bf;
+    
+    s1->include_stack_ptr = s1->include_stack;
+
+    /* parse with define parser */
+    ch = file->buf_ptr[0];
+    next_nomacro();
+    parse_define();
+    file = NULL;
+}
+
+/* undefine a preprocessor symbol */
+void tcc_undefine_symbol(TCCState *s1, const char *sym)
+{
+    TokenSym *ts;
+    Sym *s;
+    ts = tok_alloc(sym, strlen(sym));
+    s = define_find(ts->tok);
+    /* undefine symbol by putting an invalid name */
+    if (s)
+        define_undef(s);
+}
+
+#ifdef CONFIG_TCC_ASM
+
+#ifdef TCC_TARGET_I386
+// njn: inlined i386-asm.c
+//#include "i386-asm.c"
+//---------------------------------------------------------------------------
+/*
+ *  i386 specific functions for TCC assembler
+ * 
+ *  Copyright (c) 2001, 2002 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define MAX_OPERANDS 3
+
+typedef struct ASMInstr {
+    uint16_t sym;
+    uint16_t opcode;
+    uint16_t instr_type;
+#define OPC_JMP       0x01  /* jmp operand */
+#define OPC_B         0x02  /* only used zith OPC_WL */
+#define OPC_WL        0x04  /* accepts w, l or no suffix */
+#define OPC_BWL       (OPC_B | OPC_WL) /* accepts b, w, l or no suffix */
+#define OPC_REG       0x08 /* register is added to opcode */
+#define OPC_MODRM     0x10 /* modrm encoding */
+#define OPC_FWAIT     0x20 /* add fwait opcode */
+#define OPC_TEST      0x40 /* test opcodes */
+#define OPC_SHIFT     0x80 /* shift opcodes */
+#define OPC_D16      0x0100 /* generate data16 prefix */
+#define OPC_ARITH    0x0200 /* arithmetic opcodes */
+#define OPC_SHORTJMP 0x0400 /* short jmp operand */
+#define OPC_FARITH   0x0800 /* FPU arithmetic opcodes */
+#define OPC_GROUP_SHIFT 13
+
+/* in order to compress the operand type, we use specific operands and
+   we or only with EA  */ 
+#define OPT_REG8  0 /* warning: value is hardcoded from TOK_ASM_xxx */
+#define OPT_REG16 1 /* warning: value is hardcoded from TOK_ASM_xxx */
+#define OPT_REG32 2 /* warning: value is hardcoded from TOK_ASM_xxx */
+#define OPT_MMX   3 /* warning: value is hardcoded from TOK_ASM_xxx */
+#define OPT_SSE   4 /* warning: value is hardcoded from TOK_ASM_xxx */
+#define OPT_CR    5 /* warning: value is hardcoded from TOK_ASM_xxx */
+#define OPT_TR    6 /* warning: value is hardcoded from TOK_ASM_xxx */
+#define OPT_DB    7 /* warning: value is hardcoded from TOK_ASM_xxx */
+#define OPT_SEG   8
+#define OPT_ST    9
+#define OPT_IM8   10
+#define OPT_IM8S  11
+#define OPT_IM16  12
+#define OPT_IM32  13
+#define OPT_EAX   14 /* %al, %ax or %eax register */
+#define OPT_ST0   15 /* %st(0) register */
+#define OPT_CL    16 /* %cl register */
+#define OPT_DX    17 /* %dx register */
+#define OPT_ADDR  18 /* OP_EA with only offset */
+#define OPT_INDIR 19 /* *(expr) */
+
+/* composite types */ 
+#define OPT_COMPOSITE_FIRST   20
+#define OPT_IM       20 /* IM8 | IM16 | IM32 */
+#define OPT_REG      21 /* REG8 | REG16 | REG32 */ 
+#define OPT_REGW     22 /* REG16 | REG32 */
+#define OPT_IMW      23 /* IM16 | IM32 */ 
+
+/* can be ored with any OPT_xxx */
+#define OPT_EA    0x80
+
+    uint8_t nb_ops;
+    uint8_t op_type[MAX_OPERANDS]; /* see OP_xxx */
+} ASMInstr;
+
+typedef struct Operand {
+    uint32_t type;
+#define OP_REG8   (1 << OPT_REG8)
+#define OP_REG16  (1 << OPT_REG16)
+#define OP_REG32  (1 << OPT_REG32)
+#define OP_MMX    (1 << OPT_MMX)
+#define OP_SSE    (1 << OPT_SSE)
+#define OP_CR     (1 << OPT_CR)
+#define OP_TR     (1 << OPT_TR)
+#define OP_DB     (1 << OPT_DB)
+#define OP_SEG    (1 << OPT_SEG)
+#define OP_ST     (1 << OPT_ST)
+#define OP_IM8    (1 << OPT_IM8)
+#define OP_IM8S   (1 << OPT_IM8S)
+#define OP_IM16   (1 << OPT_IM16)
+#define OP_IM32   (1 << OPT_IM32)
+#define OP_EAX    (1 << OPT_EAX)
+#define OP_ST0    (1 << OPT_ST0)
+#define OP_CL     (1 << OPT_CL)
+#define OP_DX     (1 << OPT_DX)
+#define OP_ADDR   (1 << OPT_ADDR)
+#define OP_INDIR  (1 << OPT_INDIR)
+
+#define OP_EA     0x40000000
+#define OP_REG    (OP_REG8 | OP_REG16 | OP_REG32)
+#define OP_IM     OP_IM32
+    int8_t  reg; /* register, -1 if none */
+    int8_t  reg2; /* second register, -1 if none */
+    uint8_t shift;
+    ExprValue e;
+} Operand;
+
+static const uint8_t reg_to_size[5] = {
+    [OP_REG8] = 0,
+    [OP_REG16] = 1,
+    [OP_REG32] = 2,
+};
+    
+#define WORD_PREFIX_OPCODE 0x66
+
+#define NB_TEST_OPCODES 30
+
+static const uint8_t test_bits[NB_TEST_OPCODES] = {
+ 0x00, /* o */
+ 0x01, /* no */
+ 0x02, /* b */
+ 0x02, /* c */
+ 0x02, /* nae */
+ 0x03, /* nb */
+ 0x03, /* nc */
+ 0x03, /* ae */
+ 0x04, /* e */
+ 0x04, /* z */
+ 0x05, /* ne */
+ 0x05, /* nz */
+ 0x06, /* be */
+ 0x06, /* na */
+ 0x07, /* nbe */
+ 0x07, /* a */
+ 0x08, /* s */
+ 0x09, /* ns */
+ 0x0a, /* p */
+ 0x0a, /* pe */
+ 0x0b, /* np */
+ 0x0b, /* po */
+ 0x0c, /* l */
+ 0x0c, /* nge */
+ 0x0d, /* nl */
+ 0x0d, /* ge */
+ 0x0e, /* le */
+ 0x0e, /* ng */
+ 0x0f, /* nle */
+ 0x0f, /* g */
+};
+
+static const ASMInstr asm_instrs[] = {
+#define ALT(x) x
+#define DEF_ASM_OP0(name, opcode)
+#define DEF_ASM_OP0L(name, opcode, group, instr_type) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 0 },
+#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 1, { op0 }},
+#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 2, { op0, op1 }},
+#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 3, { op0, op1, op2 }},
+// njn: inlined i386-asm.h
+//#include "i386-asm.h"
+//---------------------------------------------------------------------------
+     DEF_ASM_OP0(pusha, 0x60) /* must be first OP0 */
+     DEF_ASM_OP0(popa, 0x61)
+     DEF_ASM_OP0(clc, 0xf8)
+     DEF_ASM_OP0(cld, 0xfc)
+     DEF_ASM_OP0(cli, 0xfa)
+     DEF_ASM_OP0(clts, 0x0f06)
+     DEF_ASM_OP0(cmc, 0xf5)
+     DEF_ASM_OP0(lahf, 0x9f)
+     DEF_ASM_OP0(sahf, 0x9e)
+     DEF_ASM_OP0(pushfl, 0x9c)
+     DEF_ASM_OP0(popfl, 0x9d)
+     DEF_ASM_OP0(pushf, 0x9c)
+     DEF_ASM_OP0(popf, 0x9d)
+     DEF_ASM_OP0(stc, 0xf9)
+     DEF_ASM_OP0(std, 0xfd)
+     DEF_ASM_OP0(sti, 0xfb)
+     DEF_ASM_OP0(aaa, 0x37)
+     DEF_ASM_OP0(aas, 0x3f)
+     DEF_ASM_OP0(daa, 0x27)
+     DEF_ASM_OP0(das, 0x2f)
+     DEF_ASM_OP0(aad, 0xd50a)
+     DEF_ASM_OP0(aam, 0xd40a)
+     DEF_ASM_OP0(cbw, 0x6698)
+     DEF_ASM_OP0(cwd, 0x6699)
+     DEF_ASM_OP0(cwde, 0x98)
+     DEF_ASM_OP0(cdq, 0x99)
+     DEF_ASM_OP0(cbtw, 0x6698)
+     DEF_ASM_OP0(cwtl, 0x98)
+     DEF_ASM_OP0(cwtd, 0x6699)
+     DEF_ASM_OP0(cltd, 0x99)
+     DEF_ASM_OP0(int3, 0xcc)
+     DEF_ASM_OP0(into, 0xce)
+     DEF_ASM_OP0(iret, 0xcf)
+     DEF_ASM_OP0(rsm, 0x0faa)
+     DEF_ASM_OP0(hlt, 0xf4)
+     DEF_ASM_OP0(wait, 0x9b)
+     DEF_ASM_OP0(nop, 0x90)
+     DEF_ASM_OP0(xlat, 0xd7)
+
+     /* strings */
+ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWL))
+
+     /* bits */
+     
+ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
+
+ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+     /* prefixes */
+     DEF_ASM_OP0(aword, 0x67)
+     DEF_ASM_OP0(addr16, 0x67)
+     DEF_ASM_OP0(word, 0x66)
+     DEF_ASM_OP0(data16, 0x66)
+     DEF_ASM_OP0(lock, 0xf0)
+     DEF_ASM_OP0(rep, 0xf3)
+     DEF_ASM_OP0(repe, 0xf3)
+     DEF_ASM_OP0(repz, 0xf3)
+     DEF_ASM_OP0(repne, 0xf2)
+     DEF_ASM_OP0(repnz, 0xf2)
+             
+     DEF_ASM_OP0(invd, 0x0f08)
+     DEF_ASM_OP0(wbinvd, 0x0f09)
+     DEF_ASM_OP0(cpuid, 0x0fa2)
+     DEF_ASM_OP0(wrmsr, 0x0f30)
+     DEF_ASM_OP0(rdtsc, 0x0f31)
+     DEF_ASM_OP0(rdmsr, 0x0f32)
+     DEF_ASM_OP0(rdpmc, 0x0f33)
+     DEF_ASM_OP0(ud2, 0x0f0b)
+
+     /* NOTE: we took the same order as gas opcode definition order */
+ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWL, OPT_ADDR, OPT_EAX))
+ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWL, OPT_EAX, OPT_ADDR))
+ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWL, OPT_IM, OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WL, OPT_SEG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_SEG))
+
+ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WL, OPT_CR, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WL, OPT_DB, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WL, OPT_TR, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_CR))
+ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_DB))
+ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_TR))
+
+ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16))
+ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
+
+ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP1(pushw, 0x6a, 0, OPC_WL, OPT_IM8S))
+ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WL, OPT_IM32))
+ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WL, OPT_SEG))
+
+ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WL, OPT_SEG))
+
+ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_REG, OPT_EAX))
+ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_EAX, OPT_REG))
+ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+
+ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX))
+ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8))
+ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX))
+ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX))
+
+ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8))
+ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8))
+ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX))
+ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX))
+
+ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WL, OPT_EA, OPT_REG))
+
+ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+
+     /* arith */
+ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */
+ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWL, OPT_IM, OPT_EAX))
+ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WL, OPT_IM8S, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWL, OPT_IM, OPT_EAX))
+ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WL, OPT_REG | OPT_EA, OPT_REG))
+ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW))
+ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW))
+
+ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
+ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
+
+     /* shifts */
+ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
+
+ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR))
+ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR))
+ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR))
+ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR))
+
+ALT(DEF_ASM_OP2(lcall, 0x9a, 0, 0, OPT_IM16, OPT_IM32))
+ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA))
+ALT(DEF_ASM_OP2(ljmp, 0xea, 0, 0, OPT_IM16, OPT_IM32))
+ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA))
+
+ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8))
+ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
+    DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8)
+    DEF_ASM_OP0(leave, 0xc9)
+    DEF_ASM_OP0(ret, 0xc3)
+ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16))
+    DEF_ASM_OP0(lret, 0xcb)
+ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
+
+ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR))
+    DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR)
+     
+     /* float */
+     /* specific fcomp handling */
+ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0))
+
+ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST))
+ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
+ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH))
+ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST))
+ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
+ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
+ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH))
+ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+
+     DEF_ASM_OP0(fucompp, 0xdae9)
+     DEF_ASM_OP0(ftst, 0xd9e4)
+     DEF_ASM_OP0(fxam, 0xd9e5)
+     DEF_ASM_OP0(fld1, 0xd9e8)
+     DEF_ASM_OP0(fldl2t, 0xd9e9)
+     DEF_ASM_OP0(fldl2e, 0xd9ea)
+     DEF_ASM_OP0(fldpi, 0xd9eb)
+     DEF_ASM_OP0(fldlg2, 0xd9ec)
+     DEF_ASM_OP0(fldln2, 0xd9ed)
+     DEF_ASM_OP0(fldz, 0xd9ee)
+
+     DEF_ASM_OP0(f2xm1, 0xd9f0)
+     DEF_ASM_OP0(fyl2x, 0xd9f1)
+     DEF_ASM_OP0(fptan, 0xd9f2)
+     DEF_ASM_OP0(fpatan, 0xd9f3)
+     DEF_ASM_OP0(fxtract, 0xd9f4)
+     DEF_ASM_OP0(fprem1, 0xd9f5)
+     DEF_ASM_OP0(fdecstp, 0xd9f6)
+     DEF_ASM_OP0(fincstp, 0xd9f7)
+     DEF_ASM_OP0(fprem, 0xd9f8)
+     DEF_ASM_OP0(fyl2xp1, 0xd9f9)
+     DEF_ASM_OP0(fsqrt, 0xd9fa)
+     DEF_ASM_OP0(fsincos, 0xd9fb)
+     DEF_ASM_OP0(frndint, 0xd9fc)
+     DEF_ASM_OP0(fscale, 0xd9fd)
+     DEF_ASM_OP0(fsin, 0xd9fe)
+     DEF_ASM_OP0(fcos, 0xd9ff)
+     DEF_ASM_OP0(fchs, 0xd9e0)
+     DEF_ASM_OP0(fabs, 0xd9e1)
+     DEF_ASM_OP0(fninit, 0xdbe3)
+     DEF_ASM_OP0(fnclex, 0xdbe2)
+     DEF_ASM_OP0(fnop, 0xd9d0)
+     DEF_ASM_OP0(fwait, 0x9b)
+
+    /* fp load */
+    DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA)
+ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA))
+    DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA)
+    DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA)
+    
+    /* fp store */
+    DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA)
+ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA))
+    DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA)
+
+    DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA)
+
+    /* exchange */
+    DEF_ASM_OP0(fxch, 0xd9c9)
+ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST))
+
+    /* misc FPU */
+    DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST )
+    DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST )
+
+    DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT)
+    DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )
+    DEF_ASM_OP0(fnstsw, 0xdfe0)
+ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX ))
+ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA ))
+    DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX )
+ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT))
+ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ))
+    DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT)
+    DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
+    DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
+    DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST )
+    DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST )
+    DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA )
+
+    /* segments */
+    DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA)
+    DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32)
+    DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG)
+    DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG)
+ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG))
+    DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG)
+    DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA)
+    DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA)
+    DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA)
+    DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA)
+    DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA)
+
+    /* 486 */
+    DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 )
+ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
+ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
+    DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA )
+
+    DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA)
+    DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA)
+
+    /* pentium */
+    DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA )
+    
+    /* pentium pro */
+    ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+
+    DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+
+    DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+
+    /* mmx */
+    DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */
+    DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX )
+ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 ))
+    DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX ))
+    DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+
+#undef ALT
+#undef DEF_ASM_OP0
+#undef DEF_ASM_OP0L
+#undef DEF_ASM_OP1
+#undef DEF_ASM_OP2
+#undef DEF_ASM_OP3
+//---------------------------------------------------------------------------
+
+    /* last operation */
+    { 0, },
+};
+
+static const uint16_t op0_codes[] = {
+#define ALT(x)
+#define DEF_ASM_OP0(x, opcode) opcode,
+#define DEF_ASM_OP0L(name, opcode, group, instr_type)
+#define DEF_ASM_OP1(name, opcode, group, instr_type, op0)
+#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1)
+#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2)
+// njn: inlined i386-asm.h
+//#include "i386-asm.h"
+//---------------------------------------------------------------------------
+     DEF_ASM_OP0(pusha, 0x60) /* must be first OP0 */
+     DEF_ASM_OP0(popa, 0x61)
+     DEF_ASM_OP0(clc, 0xf8)
+     DEF_ASM_OP0(cld, 0xfc)
+     DEF_ASM_OP0(cli, 0xfa)
+     DEF_ASM_OP0(clts, 0x0f06)
+     DEF_ASM_OP0(cmc, 0xf5)
+     DEF_ASM_OP0(lahf, 0x9f)
+     DEF_ASM_OP0(sahf, 0x9e)
+     DEF_ASM_OP0(pushfl, 0x9c)
+     DEF_ASM_OP0(popfl, 0x9d)
+     DEF_ASM_OP0(pushf, 0x9c)
+     DEF_ASM_OP0(popf, 0x9d)
+     DEF_ASM_OP0(stc, 0xf9)
+     DEF_ASM_OP0(std, 0xfd)
+     DEF_ASM_OP0(sti, 0xfb)
+     DEF_ASM_OP0(aaa, 0x37)
+     DEF_ASM_OP0(aas, 0x3f)
+     DEF_ASM_OP0(daa, 0x27)
+     DEF_ASM_OP0(das, 0x2f)
+     DEF_ASM_OP0(aad, 0xd50a)
+     DEF_ASM_OP0(aam, 0xd40a)
+     DEF_ASM_OP0(cbw, 0x6698)
+     DEF_ASM_OP0(cwd, 0x6699)
+     DEF_ASM_OP0(cwde, 0x98)
+     DEF_ASM_OP0(cdq, 0x99)
+     DEF_ASM_OP0(cbtw, 0x6698)
+     DEF_ASM_OP0(cwtl, 0x98)
+     DEF_ASM_OP0(cwtd, 0x6699)
+     DEF_ASM_OP0(cltd, 0x99)
+     DEF_ASM_OP0(int3, 0xcc)
+     DEF_ASM_OP0(into, 0xce)
+     DEF_ASM_OP0(iret, 0xcf)
+     DEF_ASM_OP0(rsm, 0x0faa)
+     DEF_ASM_OP0(hlt, 0xf4)
+     DEF_ASM_OP0(wait, 0x9b)
+     DEF_ASM_OP0(nop, 0x90)
+     DEF_ASM_OP0(xlat, 0xd7)
+
+     /* strings */
+ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWL))
+
+     /* bits */
+     
+ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
+
+ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+     /* prefixes */
+     DEF_ASM_OP0(aword, 0x67)
+     DEF_ASM_OP0(addr16, 0x67)
+     DEF_ASM_OP0(word, 0x66)
+     DEF_ASM_OP0(data16, 0x66)
+     DEF_ASM_OP0(lock, 0xf0)
+     DEF_ASM_OP0(rep, 0xf3)
+     DEF_ASM_OP0(repe, 0xf3)
+     DEF_ASM_OP0(repz, 0xf3)
+     DEF_ASM_OP0(repne, 0xf2)
+     DEF_ASM_OP0(repnz, 0xf2)
+             
+     DEF_ASM_OP0(invd, 0x0f08)
+     DEF_ASM_OP0(wbinvd, 0x0f09)
+     DEF_ASM_OP0(cpuid, 0x0fa2)
+     DEF_ASM_OP0(wrmsr, 0x0f30)
+     DEF_ASM_OP0(rdtsc, 0x0f31)
+     DEF_ASM_OP0(rdmsr, 0x0f32)
+     DEF_ASM_OP0(rdpmc, 0x0f33)
+     DEF_ASM_OP0(ud2, 0x0f0b)
+
+     /* NOTE: we took the same order as gas opcode definition order */
+ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWL, OPT_ADDR, OPT_EAX))
+ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWL, OPT_EAX, OPT_ADDR))
+ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWL, OPT_IM, OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WL, OPT_SEG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_SEG))
+
+ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WL, OPT_CR, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WL, OPT_DB, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WL, OPT_TR, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_CR))
+ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_DB))
+ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_TR))
+
+ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16))
+ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
+
+ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP1(pushw, 0x6a, 0, OPC_WL, OPT_IM8S))
+ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WL, OPT_IM32))
+ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WL, OPT_SEG))
+
+ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WL, OPT_SEG))
+
+ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_REG, OPT_EAX))
+ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_EAX, OPT_REG))
+ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+
+ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX))
+ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8))
+ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX))
+ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX))
+
+ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8))
+ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8))
+ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX))
+ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX))
+
+ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WL, OPT_EA, OPT_REG))
+
+ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+
+     /* arith */
+ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */
+ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWL, OPT_IM, OPT_EAX))
+ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WL, OPT_IM8S, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWL, OPT_IM, OPT_EAX))
+ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WL, OPT_REG | OPT_EA, OPT_REG))
+ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW))
+ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW))
+
+ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
+ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
+
+     /* shifts */
+ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
+
+ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR))
+ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR))
+ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR))
+ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR))
+
+ALT(DEF_ASM_OP2(lcall, 0x9a, 0, 0, OPT_IM16, OPT_IM32))
+ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA))
+ALT(DEF_ASM_OP2(ljmp, 0xea, 0, 0, OPT_IM16, OPT_IM32))
+ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA))
+
+ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8))
+ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
+    DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8)
+    DEF_ASM_OP0(leave, 0xc9)
+    DEF_ASM_OP0(ret, 0xc3)
+ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16))
+    DEF_ASM_OP0(lret, 0xcb)
+ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
+
+ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR))
+    DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR)
+    DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR)
+     
+     /* float */
+     /* specific fcomp handling */
+ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0))
+
+ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST))
+ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
+ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH))
+ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST))
+ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
+ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
+ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH))
+ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+
+     DEF_ASM_OP0(fucompp, 0xdae9)
+     DEF_ASM_OP0(ftst, 0xd9e4)
+     DEF_ASM_OP0(fxam, 0xd9e5)
+     DEF_ASM_OP0(fld1, 0xd9e8)
+     DEF_ASM_OP0(fldl2t, 0xd9e9)
+     DEF_ASM_OP0(fldl2e, 0xd9ea)
+     DEF_ASM_OP0(fldpi, 0xd9eb)
+     DEF_ASM_OP0(fldlg2, 0xd9ec)
+     DEF_ASM_OP0(fldln2, 0xd9ed)
+     DEF_ASM_OP0(fldz, 0xd9ee)
+
+     DEF_ASM_OP0(f2xm1, 0xd9f0)
+     DEF_ASM_OP0(fyl2x, 0xd9f1)
+     DEF_ASM_OP0(fptan, 0xd9f2)
+     DEF_ASM_OP0(fpatan, 0xd9f3)
+     DEF_ASM_OP0(fxtract, 0xd9f4)
+     DEF_ASM_OP0(fprem1, 0xd9f5)
+     DEF_ASM_OP0(fdecstp, 0xd9f6)
+     DEF_ASM_OP0(fincstp, 0xd9f7)
+     DEF_ASM_OP0(fprem, 0xd9f8)
+     DEF_ASM_OP0(fyl2xp1, 0xd9f9)
+     DEF_ASM_OP0(fsqrt, 0xd9fa)
+     DEF_ASM_OP0(fsincos, 0xd9fb)
+     DEF_ASM_OP0(frndint, 0xd9fc)
+     DEF_ASM_OP0(fscale, 0xd9fd)
+     DEF_ASM_OP0(fsin, 0xd9fe)
+     DEF_ASM_OP0(fcos, 0xd9ff)
+     DEF_ASM_OP0(fchs, 0xd9e0)
+     DEF_ASM_OP0(fabs, 0xd9e1)
+     DEF_ASM_OP0(fninit, 0xdbe3)
+     DEF_ASM_OP0(fnclex, 0xdbe2)
+     DEF_ASM_OP0(fnop, 0xd9d0)
+     DEF_ASM_OP0(fwait, 0x9b)
+
+    /* fp load */
+    DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA)
+ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA))
+    DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA)
+    DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA)
+    
+    /* fp store */
+    DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA)
+ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA))
+    DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA)
+
+    DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST)
+    DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA)
+
+    /* exchange */
+    DEF_ASM_OP0(fxch, 0xd9c9)
+ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST))
+
+    /* misc FPU */
+    DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST )
+    DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST )
+
+    DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT)
+    DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )
+    DEF_ASM_OP0(fnstsw, 0xdfe0)
+ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX ))
+ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA ))
+    DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX )
+ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT))
+ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ))
+    DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT)
+    DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
+    DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
+    DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST )
+    DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST )
+    DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA )
+    DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA )
+
+    /* segments */
+    DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA)
+    DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32)
+    DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG)
+    DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG)
+ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG))
+    DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG)
+    DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA)
+    DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA)
+    DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA)
+    DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA)
+    DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA)
+    DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA)
+
+    /* 486 */
+    DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 )
+ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
+ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
+    DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA )
+
+    DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA)
+    DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA)
+
+    /* pentium */
+    DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA )
+    
+    /* pentium pro */
+    ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+
+    DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+
+    DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+    DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+
+    /* mmx */
+    DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */
+    DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX )
+ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 ))
+    DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX ))
+    DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+    DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+    DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+
+#undef ALT
+#undef DEF_ASM_OP0
+#undef DEF_ASM_OP0L
+#undef DEF_ASM_OP1
+#undef DEF_ASM_OP2
+#undef DEF_ASM_OP3
+//---------------------------------------------------------------------------
+};
+
+static inline int get_reg_shift(TCCState *s1)
+{
+    int shift, v;
+
+    v = asm_int_expr(s1);
+    switch(v) {
+    case 1:
+        shift = 0;
+        break;
+    case 2:
+        shift = 1;
+        break;
+    case 4:
+        shift = 2;
+        break;
+    case 8:
+        shift = 3;
+        break;
+    default:
+        expect("1, 2, 4 or 8 constant");
+        shift = 0;
+        break;
+    }
+    return shift;
+}
+
+static int asm_parse_reg(void)
+{
+    int reg;
+    if (tok != '%')
+        goto error_32;
+    next();
+    if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) {
+        reg = tok - TOK_ASM_eax;
+        next();
+        return reg;
+    } else {
+    error_32:
+        expect("32 bit register");
+        return 0;
+    }
+}
+
+static void parse_operand(TCCState *s1, Operand *op)
+{
+    ExprValue e;
+    int reg, indir;
+    const char *p;
+
+    indir = 0;
+    if (tok == '*') {
+        next();
+        indir = OP_INDIR;
+    }
+
+    if (tok == '%') {
+        next();
+        if (tok >= TOK_ASM_al && tok <= TOK_ASM_db7) {
+            reg = tok - TOK_ASM_al;
+            op->type = 1 << (reg >> 3); /* WARNING: do not change constant order */
+            op->reg = reg & 7;
+            if ((op->type & OP_REG) && op->reg == TREG_EAX)
+                op->type |= OP_EAX;
+            else if (op->type == OP_REG8 && op->reg == TREG_ECX)
+                op->type |= OP_CL;
+            else if (op->type == OP_REG16 && op->reg == TREG_EDX)
+                op->type |= OP_DX;
+        } else if (tok >= TOK_ASM_dr0 && tok <= TOK_ASM_dr7) {
+            op->type = OP_DB;
+            op->reg = tok - TOK_ASM_dr0;
+        } else if (tok >= TOK_ASM_es && tok <= TOK_ASM_gs) {
+            op->type = OP_SEG;
+            op->reg = tok - TOK_ASM_es;
+        } else if (tok == TOK_ASM_st) {
+            op->type = OP_ST;
+            op->reg = 0;
+            next();
+            if (tok == '(') {
+                next();
+                if (tok != TOK_PPNUM)
+                    goto reg_error;
+                p = tokc.cstr->data;
+                reg = p[0] - '0';
+                if ((unsigned)reg >= 8 || p[1] != '\0')
+                    goto reg_error;
+                op->reg = reg;
+                next();
+                skip(')');
+            }
+            if (op->reg == 0)
+                op->type |= OP_ST0;
+            goto no_skip;
+        } else {
+        reg_error:
+            error("unknown register");
+        }
+        next();
+    no_skip: ;
+    } else if (tok == '$') {
+        /* constant value */
+        next();
+        asm_expr(s1, &e);
+        op->type = OP_IM32;
+        op->e.v = e.v;
+        op->e.sym = e.sym;
+        if (!op->e.sym) {
+            if (op->e.v == (uint8_t)op->e.v)
+                op->type |= OP_IM8;
+            if (op->e.v == (int8_t)op->e.v)
+                op->type |= OP_IM8S;
+            if (op->e.v == (uint16_t)op->e.v)
+                op->type |= OP_IM16;
+        }
+    } else {
+        /* address(reg,reg2,shift) with all variants */
+        op->type = OP_EA;
+        op->reg = -1;
+        op->reg2 = -1;
+        op->shift = 0;
+        if (tok != '(') {
+            asm_expr(s1, &e);
+            op->e.v = e.v;
+            op->e.sym = e.sym;
+        } else {
+            op->e.v = 0;
+            op->e.sym = NULL;
+        }
+        if (tok == '(') {
+            next();
+            if (tok != ',') {
+                op->reg = asm_parse_reg();
+            }
+            if (tok == ',') {
+                next();
+                if (tok != ',') {
+                    op->reg2 = asm_parse_reg();
+                } 
+                skip(',');
+                op->shift = get_reg_shift(s1);
+            }
+            skip(')');
+        }
+        if (op->reg == -1 && op->reg2 == -1)
+            op->type |= OP_ADDR;
+    }
+    op->type |= indir;
+}
+
+/* XXX: unify with C code output ? */
+static void gen_expr32(ExprValue *pe)
+{
+    if (pe->sym)
+        greloc(cur_text_section, pe->sym, ind, R_386_32);
+    gen_le32(pe->v);
+}
+
+/* XXX: unify with C code output ? */
+static void gen_disp32(ExprValue *pe)
+{
+    Sym *sym;
+    sym = pe->sym;
+    if (sym) {
+        if (sym->r == cur_text_section->sh_num) {
+            /* same section: we can output an absolute value. Note
+               that the TCC compiler behaves differently here because
+               it always outputs a relocation to ease (future) code
+               elimination in the linker */
+            gen_le32(pe->v + (long)sym->next - ind - 4);
+        } else {
+            greloc(cur_text_section, sym, ind, R_386_PC32);
+            gen_le32(pe->v - 4);
+        }
+    } else {
+        /* put an empty PC32 relocation */
+        put_elf_reloc(symtab_section, cur_text_section, 
+                      ind, R_386_PC32, 0);
+        gen_le32(pe->v - 4);
+    }
+}
+
+
+static void gen_le16(int v)
+{
+    g(v);
+    g(v >> 8);
+}
+
+/* generate the modrm operand */
+static inline void asm_modrm(int reg, Operand *op)
+{
+    int mod, reg1, reg2, sib_reg1;
+
+    if (op->type & (OP_REG | OP_MMX | OP_SSE)) {
+        g(0xc0 + (reg << 3) + op->reg);
+    } else if (op->reg == -1 && op->reg2 == -1) {
+        /* displacement only */
+        g(0x05 + (reg << 3));
+        gen_expr32(&op->e);
+    } else {
+        sib_reg1 = op->reg;
+        /* fist compute displacement encoding */
+        if (sib_reg1 == -1) {
+            sib_reg1 = 5;
+            mod = 0x00;
+        } else if (op->e.v == 0 && !op->e.sym && op->reg != 5) {
+            mod = 0x00;
+        } else if (op->e.v == (int8_t)op->e.v && !op->e.sym) {
+            mod = 0x40;
+        } else {
+            mod = 0x80;
+        }
+        /* compute if sib byte needed */
+        reg1 = op->reg;
+        if (op->reg2 != -1)
+            reg1 = 4;
+        g(mod + (reg << 3) + reg1);
+        if (reg1 == 4) {
+            /* add sib byte */
+            reg2 = op->reg2;
+            if (reg2 == -1)
+                reg2 = 4; /* indicate no index */
+            g((op->shift << 6) + (reg2 << 3) + sib_reg1);
+        }
+
+        /* add offset */
+        if (mod == 0x40) {
+            g(op->e.v);
+        } else if (mod == 0x80 || op->reg == -1) {
+            gen_expr32(&op->e);
+        }
+    }
+}
+
+static void asm_opcode(TCCState *s1, int opcode)
+{
+    const ASMInstr *pa;
+    int i, modrm_index, reg, v, op1, is_short_jmp;
+    int nb_ops, s, ss;
+    Operand ops[MAX_OPERANDS], *pop;
+    int op_type[3]; /* decoded op type */
+
+    /* get operands */
+    pop = ops;
+    nb_ops = 0;
+    for(;;) {
+        if (tok == ';' || tok == TOK_LINEFEED)
+            break;
+        if (nb_ops >= MAX_OPERANDS) {
+            error("incorrect number of operands");
+        }
+        parse_operand(s1, pop);
+        pop++;
+        nb_ops++;
+        if (tok != ',')
+            break;
+        next();
+    }
+
+    is_short_jmp = 0;
+    s = 0; /* avoid warning */
+    
+    /* optimize matching by using a lookup table (no hashing is needed
+       !) */
+    for(pa = asm_instrs; pa->sym != 0; pa++) {
+        s = 0;
+        if (pa->instr_type & OPC_FARITH) {
+            v = opcode - pa->sym;
+            if (!((unsigned)v < 8 * 6 && (v % 6) == 0))
+                continue;
+        } else if (pa->instr_type & OPC_ARITH) {
+            if (!(opcode >= pa->sym && opcode < pa->sym + 8 * 4))
+                continue;
+            goto compute_size;
+        } else if (pa->instr_type & OPC_SHIFT) {
+            if (!(opcode >= pa->sym && opcode < pa->sym + 7 * 4))
+                continue;
+            goto compute_size;
+        } else if (pa->instr_type & OPC_TEST) {
+            if (!(opcode >= pa->sym && opcode < pa->sym + NB_TEST_OPCODES))
+                continue;
+        } else if (pa->instr_type & OPC_B) {
+            if (!(opcode >= pa->sym && opcode <= pa->sym + 3))
+                continue;
+        compute_size:
+            s = (opcode - pa->sym) & 3;
+        } else if (pa->instr_type & OPC_WL) {
+            if (!(opcode >= pa->sym && opcode <= pa->sym + 2))
+                continue;
+            s = opcode - pa->sym + 1;
+        } else {
+            if (pa->sym != opcode)
+                continue;
+        }
+        if (pa->nb_ops != nb_ops)
+            continue;
+        /* now decode and check each operand */
+        for(i = 0; i < nb_ops; i++) {
+            int op1, op2;
+            op1 = pa->op_type[i];
+            op2 = op1 & 0x1f;
+            switch(op2) {
+            case OPT_IM:
+                v = OP_IM8 | OP_IM16 | OP_IM32;
+                break;
+            case OPT_REG:
+                v = OP_REG8 | OP_REG16 | OP_REG32;
+                break;
+            case OPT_REGW:
+                v = OP_REG16 | OP_REG32;
+                break;
+            case OPT_IMW:
+                v = OP_IM16 | OP_IM32;
+                break;
+            default:
+                v = 1 << op2;
+                break;
+            }
+            if (op1 & OPT_EA)
+                v |= OP_EA;
+            op_type[i] = v;
+            if ((ops[i].type & v) == 0)
+                goto next;
+        }
+        /* all is matching ! */
+        break;
+    next: ;
+    }
+    if (pa->sym == 0) {
+        if (opcode >= TOK_ASM_pusha && opcode <= TOK_ASM_emms) {
+            int b;
+            b = op0_codes[opcode - TOK_ASM_pusha];
+            if (b & 0xff00) 
+                g(b >> 8);
+            g(b);
+            return;
+        } else {
+            error("unknown opcode '%s'", 
+                  get_tok_str(opcode, NULL));
+        }
+    }
+    /* if the size is unknown, then evaluate it (OPC_B or OPC_WL case) */
+    if (s == 3) {
+        for(i = 0; s == 3 && i < nb_ops; i++) {
+            if ((ops[i].type & OP_REG) && !(op_type[i] & (OP_CL | OP_DX)))
+                s = reg_to_size[ops[i].type & OP_REG];
+        }
+        if (s == 3) {
+            if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) && 
+                (ops[0].type & (OP_SEG | OP_IM8S | OP_IM32)))
+                s = 2;
+            else
+                error("cannot infer opcode suffix");
+        }
+    }
+
+    /* generate data16 prefix if needed */
+    ss = s;
+    if (s == 1 || (pa->instr_type & OPC_D16))
+        g(WORD_PREFIX_OPCODE);
+    else if (s == 2)
+        s = 1;
+    /* now generates the operation */
+    if (pa->instr_type & OPC_FWAIT)
+        g(0x9b);
+
+    v = pa->opcode;
+    if (v == 0x69 || v == 0x69) {
+        /* kludge for imul $im, %reg */
+        nb_ops = 3;
+        ops[2] = ops[1];
+    } else if (v == 0xcd && ops[0].e.v == 3 && !ops[0].e.sym) {
+        v--; /* int $3 case */
+        nb_ops = 0;
+    } else if ((v == 0x06 || v == 0x07)) {
+        if (ops[0].reg >= 4) {
+            /* push/pop %fs or %gs */
+            v = 0x0fa0 + (v - 0x06) + ((ops[0].reg - 4) << 3);
+        } else {
+            v += ops[0].reg << 3;
+        }
+        nb_ops = 0;
+    } else if (v <= 0x05) {
+        /* arith case */
+        v += ((opcode - TOK_ASM_addb) >> 2) << 3;
+    } else if ((pa->instr_type & (OPC_FARITH | OPC_MODRM)) == OPC_FARITH) {
+        /* fpu arith case */
+        v += ((opcode - pa->sym) / 6) << 3;
+    }
+    if (pa->instr_type & OPC_REG) {
+        for(i = 0; i < nb_ops; i++) {
+            if (op_type[i] & (OP_REG | OP_ST)) {
+                v += ops[i].reg;
+                break;
+            }
+        }
+        /* mov $im, %reg case */
+        if (pa->opcode == 0xb0 && s >= 1)
+            v += 7;
+    }
+    if (pa->instr_type & OPC_B)
+        v += s;
+    if (pa->instr_type & OPC_TEST)
+        v += test_bits[opcode - pa->sym]; 
+    if (pa->instr_type & OPC_SHORTJMP) {
+        Sym *sym;
+        int jmp_disp;
+
+        /* see if we can really generate the jump with a byte offset */
+        sym = ops[0].e.sym;
+        if (!sym)
+            goto no_short_jump;
+        if (sym->r != cur_text_section->sh_num)
+            goto no_short_jump;
+        jmp_disp = ops[0].e.v + (long)sym->next - ind - 2;
+        if (jmp_disp == (int8_t)jmp_disp) {
+            /* OK to generate jump */
+            is_short_jmp = 1;
+            ops[0].e.v = jmp_disp;
+        } else {
+        no_short_jump:
+            if (pa->instr_type & OPC_JMP) {
+                /* long jump will be allowed. need to modify the
+                   opcode slightly */
+                if (v == 0xeb)
+                    v = 0xe9;
+                else 
+                    v += 0x0f10;
+            } else {
+                error("invalid displacement");
+            }
+        }
+    }
+    op1 = v >> 8;
+    if (op1)
+        g(op1);
+    g(v);
+        
+    /* search which operand will used for modrm */
+    modrm_index = 0;
+    if (pa->instr_type & OPC_SHIFT) {
+        reg = (opcode - pa->sym) >> 2; 
+        if (reg == 6)
+            reg = 7;
+    } else if (pa->instr_type & OPC_ARITH) {
+        reg = (opcode - pa->sym) >> 2;
+    } else if (pa->instr_type & OPC_FARITH) {
+        reg = (opcode - pa->sym) / 6;
+    } else {
+        reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7;
+    }
+    if (pa->instr_type & OPC_MODRM) {
+        /* first look for an ea operand */
+        for(i = 0;i < nb_ops; i++) {
+            if (op_type[i] & OP_EA)
+                goto modrm_found;
+        }
+        /* then if not found, a register or indirection (shift instructions) */
+        for(i = 0;i < nb_ops; i++) {
+            if (op_type[i] & (OP_REG | OP_MMX | OP_SSE | OP_INDIR))
+                goto modrm_found;
+        }
+#ifdef ASM_DEBUG
+        error("bad op table");
+#endif      
+    modrm_found:
+        modrm_index = i;
+        /* if a register is used in another operand then it is
+           used instead of group */
+        for(i = 0;i < nb_ops; i++) {
+            v = op_type[i];
+            if (i != modrm_index && 
+                (v & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_TR | OP_DB | OP_SEG))) {
+                reg = ops[i].reg;
+                break;
+            }
+        }
+
+        asm_modrm(reg, &ops[modrm_index]);
+    }
+
+    /* emit constants */
+    if (pa->opcode == 0x9a || pa->opcode == 0xea) {
+        /* ljmp or lcall kludge */
+        gen_expr32(&ops[1].e);
+        if (ops[0].e.sym)
+            error("cannot relocate");
+        gen_le16(ops[0].e.v);
+    } else {
+        for(i = 0;i < nb_ops; i++) {
+            v = op_type[i];
+            if (v & (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM8S | OP_ADDR)) {
+                /* if multiple sizes are given it means we must look
+                   at the op size */
+                if (v == (OP_IM8 | OP_IM16 | OP_IM32) ||
+                    v == (OP_IM16 | OP_IM32)) {
+                    if (ss == 0)
+                        v = OP_IM8;
+                    else if (ss == 1)
+                        v = OP_IM16;
+                    else
+                        v = OP_IM32;
+                }
+                if (v & (OP_IM8 | OP_IM8S)) {
+                    if (ops[i].e.sym)
+                        goto error_relocate;
+                    g(ops[i].e.v);
+                } else if (v & OP_IM16) {
+                    if (ops[i].e.sym) {
+                    error_relocate:
+                        error("cannot relocate");
+                    }
+                    gen_le16(ops[i].e.v);
+                } else {
+                    if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) {
+                        if (is_short_jmp)
+                            g(ops[i].e.v);
+                        else
+                            gen_disp32(&ops[i].e);
+                    } else {
+                        gen_expr32(&ops[i].e);
+                    }
+                }
+            }
+        }
+    }
+}
+
+#define NB_SAVED_REGS 3
+#define NB_ASM_REGS 8
+
+/* return the constraint priority (we allocate first the lowest
+   numbered constraints) */
+static inline int constraint_priority(const char *str)
+{
+    int priority, c, pr;
+
+    /* we take the lowest priority */
+    priority = 0;
+    for(;;) {
+        c = *str;
+        if (c == '\0')
+            break;
+        str++;
+        switch(c) {
+        case 'A':
+            pr = 0;
+            break;
+        case 'a':
+        case 'b':
+        case 'c':
+        case 'd':
+        case 'S':
+        case 'D':
+            pr = 1;
+            break;
+        case 'q':
+            pr = 2;
+            break;
+        case 'r':
+            pr = 3;
+            break;
+        case 'N':
+        case 'M':
+        case 'I':
+        case 'i':
+        case 'm':
+        case 'g':
+            pr = 4;
+            break;
+        default:
+            error("unknown constraint '%c'", c);
+            pr = 0;
+        }
+        if (pr > priority)
+            priority = pr;
+    }
+    return priority;
+}
+
+static const char *skip_constraint_modifiers(const char *p)
+{
+    while (*p == '=' || *p == '&' || *p == '+' || *p == '%')
+        p++;
+    return p;
+}
+
+#define REG_OUT_MASK 0x01
+#define REG_IN_MASK  0x02
+
+#define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask)
+
+static void asm_compute_constraints(ASMOperand *operands, 
+                                    int nb_operands, int nb_outputs, 
+                                    const uint8_t *clobber_regs,
+                                    int *pout_reg)
+{
+    ASMOperand *op;
+    int sorted_op[MAX_ASM_OPERANDS];
+    int i, j, k, p1, p2, tmp, reg, c, reg_mask;
+    const char *str;
+    uint8_t regs_allocated[NB_ASM_REGS];
+    
+    /* init fields */
+    for(i=0;i<nb_operands;i++) {
+        op = &operands[i];
+        op->input_index = -1;
+        op->ref_index = -1;
+        op->reg = -1;
+        op->is_memory = 0;
+        op->is_rw = 0;
+    }
+    /* compute constraint priority and evaluate references to output
+       constraints if input constraints */
+    for(i=0;i<nb_operands;i++) {
+        op = &operands[i];
+        str = op->constraint;
+        str = skip_constraint_modifiers(str);
+        if (isnum(*str) || *str == '[') {
+            /* this is a reference to another constraint */
+            k = find_constraint(operands, nb_operands, str, NULL);
+            if ((unsigned)k >= i || i < nb_outputs)
+                error("invalid reference in constraint %d ('%s')",
+                      i, str);
+            op->ref_index = k;
+            if (operands[k].input_index >= 0)
+                error("cannot reference twice the same operand");
+            operands[k].input_index = i;
+            op->priority = 5;
+        } else {
+            op->priority = constraint_priority(str);
+        }
+    }
+    
+    /* sort operands according to their priority */
+    for(i=0;i<nb_operands;i++)
+        sorted_op[i] = i;
+    for(i=0;i<nb_operands - 1;i++) {
+        for(j=i+1;j<nb_operands;j++) {
+            p1 = operands[sorted_op[i]].priority; 
+            p2 = operands[sorted_op[j]].priority;
+            if (p2 < p1) {
+                tmp = sorted_op[i];
+                sorted_op[i] = sorted_op[j];
+                sorted_op[j] = tmp;
+            }
+        }
+    }
+
+    for(i = 0;i < NB_ASM_REGS; i++) {
+        if (clobber_regs[i])
+            regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK;
+        else
+            regs_allocated[i] = 0;
+    }
+    /* esp cannot be used */
+    regs_allocated[4] = REG_IN_MASK | REG_OUT_MASK; 
+    /* ebp cannot be used yet */
+    regs_allocated[5] = REG_IN_MASK | REG_OUT_MASK; 
+
+    /* allocate registers and generate corresponding asm moves */
+    for(i=0;i<nb_operands;i++) {
+        j = sorted_op[i];
+        op = &operands[j];
+        str = op->constraint;
+        /* no need to allocate references */
+        if (op->ref_index >= 0)
+            continue;
+        /* select if register is used for output, input or both */
+        if (op->input_index >= 0) {
+            reg_mask = REG_IN_MASK | REG_OUT_MASK;
+        } else if (j < nb_outputs) {
+            reg_mask = REG_OUT_MASK;
+        } else {
+            reg_mask = REG_IN_MASK;
+        }
+    try_next:
+        c = *str++;
+        switch(c) {
+        case '=':
+            goto try_next;
+        case '+':
+            op->is_rw = 1;
+            /* FALL THRU */
+        case '&':
+            if (j >= nb_outputs)
+                error("'%c' modifier can only be applied to outputs", c);
+            reg_mask = REG_IN_MASK | REG_OUT_MASK;
+            goto try_next;
+        case 'A':
+            /* allocate both eax and edx */
+            if (is_reg_allocated(TREG_EAX) || 
+                is_reg_allocated(TREG_EDX))
+                goto try_next;
+            op->is_llong = 1;
+            op->reg = TREG_EAX;
+            regs_allocated[TREG_EAX] |= reg_mask;
+            regs_allocated[TREG_EDX] |= reg_mask;
+            break;
+        case 'a':
+            reg = TREG_EAX;
+            goto alloc_reg;
+        case 'b':
+            reg = 3;
+            goto alloc_reg;
+        case 'c':
+            reg = TREG_ECX;
+            goto alloc_reg;
+        case 'd':
+            reg = TREG_EDX;
+            goto alloc_reg;
+        case 'S':
+            reg = 6;
+            goto alloc_reg;
+        case 'D':
+            reg = 7;
+        alloc_reg:
+            if (is_reg_allocated(reg))
+                goto try_next;
+            goto reg_found;
+        case 'q':
+            /* eax, ebx, ecx or edx */
+            for(reg = 0; reg < 4; reg++) {
+                if (!is_reg_allocated(reg))
+                    goto reg_found;
+            }
+            goto try_next;
+        case 'r':
+            /* any general register */
+            for(reg = 0; reg < 8; reg++) {
+                if (!is_reg_allocated(reg))
+                    goto reg_found;
+            }
+            goto try_next;
+        reg_found:
+            /* now we can reload in the register */
+            op->is_llong = 0;
+            op->reg = reg;
+            regs_allocated[reg] |= reg_mask;
+            break;
+        case 'i':
+            if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST))
+                goto try_next;
+            break;
+        case 'I':
+        case 'N':
+        case 'M':
+            if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST))
+                goto try_next;
+            break;
+        case 'm':
+        case 'g':
+            /* nothing special to do because the operand is already in
+               memory, except if the pointer itself is stored in a
+               memory variable (VT_LLOCAL case) */
+            /* XXX: fix constant case */
+            /* if it is a reference to a memory zone, it must lie
+               in a register, so we reserve the register in the
+               input registers and a load will be generated
+               later */
+            if (j < nb_outputs || c == 'm') {
+                if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
+                    /* any general register */
+                    for(reg = 0; reg < 8; reg++) {
+                        if (!(regs_allocated[reg] & REG_IN_MASK))
+                            goto reg_found1;
+                    }
+                    goto try_next;
+                reg_found1:
+                    /* now we can reload in the register */
+                    regs_allocated[reg] |= REG_IN_MASK;
+                    op->reg = reg;
+                    op->is_memory = 1;
+                }
+            }
+            break;
+        default:
+            error("asm constraint %d ('%s') could not be satisfied", 
+                  j, op->constraint);
+            break;
+        }
+        /* if a reference is present for that operand, we assign it too */
+        if (op->input_index >= 0) {
+            operands[op->input_index].reg = op->reg;
+            operands[op->input_index].is_llong = op->is_llong;
+        }
+    }
+    
+    /* compute out_reg. It is used to store outputs registers to memory
+       locations references by pointers (VT_LLOCAL case) */
+    *pout_reg = -1;
+    for(i=0;i<nb_operands;i++) {
+        op = &operands[i];
+        if (op->reg >= 0 && 
+            (op->vt->r & VT_VALMASK) == VT_LLOCAL  &&
+            !op->is_memory) {
+            for(reg = 0; reg < 8; reg++) {
+                if (!(regs_allocated[reg] & REG_OUT_MASK))
+                    goto reg_found2;
+            }
+            error("could not find free output register for reloading");
+        reg_found2:
+            *pout_reg = reg;
+            break;
+        }
+    }
+    
+    /* print sorted constraints */
+#ifdef ASM_DEBUG
+    for(i=0;i<nb_operands;i++) {
+        j = sorted_op[i];
+        op = &operands[j];
+        printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n", 
+               j,                
+               op->id ? get_tok_str(op->id, NULL) : "", 
+               op->constraint,
+               op->vt->r,
+               op->reg);
+    }
+    if (*pout_reg >= 0)
+        printf("out_reg=%d\n", *pout_reg);
+#endif
+}
+
+static void subst_asm_operand(CString *add_str, 
+                              SValue *sv, int modifier)
+{
+    int r, reg, size, val;
+    char buf[64];
+
+    r = sv->r;
+    if ((r & VT_VALMASK) == VT_CONST) {
+        if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n')
+            cstr_ccat(add_str, '$');
+        if (r & VT_SYM) {
+            cstr_cat(add_str, get_tok_str(sv->sym->v, NULL));
+            if (sv->c.i != 0) {
+                cstr_ccat(add_str, '+');
+            } else {
+                return;
+            }
+        }
+        val = sv->c.i;
+        if (modifier == 'n')
+            val = -val;
+        snprintf(buf, sizeof(buf), "%d", sv->c.i);
+        cstr_cat(add_str, buf);
+    } else if ((r & VT_VALMASK) == VT_LOCAL) {
+        snprintf(buf, sizeof(buf), "%d(%%ebp)", sv->c.i);
+        cstr_cat(add_str, buf);
+    } else if (r & VT_LVAL) {
+        reg = r & VT_VALMASK;
+        if (reg >= VT_CONST)
+            error("internal compiler error");
+        snprintf(buf, sizeof(buf), "(%%%s)", 
+                 get_tok_str(TOK_ASM_eax + reg, NULL));
+        cstr_cat(add_str, buf);
+    } else {
+        /* register case */
+        reg = r & VT_VALMASK;
+        if (reg >= VT_CONST)
+            error("internal compiler error");
+
+        /* choose register operand size */
+        if ((sv->type.t & VT_BTYPE) == VT_BYTE)
+            size = 1;
+        else if ((sv->type.t & VT_BTYPE) == VT_SHORT)
+            size = 2;
+        else
+            size = 4;
+        if (size == 1 && reg >= 4)
+            size = 4;
+
+        if (modifier == 'b') {
+            if (reg >= 4)
+                error("cannot use byte register");
+            size = 1;
+        } else if (modifier == 'h') {
+            if (reg >= 4)
+                error("cannot use byte register");
+            size = -1;
+        } else if (modifier == 'w') {
+            size = 2;
+        }
+
+        switch(size) {
+        case -1:
+            reg = TOK_ASM_ah + reg;
+            break;
+        case 1:
+            reg = TOK_ASM_al + reg;
+            break;
+        case 2:
+            reg = TOK_ASM_ax + reg;
+            break;
+        default:
+            reg = TOK_ASM_eax + reg;
+            break;
+        }
+        snprintf(buf, sizeof(buf), "%%%s", get_tok_str(reg, NULL));
+        cstr_cat(add_str, buf);
+    }
+}
+
+/* generate prolog and epilog code for asm statment */
+static void asm_gen_code(ASMOperand *operands, int nb_operands, 
+                         int nb_outputs, int is_output,
+                         uint8_t *clobber_regs,
+                         int out_reg)
+{
+    uint8_t regs_allocated[NB_ASM_REGS];
+    ASMOperand *op;
+    int i, reg;
+    static uint8_t reg_saved[NB_SAVED_REGS] = { 3, 6, 7 };
+
+    /* mark all used registers */
+    memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated));
+    for(i = 0; i < nb_operands;i++) {
+        op = &operands[i];
+        if (op->reg >= 0)
+            regs_allocated[op->reg] = 1;
+    }
+    if (!is_output) {
+        /* generate reg save code */
+        for(i = 0; i < NB_SAVED_REGS; i++) {
+            reg = reg_saved[i];
+            if (regs_allocated[reg]) 
+                g(0x50 + reg);
+        }
+
+        /* generate load code */
+        for(i = 0; i < nb_operands; i++) {
+            op = &operands[i];
+            if (op->reg >= 0) {
+                if ((op->vt->r & VT_VALMASK) == VT_LLOCAL &&
+                    op->is_memory) {
+                    /* memory reference case (for both input and
+                       output cases) */
+                    SValue sv;
+                    sv = *op->vt;
+                    sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
+                    load(op->reg, &sv);
+                } else if (i >= nb_outputs || op->is_rw) {
+                    /* load value in register */
+                    load(op->reg, op->vt);
+                    if (op->is_llong) {
+                        SValue sv;
+                        sv = *op->vt;
+                        sv.c.ul += 4;
+                        load(TREG_EDX, &sv);
+                    }
+                }
+            }
+        }
+    } else {
+        /* generate save code */
+        for(i = 0 ; i < nb_outputs; i++) {
+            op = &operands[i];
+            if (op->reg >= 0) {
+                if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
+                    if (!op->is_memory) {
+                        SValue sv;
+                        sv = *op->vt;
+                        sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
+                        load(out_reg, &sv);
+
+                        sv.r = (sv.r & ~VT_VALMASK) | out_reg;
+                        store(op->reg, &sv);
+                    }
+                } else {
+                    store(op->reg, op->vt);
+                    if (op->is_llong) {
+                        SValue sv;
+                        sv = *op->vt;
+                        sv.c.ul += 4;
+                        store(TREG_EDX, &sv);
+                    }
+                }
+            }
+        }
+        /* generate reg restore code */
+        for(i = NB_SAVED_REGS - 1; i >= 0; i--) {
+            reg = reg_saved[i];
+            if (regs_allocated[reg]) 
+                g(0x58 + reg);
+        }
+    }
+}
+
+static void asm_clobber(uint8_t *clobber_regs, const char *str)
+{
+    int reg;
+    TokenSym *ts;
+
+    if (!strcmp(str, "memory") || 
+        !strcmp(str, "cc"))
+        return;
+    ts = tok_alloc(str, strlen(str));
+    reg = ts->tok;
+    if (reg >= TOK_ASM_eax && reg <= TOK_ASM_edi) {
+        reg -= TOK_ASM_eax;
+    } else if (reg >= TOK_ASM_ax && reg <= TOK_ASM_di) {
+        reg -= TOK_ASM_ax;
+    } else {
+        error("invalid clobber register '%s'", str);
+    }
+    clobber_regs[reg] = 1;
+}
+//---------------------------------------------------------------------------
+#endif
+// njn: inlined tccasm.c
+//#include "tccasm.c"
+//---------------------------------------------------------------------------
+/*
+ *  GAS like assembler for TCC
+ * 
+ *  Copyright (c) 2001-2004 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+static int asm_get_local_label_name(TCCState *s1, unsigned int n)
+{
+    char buf[64];
+    TokenSym *ts;
+
+    snprintf(buf, sizeof(buf), "L..%u", n);
+    ts = tok_alloc(buf, strlen(buf));
+    return ts->tok;
+}
+
+static void asm_expr(TCCState *s1, ExprValue *pe);
+
+/* We do not use the C expression parser to handle symbols. Maybe the
+   C expression parser could be tweaked to do so. */
+
+static void asm_expr_unary(TCCState *s1, ExprValue *pe)
+{
+    Sym *sym;
+    int op, n, label;
+    const char *p;
+
+    switch(tok) {
+    case TOK_PPNUM:
+        p = tokc.cstr->data;
+        n = strtoul(p, (char **)&p, 0);
+        if (*p == 'b' || *p == 'f') {
+            /* backward or forward label */
+            label = asm_get_local_label_name(s1, n);
+            sym = label_find(label);
+            if (*p == 'b') {
+                /* backward : find the last corresponding defined label */
+                if (sym && sym->r == 0)
+                    sym = sym->prev_tok;
+                if (!sym)
+                    error("local label '%d' not found backward", n);
+            } else {
+                /* forward */
+                if (!sym || sym->r) {
+                    /* if the last label is defined, then define a new one */
+                    sym = label_push(&s1->asm_labels, label, 0);
+                    sym->type.t = VT_STATIC | VT_VOID;
+                }
+            }
+            pe->v = 0;
+            pe->sym = sym;
+        } else if (*p == '\0') {
+            pe->v = n;
+            pe->sym = NULL;
+        } else {
+            error("invalid number syntax");
+        }
+        next();
+        break;
+    case '+':
+        next();
+        asm_expr_unary(s1, pe);
+        break;
+    case '-':
+    case '~':
+        op = tok;
+        next();
+        asm_expr_unary(s1, pe);
+        if (pe->sym)
+            error("invalid operation with label");
+        if (op == '-')
+            pe->v = -pe->v;
+        else
+            pe->v = ~pe->v;
+        break;
+    case TOK_CCHAR:
+    case TOK_LCHAR:
+       pe->v = tokc.i;
+       pe->sym = NULL;
+       next();
+       break;
+    case '(':
+        next();
+        asm_expr(s1, pe);
+        skip(')');
+        break;
+    default:
+        if (tok >= TOK_IDENT) {
+            /* label case : if the label was not found, add one */
+            sym = label_find(tok);
+            if (!sym) {
+                sym = label_push(&s1->asm_labels, tok, 0);
+                /* NOTE: by default, the symbol is global */
+                sym->type.t = VT_VOID;
+            }
+            if (sym->r == SHN_ABS) {
+                /* if absolute symbol, no need to put a symbol value */
+                pe->v = (long)sym->next;
+                pe->sym = NULL;
+            } else {
+                pe->v = 0;
+                pe->sym = sym;
+            }
+            next();
+        } else {
+            error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
+        }
+        break;
+    }
+}
+    
+static void asm_expr_prod(TCCState *s1, ExprValue *pe)
+{
+    int op;
+    ExprValue e2;
+
+    asm_expr_unary(s1, pe);
+    for(;;) {
+        op = tok;
+        if (op != '*' && op != '/' && op != '%' && 
+            op != TOK_SHL && op != TOK_SAR)
+            break;
+        next();
+        asm_expr_unary(s1, &e2);
+        if (pe->sym || e2.sym)
+            error("invalid operation with label");
+        switch(op) {
+        case '*':
+            pe->v *= e2.v;
+            break;
+        case '/':  
+            if (e2.v == 0) {
+            div_error:
+                error("division by zero");
+            }
+            pe->v /= e2.v;
+            break;
+        case '%':  
+            if (e2.v == 0)
+                goto div_error;
+            pe->v %= e2.v;
+            break;
+        case TOK_SHL:
+            pe->v <<= e2.v;
+            break;
+        default:
+        case TOK_SAR:
+            pe->v >>= e2.v;
+            break;
+        }
+    }
+}
+
+static void asm_expr_logic(TCCState *s1, ExprValue *pe)
+{
+    int op;
+    ExprValue e2;
+
+    asm_expr_prod(s1, pe);
+    for(;;) {
+        op = tok;
+        if (op != '&' && op != '|' && op != '^')
+            break;
+        next();
+        asm_expr_prod(s1, &e2);
+        if (pe->sym || e2.sym)
+            error("invalid operation with label");
+        switch(op) {
+        case '&':
+            pe->v &= e2.v;
+            break;
+        case '|':  
+            pe->v |= e2.v;
+            break;
+        default:
+        case '^':
+            pe->v ^= e2.v;
+            break;
+        }
+    }
+}
+
+static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
+{
+    int op;
+    ExprValue e2;
+
+    asm_expr_logic(s1, pe);
+    for(;;) {
+        op = tok;
+        if (op != '+' && op != '-')
+            break;
+        next();
+        asm_expr_logic(s1, &e2);
+        if (op == '+') {
+            if (pe->sym != NULL && e2.sym != NULL)
+                goto cannot_relocate;
+            pe->v += e2.v;
+            if (pe->sym == NULL && e2.sym != NULL)
+                pe->sym = e2.sym;
+        } else {
+            pe->v -= e2.v;
+            /* NOTE: we are less powerful than gas in that case
+               because we store only one symbol in the expression */
+            if (!pe->sym && !e2.sym) {
+                /* OK */
+            } else if (pe->sym && !e2.sym) {
+                /* OK */
+            } else if (pe->sym && e2.sym) {
+                if (pe->sym == e2.sym) { 
+                    /* OK */
+                } else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) {
+                    /* we also accept defined symbols in the same section */
+                    pe->v += (long)pe->sym->next - (long)e2.sym->next;
+                } else {
+                    goto cannot_relocate;
+                }
+                pe->sym = NULL; /* same symbols can be substracted to NULL */
+            } else {
+            cannot_relocate:
+                error("invalid operation with label");
+            }
+        }
+    }
+}
+
+static void asm_expr(TCCState *s1, ExprValue *pe)
+{
+    asm_expr_sum(s1, pe);
+}
+
+static int asm_int_expr(TCCState *s1)
+{
+    ExprValue e;
+    asm_expr(s1, &e);
+    if (e.sym)
+        expect("constant");
+    return e.v;
+}
+
+/* NOTE: the same name space as C labels is used to avoid using too
+   much memory when storing labels in TokenStrings */
+static void asm_new_label1(TCCState *s1, int label, int is_local,
+                           int sh_num, int value)
+{
+    Sym *sym;
+
+    sym = label_find(label);
+    if (sym) {
+        if (sym->r) {
+            /* the label is already defined */
+            if (!is_local) {
+                error("assembler label '%s' already defined", 
+                      get_tok_str(label, NULL));
+            } else {
+                /* redefinition of local labels is possible */
+                goto new_label;
+            }
+        }
+    } else {
+    new_label:
+        sym = label_push(&s1->asm_labels, label, 0);
+        sym->type.t = VT_STATIC | VT_VOID;
+    }
+    sym->r = sh_num;
+    sym->next = (void *)value;
+}
+
+static void asm_new_label(TCCState *s1, int label, int is_local)
+{
+    asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
+}
+
+static void asm_free_labels(TCCState *st)
+{
+    Sym *s, *s1;
+    Section *sec;
+    
+    for(s = st->asm_labels; s != NULL; s = s1) {
+        s1 = s->prev;
+        /* define symbol value in object file */
+        if (s->r) {
+            if (s->r == SHN_ABS)
+                sec = SECTION_ABS;
+            else
+                sec = st->sections[s->r];
+            put_extern_sym2(s, sec, (long)s->next, 0, 0);
+        }
+        /* remove label */
+        table_ident[s->v - TOK_IDENT]->sym_label = NULL;
+        sym_free(s);
+    }
+    st->asm_labels = NULL;
+}
+
+static void use_section1(TCCState *s1, Section *sec)
+{
+    cur_text_section->data_offset = ind;
+    cur_text_section = sec;
+    ind = cur_text_section->data_offset;
+}
+
+static void use_section(TCCState *s1, const char *name)
+{
+    Section *sec;
+    sec = find_section(s1, name);
+    use_section1(s1, sec);
+}
+
+static void asm_parse_directive(TCCState *s1)
+{
+    int n, offset, v, size, tok1;
+    Section *sec;
+    uint8_t *ptr;
+
+    /* assembler directive */
+    next();
+    sec = cur_text_section;
+    switch(tok) {
+    case TOK_ASM_align:
+    case TOK_ASM_skip:
+    case TOK_ASM_space:
+        tok1 = tok;
+        next();
+        n = asm_int_expr(s1);
+        if (tok1 == TOK_ASM_align) {
+            if (n < 0 || (n & (n-1)) != 0)
+                error("alignment must be a positive power of two");
+            offset = (ind + n - 1) & -n;
+            size = offset - ind;
+            /* the section must have a compatible alignment */
+            if (sec->sh_addralign < n)
+                sec->sh_addralign = n;
+        } else {
+            size = n;
+        }
+        v = 0;
+        if (tok == ',') {
+            next();
+            v = asm_int_expr(s1);
+        }
+    zero_pad:
+        if (sec->sh_type != SHT_NOBITS) {
+            sec->data_offset = ind;
+            ptr = section_ptr_add(sec, size);
+            memset(ptr, v, size);
+        }
+        ind += size;
+        break;
+    case TOK_ASM_quad:
+        next();
+        for(;;) {
+            uint64_t vl;
+            const char *p;
+
+            p = tokc.cstr->data;
+            if (tok != TOK_PPNUM) {
+            error_constant:
+                error("64 bit constant");
+            }
+            vl = strtoll(p, (char **)&p, 0);
+            if (*p != '\0')
+                goto error_constant;
+            next();
+            if (sec->sh_type != SHT_NOBITS) {
+                /* XXX: endianness */
+                gen_le32(vl);
+                gen_le32(vl >> 32);
+            } else {
+                ind += 8;
+            }
+            if (tok != ',')
+                break;
+            next();
+        }
+        break;
+    case TOK_ASM_byte:
+        size = 1;
+        goto asm_data;
+    case TOK_ASM_word:
+    case TOK_SHORT:
+        size = 2;
+        goto asm_data;
+    case TOK_LONG:
+    case TOK_INT:
+        size = 4;
+    asm_data:
+        next();
+        for(;;) {
+            ExprValue e;
+            asm_expr(s1, &e);
+            if (sec->sh_type != SHT_NOBITS) {
+                if (size == 4) {
+                    gen_expr32(&e);
+                } else {
+                    if (e.sym)
+                        expect("constant");
+                    if (size == 1)
+                        g(e.v);
+                    else
+                        gen_le16(e.v);
+                }
+            } else {
+                ind += size;
+            }
+            if (tok != ',')
+                break;
+            next();
+        }
+        break;
+    case TOK_ASM_fill:
+        {
+            int repeat, size, val, i, j;
+            uint8_t repeat_buf[8];
+            next();
+            repeat = asm_int_expr(s1);
+            if (repeat < 0) {
+                error("repeat < 0; .fill ignored");
+                break;
+            }
+            size = 1;
+            val = 0;
+            if (tok == ',') {
+                next();
+                size = asm_int_expr(s1);
+                if (size < 0) {
+                    error("size < 0; .fill ignored");
+                    break;
+                }
+                if (size > 8)
+                    size = 8;
+                if (tok == ',') {
+                    next();
+                    val = asm_int_expr(s1);
+                }
+            }
+            /* XXX: endianness */
+            repeat_buf[0] = val;
+            repeat_buf[1] = val >> 8;
+            repeat_buf[2] = val >> 16;
+            repeat_buf[3] = val >> 24;
+            repeat_buf[4] = 0;
+            repeat_buf[5] = 0;
+            repeat_buf[6] = 0;
+            repeat_buf[7] = 0;
+            for(i = 0; i < repeat; i++) {
+                for(j = 0; j < size; j++) {
+                    g(repeat_buf[j]);
+                }
+            }
+        }
+        break;
+    case TOK_ASM_org:
+        {
+            unsigned long n;
+            next();
+            /* XXX: handle section symbols too */
+            n = asm_int_expr(s1);
+            if (n < ind)
+                error("attempt to .org backwards");
+            v = 0;
+            size = n - ind;
+            goto zero_pad;
+        }
+        break;
+    case TOK_ASM_globl:
+    case TOK_ASM_global:
+       { 
+            Sym *sym;
+
+            next();
+            sym = label_find(tok);
+            if (!sym) {
+                sym = label_push(&s1->asm_labels, tok, 0);
+                sym->type.t = VT_VOID;
+            }
+            sym->type.t &= ~VT_STATIC;
+            next();
+       }
+       break;
+    case TOK_ASM_string:
+    case TOK_ASM_ascii:
+    case TOK_ASM_asciz:
+        {
+            const uint8_t *p;
+            int i, size, t;
+
+            t = tok;
+            next();
+            for(;;) {
+                if (tok != TOK_STR)
+                    expect("string constant");
+                p = tokc.cstr->data;
+                size = tokc.cstr->size;
+                if (t == TOK_ASM_ascii && size > 0)
+                    size--;
+                for(i = 0; i < size; i++)
+                    g(p[i]);
+                next();
+                if (tok == ',') {
+                    next();
+                } else if (tok != TOK_STR) {
+                    break;
+                }
+            }
+       }
+       break;
+    case TOK_ASM_text:
+    case TOK_ASM_data:
+    case TOK_ASM_bss:
+       { 
+            char sname[64];
+            tok1 = tok;
+            n = 0;
+            next();
+            if (tok != ';' && tok != TOK_LINEFEED) {
+               n = asm_int_expr(s1);
+               next();
+            }
+            sprintf(sname, (n?".%s%d":".%s"), get_tok_str(tok1, NULL), n);
+            use_section(s1, sname);
+       }
+       break;
+    case TOK_SECTION1:
+        {
+            char sname[256];
+
+            /* XXX: support more options */
+            next();
+            sname[0] = '\0';
+            while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
+                if (tok == TOK_STR)
+                    pstrcat(sname, sizeof(sname), tokc.cstr->data);
+                else
+                    pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
+                next();
+            }
+            if (tok == ',') {
+                /* skip section options */
+                next();
+                if (tok != TOK_STR)
+                    expect("string constant");
+                next();
+            }
+            last_text_section = cur_text_section;
+            use_section(s1, sname);
+        }
+        break;
+    case TOK_ASM_previous:
+        { 
+            Section *sec;
+            next();
+            if (!last_text_section)
+                error("no previous section referenced");
+            sec = cur_text_section;
+            use_section1(s1, last_text_section);
+            last_text_section = sec;
+        }
+        break;
+    default:
+        error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
+        break;
+    }
+}
+
+
+/* assemble a file */
+static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
+{
+    int opcode;
+
+#if 0
+    /* print stats about opcodes */
+    {
+        const ASMInstr *pa;
+        int freq[4];
+        int op_vals[500];
+        int nb_op_vals, i, j;
+
+        nb_op_vals = 0;
+        memset(freq, 0, sizeof(freq));
+        for(pa = asm_instrs; pa->sym != 0; pa++) {
+            freq[pa->nb_ops]++;
+            for(i=0;i<pa->nb_ops;i++) {
+                for(j=0;j<nb_op_vals;j++) {
+                    if (pa->op_type[i] == op_vals[j])
+                        goto found;
+                }
+                op_vals[nb_op_vals++] = pa->op_type[i];
+            found: ;
+            }
+        }
+        for(i=0;i<nb_op_vals;i++) {
+            int v = op_vals[i];
+            if ((v & (v - 1)) != 0)
+                printf("%3d: %08x\n", i, v);
+        }
+        printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
+               sizeof(asm_instrs), sizeof(asm_instrs) / sizeof(ASMInstr),
+               freq[0], freq[1], freq[2], freq[3]);
+    }
+#endif
+
+    /* XXX: undefine C labels */
+
+    ch = file->buf_ptr[0];
+    tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
+    parse_flags = PARSE_FLAG_ASM_COMMENTS;
+    if (do_preprocess)
+        parse_flags |= PARSE_FLAG_PREPROCESS;
+    next();
+    for(;;) {
+        if (tok == TOK_EOF)
+            break;
+        parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
+    redo:
+        if (tok == '#') {
+            /* horrible gas comment */
+            while (tok != TOK_LINEFEED)
+                next();
+        } else if (tok == '.') {
+            asm_parse_directive(s1);
+        } else if (tok == TOK_PPNUM) {
+            const char *p;
+            int n;
+            p = tokc.cstr->data;
+            n = strtoul(p, (char **)&p, 10);
+            if (*p != '\0')
+                expect("':'");
+            /* new local label */
+            asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
+            next();
+            skip(':');
+            goto redo;
+        } else if (tok >= TOK_IDENT) {
+            /* instruction or label */
+            opcode = tok;
+            next();
+            if (tok == ':') {
+                /* new label */
+                asm_new_label(s1, opcode, 0);
+                next();
+                goto redo;
+            } else if (tok == '=') {
+                int n;
+                next();
+                n = asm_int_expr(s1);
+                asm_new_label1(s1, opcode, 0, SHN_ABS, n);
+                goto redo;
+            } else {
+                asm_opcode(s1, opcode);
+            }
+        }
+        /* end of line */
+        if (tok != ';' && tok != TOK_LINEFEED){
+            expect("end of line");
+        }
+        parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
+        next();
+    }
+
+    asm_free_labels(s1);
+
+    return 0;
+}
+
+/* Assemble the current file */
+static int tcc_assemble(TCCState *s1, int do_preprocess)
+{
+    Sym *define_start;
+    int ret;
+
+    preprocess_init(s1);
+
+    /* default section is text */
+    cur_text_section = text_section;
+    ind = cur_text_section->data_offset;
+
+    define_start = define_stack;
+
+    ret = tcc_assemble_internal(s1, do_preprocess);
+
+    cur_text_section->data_offset = ind;
+
+    free_defines(define_start); 
+
+    return ret;
+}
+
+/********************************************************************/
+/* GCC inline asm support */
+
+/* assemble the string 'str' in the current C compilation unit without
+   C preprocessing. NOTE: str is modified by modifying the '\0' at the
+   end */
+static void tcc_assemble_inline(TCCState *s1, char *str, int len)
+{
+    BufferedFile *bf, *saved_file;
+    int saved_parse_flags, *saved_macro_ptr;
+
+    bf = tcc_malloc(sizeof(BufferedFile));
+    memset(bf, 0, sizeof(BufferedFile));
+    bf->fd = -1;
+    bf->buf_ptr = str;
+    bf->buf_end = str + len;
+    str[len] = CH_EOB;
+    /* same name as current file so that errors are correctly
+       reported */
+    pstrcpy(bf->filename, sizeof(bf->filename), file->filename);
+    bf->line_num = file->line_num;
+    saved_file = file;
+    file = bf;
+    saved_parse_flags = parse_flags;
+    saved_macro_ptr = macro_ptr;
+    macro_ptr = NULL;
+    
+    tcc_assemble_internal(s1, 0);
+
+    parse_flags = saved_parse_flags;
+    macro_ptr = saved_macro_ptr;
+    file = saved_file;
+    tcc_free(bf);
+}
+
+/* find a constraint by its number or id (gcc 3 extended
+   syntax). return -1 if not found. Return in *pp in char after the
+   constraint */
+static int find_constraint(ASMOperand *operands, int nb_operands, 
+                           const char *name, const char **pp)
+{
+    int index;
+    TokenSym *ts;
+    const char *p;
+
+    if (isnum(*name)) {
+        index = 0;
+        while (isnum(*name)) {
+            index = (index * 10) + (*name) - '0';
+            name++;
+        }
+        if ((unsigned)index >= nb_operands)
+            index = -1;
+    } else if (*name == '[') {
+        name++;
+        p = strchr(name, ']');
+        if (p) {
+            ts = tok_alloc(name, p - name);
+            for(index = 0; index < nb_operands; index++) {
+                if (operands[index].id == ts->tok)
+                    goto found;
+            }
+            index = -1;
+        found:
+            name = p + 1;
+        } else {
+            index = -1;
+        }
+    } else {
+        index = -1;
+    }
+    if (pp)
+        *pp = name;
+    return index;
+}
+
+static void subst_asm_operands(ASMOperand *operands, int nb_operands, 
+                               int nb_outputs,
+                               CString *out_str, CString *in_str)
+{
+    int c, index, modifier;
+    const char *str;
+    ASMOperand *op;
+    SValue sv;
+
+    cstr_new(out_str);
+    str = in_str->data;
+    for(;;) {
+        c = *str++;
+        if (c == '%') {
+            if (*str == '%') {
+                str++;
+                goto add_char;
+            }
+            modifier = 0;
+            if (*str == 'c' || *str == 'n' ||
+                *str == 'b' || *str == 'w' || *str == 'h')
+                modifier = *str++;
+            index = find_constraint(operands, nb_operands, str, &str);
+            if (index < 0)
+                error("invalid operand reference after %%");
+            op = &operands[index];
+            sv = *op->vt;
+            if (op->reg >= 0) {
+                sv.r = op->reg;
+                if ((op->vt->r & VT_VALMASK) == VT_LLOCAL)
+                    sv.r |= VT_LVAL;
+            }
+            subst_asm_operand(out_str, &sv, modifier);
+        } else {
+        add_char:
+            cstr_ccat(out_str, c);
+            if (c == '\0')
+                break;
+        }
+    }
+}
+
+
+static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
+                               int is_output)
+{
+    ASMOperand *op;
+    int nb_operands;
+
+    if (tok != ':') {
+        nb_operands = *nb_operands_ptr;
+        for(;;) {
+            if (nb_operands >= MAX_ASM_OPERANDS)
+                error("too many asm operands");
+            op = &operands[nb_operands++];
+            op->id = 0;
+            if (tok == '[') {
+                next();
+                if (tok < TOK_IDENT)
+                    expect("identifier");
+                op->id = tok;
+                next();
+                skip(']');
+            }
+            if (tok != TOK_STR)
+                expect("string constant");
+            op->constraint = tcc_malloc(tokc.cstr->size);
+            strcpy(op->constraint, tokc.cstr->data);
+            next();
+            skip('(');
+            gexpr();
+            if (is_output) {
+                test_lvalue();
+            } else {
+                /* we want to avoid LLOCAL case, except when the 'm'
+                   constraint is used. Note that it may come from
+                   register storage, so we need to convert (reg)
+                   case */
+                if ((vtop->r & VT_LVAL) &&
+                    ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
+                     (vtop->r & VT_VALMASK) < VT_CONST) &&
+                    !strchr(op->constraint, 'm')) {
+                    gv(RC_INT);
+                }
+            }
+            op->vt = vtop;
+            skip(')');
+            if (tok == ',') {
+                next();
+            } else {
+                break;
+            }
+        }
+        *nb_operands_ptr = nb_operands;
+    }
+}
+
+static void parse_asm_str(CString *astr)
+{
+    skip('(');
+    /* read the string */
+    if (tok != TOK_STR)
+        expect("string constant");
+    cstr_new(astr);
+    while (tok == TOK_STR) {
+        /* XXX: add \0 handling too ? */
+        cstr_cat(astr, tokc.cstr->data);
+        next();
+    }
+    cstr_ccat(astr, '\0');
+}
+
+/* parse the GCC asm() instruction */
+static void asm_instr(void)
+{
+    CString astr, astr1;
+    ASMOperand operands[MAX_ASM_OPERANDS];
+    int nb_inputs, nb_outputs, nb_operands, i, must_subst, out_reg;
+    uint8_t clobber_regs[NB_ASM_REGS];
+
+    next();
+    /* since we always generate the asm() instruction, we can ignore
+       volatile */
+    if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
+        next();
+    }
+    parse_asm_str(&astr);
+    nb_operands = 0;
+    nb_outputs = 0;
+    must_subst = 0;
+    memset(clobber_regs, 0, sizeof(clobber_regs));
+    if (tok == ':') {
+        next();
+        must_subst = 1;
+        /* output args */
+        parse_asm_operands(operands, &nb_operands, 1);
+        nb_outputs = nb_operands;
+        if (tok == ':') {
+            next();
+            /* input args */
+            parse_asm_operands(operands, &nb_operands, 0);
+            if (tok == ':') {
+                /* clobber list */
+                /* XXX: handle registers */
+                next();
+                for(;;) {
+                    if (tok != TOK_STR)
+                        expect("string constant");
+                    asm_clobber(clobber_regs, tokc.cstr->data);
+                    next();
+                    if (tok == ',') {
+                        next();
+                    } else {
+                        break;
+                    }
+                }
+            }
+        }
+    }
+    skip(')');
+    /* NOTE: we do not eat the ';' so that we can restore the current
+       token after the assembler parsing */
+    if (tok != ';')
+        expect("';'");
+    nb_inputs = nb_operands - nb_outputs;
+    
+    /* save all values in the memory */
+    save_regs(0);
+
+    /* compute constraints */
+    asm_compute_constraints(operands, nb_operands, nb_outputs, 
+                            clobber_regs, &out_reg);
+
+    /* substitute the operands in the asm string. No substitution is
+       done if no operands (GCC behaviour) */
+#ifdef ASM_DEBUG
+    printf("asm: \"%s\"\n", (char *)astr.data);
+#endif
+    if (must_subst) {
+        subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
+        cstr_free(&astr);
+    } else {
+        astr1 = astr;
+    }
+#ifdef ASM_DEBUG
+    printf("subst_asm: \"%s\"\n", (char *)astr1.data);
+#endif
+
+    /* generate loads */
+    asm_gen_code(operands, nb_operands, nb_outputs, 0, 
+                 clobber_regs, out_reg);    
+
+    /* assemble the string with tcc internal assembler */
+    tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
+
+    /* restore the current C token */
+    next();
+
+    /* store the output values if needed */
+    asm_gen_code(operands, nb_operands, nb_outputs, 1, 
+                 clobber_regs, out_reg);
+    
+    /* free everything */
+    for(i=0;i<nb_operands;i++) {
+        ASMOperand *op;
+        op = &operands[i];
+        tcc_free(op->constraint);
+        vpop();
+    }
+    cstr_free(&astr1);
+}
+
+static void asm_global_instr(void)
+{
+    CString astr;
+
+    next();
+    parse_asm_str(&astr);
+    skip(')');
+    /* NOTE: we do not eat the ';' so that we can restore the current
+       token after the assembler parsing */
+    if (tok != ';')
+        expect("';'");
+    
+#ifdef ASM_DEBUG
+    printf("asm_global: \"%s\"\n", (char *)astr.data);
+#endif
+    cur_text_section = text_section;
+    ind = cur_text_section->data_offset;
+
+    /* assemble the string with tcc internal assembler */
+    tcc_assemble_inline(tcc_state, astr.data, astr.size - 1);
+    
+    cur_text_section->data_offset = ind;
+
+    /* restore the current C token */
+    next();
+
+    cstr_free(&astr);
+}
+//---------------------------------------------------------------------------
+
+#else
+static void asm_instr(void)
+{
+    error("inline asm() not supported");
+}
+static void asm_global_instr(void)
+{
+    error("inline asm() not supported");
+}
+#endif
+
+// njn: inlined tccelf.c
+//#include "tccelf.c"
+//---------------------------------------------------------------------------
+/*
+ *  ELF file handling for TCC
+ * 
+ *  Copyright (c) 2001-2004 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+static int put_elf_str(Section *s, const char *sym)
+{
+    int offset, len;
+    char *ptr;
+
+    len = strlen(sym) + 1;
+    offset = s->data_offset;
+    ptr = section_ptr_add(s, len);
+    memcpy(ptr, sym, len);
+    return offset;
+}
+
+/* elf symbol hashing function */
+static unsigned long elf_hash(const unsigned char *name)
+{
+    unsigned long h = 0, g;
+    
+    while (*name) {
+        h = (h << 4) + *name++;
+        g = h & 0xf0000000;
+        if (g)
+            h ^= g >> 24;
+        h &= ~g;
+    }
+    return h;
+}
+
+/* rebuild hash table of section s */
+/* NOTE: we do factorize the hash table code to go faster */
+static void rebuild_hash(Section *s, unsigned int nb_buckets)
+{
+    Elf32_Sym *sym;
+    int *ptr, *hash, nb_syms, sym_index, h;
+    char *strtab;
+
+    strtab = s->link->data;
+    nb_syms = s->data_offset / sizeof(Elf32_Sym);
+
+    s->hash->data_offset = 0;
+    ptr = section_ptr_add(s->hash, (2 + nb_buckets + nb_syms) * sizeof(int));
+    ptr[0] = nb_buckets;
+    ptr[1] = nb_syms;
+    ptr += 2;
+    hash = ptr;
+    memset(hash, 0, (nb_buckets + 1) * sizeof(int));
+    ptr += nb_buckets + 1;
+
+    sym = (Elf32_Sym *)s->data + 1;
+    for(sym_index = 1; sym_index < nb_syms; sym_index++) {
+        if (ELF32_ST_BIND(sym->st_info) != STB_LOCAL) {
+            h = elf_hash(strtab + sym->st_name) % nb_buckets;
+            *ptr = hash[h];
+            hash[h] = sym_index;
+        } else {
+            *ptr = 0;
+        }
+        ptr++;
+        sym++;
+    }
+}
+
+/* return the symbol number */
+static int put_elf_sym(Section *s, 
+                       unsigned long value, unsigned long size,
+                       int info, int other, int shndx, const char *name)
+{
+    int name_offset, sym_index;
+    int nbuckets, h;
+    Elf32_Sym *sym;
+    Section *hs;
+    
+    sym = section_ptr_add(s, sizeof(Elf32_Sym));
+    if (name)
+        name_offset = put_elf_str(s->link, name);
+    else
+        name_offset = 0;
+    /* XXX: endianness */
+    sym->st_name = name_offset;
+    sym->st_value = value;
+    sym->st_size = size;
+    sym->st_info = info;
+    sym->st_other = other;
+    sym->st_shndx = shndx;
+    sym_index = sym - (Elf32_Sym *)s->data;
+    hs = s->hash;
+    if (hs) {
+        int *ptr, *base;
+        ptr = section_ptr_add(hs, sizeof(int));
+        base = (int *)hs->data;
+        /* only add global or weak symbols */
+        if (ELF32_ST_BIND(info) != STB_LOCAL) {
+            /* add another hashing entry */
+            nbuckets = base[0];
+            h = elf_hash(name) % nbuckets;
+            *ptr = base[2 + h];
+            base[2 + h] = sym_index;
+            base[1]++;
+            /* we resize the hash table */
+            hs->nb_hashed_syms++;
+            if (hs->nb_hashed_syms > 2 * nbuckets) {
+                rebuild_hash(s, 2 * nbuckets);
+            }
+        } else {
+            *ptr = 0;
+            base[1]++;
+        }
+    }
+    return sym_index;
+}
+
+/* find global ELF symbol 'name' and return its index. Return 0 if not
+   found. */
+static int find_elf_sym(Section *s, const char *name)
+{
+    Elf32_Sym *sym;
+    Section *hs;
+    int nbuckets, sym_index, h;
+    const char *name1;
+    
+    hs = s->hash;
+    if (!hs)
+        return 0;
+    nbuckets = ((int *)hs->data)[0];
+    h = elf_hash(name) % nbuckets;
+    sym_index = ((int *)hs->data)[2 + h];
+    while (sym_index != 0) {
+        sym = &((Elf32_Sym *)s->data)[sym_index];
+        name1 = s->link->data + sym->st_name;
+        if (!strcmp(name, name1))
+            return sym_index;
+        sym_index = ((int *)hs->data)[2 + nbuckets + sym_index];
+    }
+    return 0;
+}
+
+/* return elf symbol value or error */
+int tcc_get_symbol(TCCState *s, unsigned long *pval, const char *name)
+{
+    int sym_index;
+    Elf32_Sym *sym;
+    
+    sym_index = find_elf_sym(symtab_section, name);
+    if (!sym_index)
+        return -1;
+    sym = &((Elf32_Sym *)symtab_section->data)[sym_index];
+    *pval = sym->st_value;
+    return 0;
+}
+
+void *tcc_get_symbol_err(TCCState *s, const char *name)
+{
+    unsigned long val;
+    if (tcc_get_symbol(s, &val, name) < 0)
+        error("%s not defined", name);
+    return (void *)val;
+}
+
+/* add an elf symbol : check if it is already defined and patch
+   it. Return symbol index. NOTE that sh_num can be SHN_UNDEF. */
+static int add_elf_sym(Section *s, unsigned long value, unsigned long size,
+                       int info, int other, int sh_num, const char *name)
+{
+    Elf32_Sym *esym;
+    int sym_bind, sym_index, sym_type, esym_bind;
+
+    sym_bind = ELF32_ST_BIND(info);
+    sym_type = ELF32_ST_TYPE(info);
+        
+    if (sym_bind != STB_LOCAL) {
+        /* we search global or weak symbols */
+        sym_index = find_elf_sym(s, name);
+        if (!sym_index)
+            goto do_def;
+        esym = &((Elf32_Sym *)s->data)[sym_index];
+        if (esym->st_shndx != SHN_UNDEF) {
+            esym_bind = ELF32_ST_BIND(esym->st_info);
+            if (sh_num == SHN_UNDEF) {
+                /* ignore adding of undefined symbol if the
+                   corresponding symbol is already defined */
+            } else if (sym_bind == STB_GLOBAL && esym_bind == STB_WEAK) {
+                /* global overrides weak, so patch */
+                goto do_patch;
+            } else if (sym_bind == STB_WEAK && esym_bind == STB_GLOBAL) {
+                /* weak is ignored if already global */
+            } else {
+#if 0
+                printf("new_bind=%d new_shndx=%d last_bind=%d old_shndx=%d\n",
+                       sym_bind, sh_num, esym_bind, esym->st_shndx);
+#endif
+                /* NOTE: we accept that two DLL define the same symbol */
+                if (s != tcc_state->dynsymtab_section)
+                    error_noabort("'%s' defined twice", name);
+            }
+        } else {
+        do_patch:
+            esym->st_info = ELF32_ST_INFO(sym_bind, sym_type);
+            esym->st_shndx = sh_num;
+            esym->st_value = value;
+            esym->st_size = size;
+            esym->st_other = other;
+        }
+    } else {
+    do_def:
+        sym_index = put_elf_sym(s, value, size, 
+                                ELF32_ST_INFO(sym_bind, sym_type), other, 
+                                sh_num, name);
+    }
+    return sym_index;
+}
+
+/* put relocation */
+static void put_elf_reloc(Section *symtab, Section *s, unsigned long offset,
+                          int type, int symbol)
+{
+    char buf[256];
+    Section *sr;
+    Elf32_Rel *rel;
+
+    sr = s->reloc;
+    if (!sr) {
+        /* if no relocation section, create it */
+        snprintf(buf, sizeof(buf), ".rel%s", s->name);
+        /* if the symtab is allocated, then we consider the relocation
+           are also */
+        sr = new_section(tcc_state, buf, SHT_REL, symtab->sh_flags);
+        sr->sh_entsize = sizeof(Elf32_Rel);
+        sr->link = symtab;
+        sr->sh_info = s->sh_num;
+        s->reloc = sr;
+    }
+    rel = section_ptr_add(sr, sizeof(Elf32_Rel));
+    rel->r_offset = offset;
+    rel->r_info = ELF32_R_INFO(symbol, type);
+}
+
+/* put stab debug information */
+
+typedef struct {
+    unsigned long n_strx;         /* index into string table of name */
+    unsigned char n_type;         /* type of symbol */
+    unsigned char n_other;        /* misc info (usually empty) */
+    unsigned short n_desc;        /* description field */
+    unsigned long n_value;        /* value of symbol */
+} Stab_Sym;
+
+static void put_stabs(const char *str, int type, int other, int desc, 
+                      unsigned long value)
+{
+    Stab_Sym *sym;
+
+    sym = section_ptr_add(stab_section, sizeof(Stab_Sym));
+    if (str) {
+        sym->n_strx = put_elf_str(stabstr_section, str);
+    } else {
+        sym->n_strx = 0;
+    }
+    sym->n_type = type;
+    sym->n_other = other;
+    sym->n_desc = desc;
+    sym->n_value = value;
+}
+
+static void put_stabs_r(const char *str, int type, int other, int desc, 
+                        unsigned long value, Section *sec, int sym_index)
+{
+    put_stabs(str, type, other, desc, value);
+    put_elf_reloc(symtab_section, stab_section, 
+                  stab_section->data_offset - sizeof(unsigned long),
+                  R_DATA_32, sym_index);
+}
+
+static void put_stabn(int type, int other, int desc, int value)
+{
+    put_stabs(NULL, type, other, desc, value);
+}
+
+static void put_stabd(int type, int other, int desc)
+{
+    put_stabs(NULL, type, other, desc, 0);
+}
+
+/* In an ELF file symbol table, the local symbols must appear below
+   the global and weak ones. Since TCC cannot sort it while generating
+   the code, we must do it after. All the relocation tables are also
+   modified to take into account the symbol table sorting */
+static void sort_syms(TCCState *s1, Section *s)
+{
+    int *old_to_new_syms;
+    Elf32_Sym *new_syms;
+    int nb_syms, i;
+    Elf32_Sym *p, *q;
+    Elf32_Rel *rel, *rel_end;
+    Section *sr;
+    int type, sym_index;
+
+    nb_syms = s->data_offset / sizeof(Elf32_Sym);
+    new_syms = tcc_malloc(nb_syms * sizeof(Elf32_Sym));
+    old_to_new_syms = tcc_malloc(nb_syms * sizeof(int));
+
+    /* first pass for local symbols */
+    p = (Elf32_Sym *)s->data;
+    q = new_syms;
+    for(i = 0; i < nb_syms; i++) {
+        if (ELF32_ST_BIND(p->st_info) == STB_LOCAL) {
+            old_to_new_syms[i] = q - new_syms;
+            *q++ = *p;
+        }
+        p++;
+    }
+    /* save the number of local symbols in section header */
+    s->sh_info = q - new_syms;
+
+    /* then second pass for non local symbols */
+    p = (Elf32_Sym *)s->data;
+    for(i = 0; i < nb_syms; i++) {
+        if (ELF32_ST_BIND(p->st_info) != STB_LOCAL) {
+            old_to_new_syms[i] = q - new_syms;
+            *q++ = *p;
+        }
+        p++;
+    }
+    
+    /* we copy the new symbols to the old */
+    memcpy(s->data, new_syms, nb_syms * sizeof(Elf32_Sym));
+    tcc_free(new_syms);
+
+    /* now we modify all the relocations */
+    for(i = 1; i < s1->nb_sections; i++) {
+        sr = s1->sections[i];
+        if (sr->sh_type == SHT_REL && sr->link == s) {
+            rel_end = (Elf32_Rel *)(sr->data + sr->data_offset);
+            for(rel = (Elf32_Rel *)sr->data;
+                rel < rel_end;
+                rel++) {
+                sym_index = ELF32_R_SYM(rel->r_info);
+                type = ELF32_R_TYPE(rel->r_info);
+                sym_index = old_to_new_syms[sym_index];
+                rel->r_info = ELF32_R_INFO(sym_index, type);
+            }
+        }
+    }
+    
+    tcc_free(old_to_new_syms);
+}
+
+/* relocate common symbols in the .bss section */
+static void relocate_common_syms(void)
+{
+    Elf32_Sym *sym, *sym_end;
+    unsigned long offset, align;
+    
+    sym_end = (Elf32_Sym *)(symtab_section->data + symtab_section->data_offset);
+    for(sym = (Elf32_Sym *)symtab_section->data + 1; 
+        sym < sym_end;
+        sym++) {
+        if (sym->st_shndx == SHN_COMMON) {
+            /* align symbol */
+            align = sym->st_value;
+            offset = bss_section->data_offset;
+            offset = (offset + align - 1) & -align;
+            sym->st_value = offset;
+            sym->st_shndx = bss_section->sh_num;
+            offset += sym->st_size;
+            bss_section->data_offset = offset;
+        }
+    }
+}
+
+/* relocate symbol table, resolve undefined symbols if do_resolve is
+   true and output error if undefined symbol. */
+static void relocate_syms(TCCState *s1, int do_resolve)
+{
+    Elf32_Sym *sym, *esym, *sym_end;
+    int sym_bind, sh_num, sym_index;
+    const char *name;
+    unsigned long addr;
+
+    sym_end = (Elf32_Sym *)(symtab_section->data + symtab_section->data_offset);
+    for(sym = (Elf32_Sym *)symtab_section->data + 1; 
+        sym < sym_end;
+        sym++) {
+        sh_num = sym->st_shndx;
+        if (sh_num == SHN_UNDEF) {
+            name = strtab_section->data + sym->st_name;
+            if (do_resolve) {
+                name = symtab_section->link->data + sym->st_name;
+                addr = (unsigned long)resolve_sym(s1, name, ELF32_ST_TYPE(sym->st_info));
+                if (addr) {
+                    sym->st_value = addr;
+                    goto found;
+                }
+            } else if (s1->dynsym) {
+                /* if dynamic symbol exist, then use it */
+                sym_index = find_elf_sym(s1->dynsym, name);
+                if (sym_index) {
+                    esym = &((Elf32_Sym *)s1->dynsym->data)[sym_index];
+                    sym->st_value = esym->st_value;
+                    goto found;
+                }
+            }
+            /* XXX: _fp_hw seems to be part of the ABI, so we ignore
+               it */
+            if (!strcmp(name, "_fp_hw"))
+                goto found;
+            /* only weak symbols are accepted to be undefined. Their
+               value is zero */
+            sym_bind = ELF32_ST_BIND(sym->st_info);
+            if (sym_bind == STB_WEAK) {
+                sym->st_value = 0;
+            } else {
+                error_noabort("undefined symbol '%s'", name);
+            }
+        } else if (sh_num < SHN_LORESERVE) {
+            /* add section base */
+            sym->st_value += s1->sections[sym->st_shndx]->sh_addr;
+        }
+    found: ;
+    }
+}
+
+/* relocate a given section (CPU dependent) */
+static void relocate_section(TCCState *s1, Section *s)
+{
+    Section *sr;
+    Elf32_Rel *rel, *rel_end, *qrel;
+    Elf32_Sym *sym;
+    int type, sym_index;
+    unsigned char *ptr;
+    unsigned long val, addr;
+#if defined(TCC_TARGET_I386)
+    int esym_index;
+#endif
+
+    sr = s->reloc;
+    rel_end = (Elf32_Rel *)(sr->data + sr->data_offset);
+    qrel = (Elf32_Rel *)sr->data;
+    for(rel = qrel;
+        rel < rel_end;
+        rel++) {
+        ptr = s->data + rel->r_offset;
+
+        sym_index = ELF32_R_SYM(rel->r_info);
+        sym = &((Elf32_Sym *)symtab_section->data)[sym_index];
+        val = sym->st_value;
+        type = ELF32_R_TYPE(rel->r_info);
+        addr = s->sh_addr + rel->r_offset;
+
+        /* CPU specific */
+        switch(type) {
+#if defined(TCC_TARGET_I386)
+        case R_386_32:
+            if (s1->output_type == TCC_OUTPUT_DLL) {
+                esym_index = s1->symtab_to_dynsym[sym_index];
+                qrel->r_offset = rel->r_offset;
+                if (esym_index) {
+                    qrel->r_info = ELF32_R_INFO(esym_index, R_386_32);
+                    qrel++;
+                    break;
+                } else {
+                    qrel->r_info = ELF32_R_INFO(0, R_386_RELATIVE);
+                    qrel++;
+                }
+            }
+            *(int *)ptr += val;
+            break;
+        case R_386_PC32:
+            if (s1->output_type == TCC_OUTPUT_DLL) {
+                /* DLL relocation */
+                esym_index = s1->symtab_to_dynsym[sym_index];
+                if (esym_index) {
+                    qrel->r_offset = rel->r_offset;
+                    qrel->r_info = ELF32_R_INFO(esym_index, R_386_PC32);
+                    qrel++;
+                    break;
+                }
+            }
+            *(int *)ptr += val - addr;
+            break;
+        case R_386_PLT32:
+            *(int *)ptr += val - addr;
+            break;
+        case R_386_GLOB_DAT:
+        case R_386_JMP_SLOT:
+            *(int *)ptr = val;
+            break;
+        case R_386_GOTPC:
+            *(int *)ptr += s1->got->sh_addr - addr;
+            break;
+        case R_386_GOTOFF:
+            *(int *)ptr += val - s1->got->sh_addr;
+            break;
+        case R_386_GOT32:
+            /* we load the got offset */
+            *(int *)ptr += s1->got_offsets[sym_index];
+            break;
+#elif defined(TCC_TARGET_ARM)
+       case R_ARM_PC24:
+       case R_ARM_PLT32:
+           {
+                int x;
+                x = (*(int *)ptr)&0xffffff;
+                (*(int *)ptr) &= 0xff000000;
+                if (x & 0x800000)
+                    x -= 0x1000000;
+                x *= 4;
+                x += val - addr;
+                if((x & 3) != 0 || x >= 0x4000000 || x < -0x4000000)
+                    error("can't relocate value at %x",addr);
+                x >>= 2;
+                x &= 0xffffff;
+                (*(int *)ptr) |= x;
+           }
+           break;
+       case R_ARM_ABS32:
+           *(int *)ptr += val;
+           break;
+       case R_ARM_GOTPC:
+           *(int *)ptr += s1->got->sh_addr - addr;
+           break;
+        case R_ARM_GOT32:
+            /* we load the got offset */
+            *(int *)ptr += s1->got_offsets[sym_index];
+            break;
+       case R_ARM_COPY:
+            break;
+       default:
+           fprintf(stderr,"FIXME: handle reloc type %x at %lx [%.8x] to %lx\n",
+                    type,addr,(unsigned int )ptr,val);
+            break;
+#elif defined(TCC_TARGET_C67)
+       case R_C60_32:
+           *(int *)ptr += val;
+           break;
+        case R_C60LO16:
+            {
+                uint32_t orig;
+                
+                /* put the low 16 bits of the absolute address */
+                // add to what is already there
+                
+                orig  =   ((*(int *)(ptr  )) >> 7) & 0xffff;
+                orig |=  (((*(int *)(ptr+4)) >> 7) & 0xffff) << 16;
+                
+                //patch both at once - assumes always in pairs Low - High
+                
+                *(int *) ptr    = (*(int *) ptr    & (~(0xffff << 7)) ) |  (((val+orig)      & 0xffff) << 7);
+                *(int *)(ptr+4) = (*(int *)(ptr+4) & (~(0xffff << 7)) ) | ((((val+orig)>>16) & 0xffff) << 7);
+            }
+            break;
+        case R_C60HI16:
+            break;
+        default:
+           fprintf(stderr,"FIXME: handle reloc type %x at %lx [%.8x] to %lx\n",
+                    type,addr,(unsigned int )ptr,val);
+            break;
+#else
+#error unsupported processor
+#endif
+        }
+    }
+    /* if the relocation is allocated, we change its symbol table */
+    if (sr->sh_flags & SHF_ALLOC)
+        sr->link = s1->dynsym;
+}
+
+/* relocate relocation table in 'sr' */
+static void relocate_rel(TCCState *s1, Section *sr)
+{
+    Section *s;
+    Elf32_Rel *rel, *rel_end;
+    
+    s = s1->sections[sr->sh_info];
+    rel_end = (Elf32_Rel *)(sr->data + sr->data_offset);
+    for(rel = (Elf32_Rel *)sr->data;
+        rel < rel_end;
+        rel++) {
+        rel->r_offset += s->sh_addr;
+    }
+}
+
+/* count the number of dynamic relocations so that we can reserve
+   their space */
+static int prepare_dynamic_rel(TCCState *s1, Section *sr)
+{
+    Elf32_Rel *rel, *rel_end;
+    int sym_index, esym_index, type, count;
+
+    count = 0;
+    rel_end = (Elf32_Rel *)(sr->data + sr->data_offset);
+    for(rel = (Elf32_Rel *)sr->data; rel < rel_end; rel++) {
+        sym_index = ELF32_R_SYM(rel->r_info);
+        type = ELF32_R_TYPE(rel->r_info);
+        switch(type) {
+        case R_386_32:
+            count++;
+            break;
+        case R_386_PC32:
+            esym_index = s1->symtab_to_dynsym[sym_index];
+            if (esym_index)
+                count++;
+            break;
+        default:
+            break;
+        }
+    }
+    if (count) {
+        /* allocate the section */
+        sr->sh_flags |= SHF_ALLOC;
+        sr->sh_size = count * sizeof(Elf32_Rel);
+    }
+    return count;
+}
+
+static void put_got_offset(TCCState *s1, int index, unsigned long val)
+{
+    int n;
+    unsigned long *tab;
+
+    if (index >= s1->nb_got_offsets) {
+        /* find immediately bigger power of 2 and reallocate array */
+        n = 1;
+        while (index >= n)
+            n *= 2;
+        tab = tcc_realloc(s1->got_offsets, n * sizeof(unsigned long));
+        if (!tab)
+            error("memory full");
+        s1->got_offsets = tab;
+        memset(s1->got_offsets + s1->nb_got_offsets, 0,
+               (n - s1->nb_got_offsets) * sizeof(unsigned long));
+        s1->nb_got_offsets = n;
+    }
+    s1->got_offsets[index] = val;
+}
+
+/* XXX: suppress that */
+static void put32(unsigned char *p, uint32_t val)
+{
+    p[0] = val;
+    p[1] = val >> 8;
+    p[2] = val >> 16;
+    p[3] = val >> 24;
+}
+
+#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_ARM)
+static uint32_t get32(unsigned char *p)
+{
+    return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+}
+#endif
+
+static void build_got(TCCState *s1)
+{
+    unsigned char *ptr;
+
+    /* if no got, then create it */
+    s1->got = new_section(s1, ".got", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
+    s1->got->sh_entsize = 4;
+    add_elf_sym(symtab_section, 0, 4, ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT), 
+                0, s1->got->sh_num, "_GLOBAL_OFFSET_TABLE_");
+    ptr = section_ptr_add(s1->got, 3 * sizeof(int));
+    /* keep space for _DYNAMIC pointer, if present */
+    put32(ptr, 0);
+    /* two dummy got entries */
+    put32(ptr + 4, 0);
+    put32(ptr + 8, 0);
+}
+
+/* put a got entry corresponding to a symbol in symtab_section. 'size'
+   and 'info' can be modifed if more precise info comes from the DLL */
+static void put_got_entry(TCCState *s1,
+                          int reloc_type, unsigned long size, int info, 
+                          int sym_index)
+{
+    int index;
+    const char *name;
+    Elf32_Sym *sym;
+    unsigned long offset;
+    int *ptr;
+
+    if (!s1->got)
+        build_got(s1);
+
+    /* if a got entry already exists for that symbol, no need to add one */
+    if (sym_index < s1->nb_got_offsets &&
+        s1->got_offsets[sym_index] != 0)
+        return;
+    
+    put_got_offset(s1, sym_index, s1->got->data_offset);
+
+    if (s1->dynsym) {
+        sym = &((Elf32_Sym *)symtab_section->data)[sym_index];
+        name = symtab_section->link->data + sym->st_name;
+        offset = sym->st_value;
+#ifdef TCC_TARGET_I386
+        if (reloc_type == R_386_JMP_SLOT) {
+            Section *plt;
+            uint8_t *p;
+            int modrm;
+
+            /* if we build a DLL, we add a %ebx offset */
+            if (s1->output_type == TCC_OUTPUT_DLL)
+                modrm = 0xa3;
+            else
+                modrm = 0x25;
+
+            /* add a PLT entry */
+            plt = s1->plt;
+            if (plt->data_offset == 0) {
+                /* first plt entry */
+                p = section_ptr_add(plt, 16);
+                p[0] = 0xff; /* pushl got + 4 */
+                p[1] = modrm + 0x10;
+                put32(p + 2, 4);
+                p[6] = 0xff; /* jmp *(got + 8) */
+                p[7] = modrm;
+                put32(p + 8, 8);
+            }
+
+            p = section_ptr_add(plt, 16);
+            p[0] = 0xff; /* jmp *(got + x) */
+            p[1] = modrm;
+            put32(p + 2, s1->got->data_offset);
+            p[6] = 0x68; /* push $xxx */
+            put32(p + 7, (plt->data_offset - 32) >> 1);
+            p[11] = 0xe9; /* jmp plt_start */
+            put32(p + 12, -(plt->data_offset));
+
+            /* the symbol is modified so that it will be relocated to
+               the PLT */
+            if (s1->output_type == TCC_OUTPUT_EXE)
+                offset = plt->data_offset - 16;
+        }
+#elif defined(TCC_TARGET_ARM)
+       if (reloc_type == R_ARM_JUMP_SLOT) {
+            Section *plt;
+            uint8_t *p;
+            
+            /* if we build a DLL, we add a %ebx offset */
+            if (s1->output_type == TCC_OUTPUT_DLL)
+                error("DLLs unimplemented!");
+
+            /* add a PLT entry */
+            plt = s1->plt;
+            if (plt->data_offset == 0) {
+                /* first plt entry */
+                p = section_ptr_add(plt, 16);
+               put32(p     , 0xe52de004);
+               put32(p +  4, 0xe59fe010);
+               put32(p +  8, 0xe08fe00e);
+               put32(p + 12, 0xe5bef008);
+            }
+
+            p = section_ptr_add(plt, 16);
+           put32(p  , 0xe59fc004);
+           put32(p+4, 0xe08fc00c);
+           put32(p+8, 0xe59cf000);
+           put32(p+12, s1->got->data_offset);
+
+            /* the symbol is modified so that it will be relocated to
+               the PLT */
+            if (s1->output_type == TCC_OUTPUT_EXE)
+                offset = plt->data_offset - 16;
+        }
+#elif defined(TCC_TARGET_C67)
+        error("C67 got not implemented");
+#else
+#error unsupported CPU
+#endif
+        index = put_elf_sym(s1->dynsym, offset, 
+                            size, info, 0, sym->st_shndx, name);
+        /* put a got entry */
+        put_elf_reloc(s1->dynsym, s1->got, 
+                      s1->got->data_offset, 
+                      reloc_type, index);
+    }
+    ptr = section_ptr_add(s1->got, sizeof(int));
+    *ptr = 0;
+}
+
+/* build GOT and PLT entries */
+static void build_got_entries(TCCState *s1)
+{
+    Section *s, *symtab;
+    Elf32_Rel *rel, *rel_end;
+    Elf32_Sym *sym;
+    int i, type, reloc_type, sym_index;
+
+    for(i = 1; i < s1->nb_sections; i++) {
+        s = s1->sections[i];
+        if (s->sh_type != SHT_REL)
+            continue;
+        /* no need to handle got relocations */
+        if (s->link != symtab_section)
+            continue;
+        symtab = s->link;
+        rel_end = (Elf32_Rel *)(s->data + s->data_offset);
+        for(rel = (Elf32_Rel *)s->data;
+            rel < rel_end;
+            rel++) {
+            type = ELF32_R_TYPE(rel->r_info);
+            switch(type) {
+#if defined(TCC_TARGET_I386)
+            case R_386_GOT32:
+            case R_386_GOTOFF:
+            case R_386_GOTPC:
+            case R_386_PLT32:
+                if (!s1->got)
+                    build_got(s1);
+                if (type == R_386_GOT32 || type == R_386_PLT32) {
+                    sym_index = ELF32_R_SYM(rel->r_info);
+                    sym = &((Elf32_Sym *)symtab_section->data)[sym_index];
+                    /* look at the symbol got offset. If none, then add one */
+                    if (type == R_386_GOT32)
+                        reloc_type = R_386_GLOB_DAT;
+                    else
+                        reloc_type = R_386_JMP_SLOT;
+                    put_got_entry(s1, reloc_type, sym->st_size, sym->st_info, 
+                                  sym_index);
+                }
+                break;
+#elif defined(TCC_TARGET_ARM)
+           case R_ARM_GOT32:
+            case R_ARM_GOTOFF:
+            case R_ARM_GOTPC:
+            case R_ARM_PLT32:
+                if (!s1->got)
+                    build_got(s1);
+                if (type == R_ARM_GOT32 || type == R_ARM_PLT32) {
+                    sym_index = ELF32_R_SYM(rel->r_info);
+                    sym = &((Elf32_Sym *)symtab_section->data)[sym_index];
+                    /* look at the symbol got offset. If none, then add one */
+                    if (type == R_ARM_GOT32)
+                        reloc_type = R_ARM_GLOB_DAT;
+                    else
+                        reloc_type = R_ARM_JUMP_SLOT;
+                    put_got_entry(s1, reloc_type, sym->st_size, sym->st_info, 
+                                  sym_index);
+                }
+                break;
+#elif defined(TCC_TARGET_C67)
+           case R_C60_GOT32:
+            case R_C60_GOTOFF:
+            case R_C60_GOTPC:
+            case R_C60_PLT32:
+                if (!s1->got)
+                    build_got(s1);
+                if (type == R_C60_GOT32 || type == R_C60_PLT32) {
+                    sym_index = ELF32_R_SYM(rel->r_info);
+                    sym = &((Elf32_Sym *)symtab_section->data)[sym_index];
+                    /* look at the symbol got offset. If none, then add one */
+                    if (type == R_C60_GOT32)
+                        reloc_type = R_C60_GLOB_DAT;
+                    else
+                        reloc_type = R_C60_JMP_SLOT;
+                    put_got_entry(s1, reloc_type, sym->st_size, sym->st_info, 
+                                  sym_index);
+                }
+                break;
+#else
+#error unsupported CPU
+#endif
+            default:
+                break;
+            }
+        }
+    }
+}
+
+static Section *new_symtab(TCCState *s1,
+                           const char *symtab_name, int sh_type, int sh_flags,
+                           const char *strtab_name, 
+                           const char *hash_name, int hash_sh_flags)
+{
+    Section *symtab, *strtab, *hash;
+    int *ptr, nb_buckets;
+
+    symtab = new_section(s1, symtab_name, sh_type, sh_flags);
+    symtab->sh_entsize = sizeof(Elf32_Sym);
+    strtab = new_section(s1, strtab_name, SHT_STRTAB, sh_flags);
+    put_elf_str(strtab, "");
+    symtab->link = strtab;
+    put_elf_sym(symtab, 0, 0, 0, 0, 0, NULL);
+    
+    nb_buckets = 1;
+
+    hash = new_section(s1, hash_name, SHT_HASH, hash_sh_flags);
+    hash->sh_entsize = sizeof(int);
+    symtab->hash = hash;
+    hash->link = symtab;
+
+    ptr = section_ptr_add(hash, (2 + nb_buckets + 1) * sizeof(int));
+    ptr[0] = nb_buckets;
+    ptr[1] = 1;
+    memset(ptr + 2, 0, (nb_buckets + 1) * sizeof(int));
+    return symtab;
+}
+
+/* put dynamic tag */
+static void put_dt(Section *dynamic, int dt, unsigned long val)
+{
+    Elf32_Dyn *dyn;
+    dyn = section_ptr_add(dynamic, sizeof(Elf32_Dyn));
+    dyn->d_tag = dt;
+    dyn->d_un.d_val = val;
+}
+
+static void add_init_array_defines(TCCState *s1, const char *section_name)
+{
+    Section *s;
+    long end_offset;
+    char sym_start[1024];
+    char sym_end[1024];
+    
+    snprintf(sym_start, sizeof(sym_start), "__%s_start", section_name + 1);
+    snprintf(sym_end, sizeof(sym_end), "__%s_end", section_name + 1);
+
+    s = find_section(s1, section_name);
+    if (!s) {
+        end_offset = 0;
+        s = data_section;
+    } else {
+        end_offset = s->data_offset;
+    }
+
+    add_elf_sym(symtab_section, 
+                0, 0,
+                ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
+                s->sh_num, sym_start);
+    add_elf_sym(symtab_section, 
+                end_offset, 0,
+                ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
+                s->sh_num, sym_end);
+}
+
+/* add tcc runtime libraries */
+static void tcc_add_runtime(TCCState *s1)
+{
+    char buf[1024];
+
+#ifdef CONFIG_TCC_BCHECK
+    if (do_bounds_check) {
+        unsigned long *ptr;
+        Section *init_section;
+        unsigned char *pinit;
+        int sym_index;
+
+        /* XXX: add an object file to do that */
+        ptr = section_ptr_add(bounds_section, sizeof(unsigned long));
+        *ptr = 0;
+        add_elf_sym(symtab_section, 0, 0, 
+                    ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
+                    bounds_section->sh_num, "__bounds_start");
+        /* add bound check code */
+        snprintf(buf, sizeof(buf), "%s/%s", tcc_lib_path, "bcheck.o");
+        tcc_add_file(s1, buf);
+#ifdef TCC_TARGET_I386
+        if (s1->output_type != TCC_OUTPUT_MEMORY) {
+            /* add 'call __bound_init()' in .init section */
+            init_section = find_section(s1, ".init");
+            pinit = section_ptr_add(init_section, 5);
+            pinit[0] = 0xe8;
+            put32(pinit + 1, -4);
+            sym_index = find_elf_sym(symtab_section, "__bound_init");
+            put_elf_reloc(symtab_section, init_section, 
+                          init_section->data_offset - 4, R_386_PC32, sym_index);
+        }
+#endif
+    }
+#endif
+    /* add libc */
+    if (!s1->nostdlib) {
+        tcc_add_library(s1, "c");
+
+        snprintf(buf, sizeof(buf), "%s/%s", tcc_lib_path, "libtcc1.a");
+        tcc_add_file(s1, buf);
+    }
+    /* add crt end if not memory output */
+    if (s1->output_type != TCC_OUTPUT_MEMORY && !s1->nostdlib) {
+        tcc_add_file(s1, CONFIG_TCC_CRT_PREFIX "/crtn.o");
+    }
+}
+
+/* add various standard linker symbols (must be done after the
+   sections are filled (for example after allocating common
+   symbols)) */
+static void tcc_add_linker_symbols(TCCState *s1)
+{
+    char buf[1024];
+    int i;
+    Section *s;
+
+    add_elf_sym(symtab_section, 
+                text_section->data_offset, 0,
+                ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
+                text_section->sh_num, "_etext");
+    add_elf_sym(symtab_section, 
+                data_section->data_offset, 0,
+                ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
+                data_section->sh_num, "_edata");
+    add_elf_sym(symtab_section, 
+                bss_section->data_offset, 0,
+                ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
+                bss_section->sh_num, "_end");
+    /* horrible new standard ldscript defines */
+    add_init_array_defines(s1, ".preinit_array");
+    add_init_array_defines(s1, ".init_array");
+    add_init_array_defines(s1, ".fini_array");
+    
+    /* add start and stop symbols for sections whose name can be
+       expressed in C */
+    for(i = 1; i < s1->nb_sections; i++) {
+        s = s1->sections[i];
+        if (s->sh_type == SHT_PROGBITS &&
+            (s->sh_flags & SHF_ALLOC)) {
+            const char *p;
+            int ch;
+
+            /* check if section name can be expressed in C */
+            p = s->name;
+            for(;;) {
+                ch = *p;
+                if (!ch)
+                    break;
+                if (!isid(ch) && !isnum(ch))
+                    goto next_sec;
+                p++;
+            }
+            snprintf(buf, sizeof(buf), "__start_%s", s->name);
+            add_elf_sym(symtab_section, 
+                        0, 0,
+                        ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
+                        s->sh_num, buf);
+            snprintf(buf, sizeof(buf), "__stop_%s", s->name);
+            add_elf_sym(symtab_section,
+                        s->data_offset, 0,
+                        ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
+                        s->sh_num, buf);
+        }
+    next_sec: ;
+    }
+}
+
+/* name of ELF interpreter */
+#ifdef __FreeBSD__
+static char elf_interp[] = "/usr/libexec/ld-elf.so.1";
+#else
+static char elf_interp[] = "/lib/ld-linux.so.2";
+#endif
+
+static void tcc_output_binary(TCCState *s1, FILE *f,
+                              const int *section_order)
+{
+    Section *s;
+    int i, offset, size;
+
+    offset = 0;
+    for(i=1;i<s1->nb_sections;i++) {
+        s = s1->sections[section_order[i]];
+        if (s->sh_type != SHT_NOBITS &&
+            (s->sh_flags & SHF_ALLOC)) {
+            while (offset < s->sh_offset) {
+                fputc(0, f);
+                offset++;
+            }
+            size = s->sh_size;
+            fwrite(s->data, 1, size, f);
+            offset += size;
+        }
+    }
+}
+
+/* output an ELF file */
+/* XXX: suppress unneeded sections */
+int tcc_output_file(TCCState *s1, const char *filename)
+{
+    Elf32_Ehdr ehdr;
+    FILE *f;
+    int fd, mode, ret;
+    int *section_order;
+    int shnum, i, phnum, file_offset, offset, size, j, tmp, sh_order_index, k;
+    unsigned long addr;
+    Section *strsec, *s;
+    Elf32_Shdr shdr, *sh;
+    Elf32_Phdr *phdr, *ph;
+    Section *interp, *dynamic, *dynstr;
+    unsigned long saved_dynamic_data_offset;
+    Elf32_Sym *sym;
+    int type, file_type;
+    unsigned long rel_addr, rel_size;
+    
+    file_type = s1->output_type;
+    s1->nb_errors = 0;
+
+    if (file_type != TCC_OUTPUT_OBJ) {
+        tcc_add_runtime(s1);
+    }
+
+    phdr = NULL;
+    section_order = NULL;
+    interp = NULL;
+    dynamic = NULL;
+    dynstr = NULL; /* avoid warning */
+    saved_dynamic_data_offset = 0; /* avoid warning */
+    
+    if (file_type != TCC_OUTPUT_OBJ) {
+        relocate_common_syms();
+
+        tcc_add_linker_symbols(s1);
+
+        if (!s1->static_link) {
+            const char *name;
+            int sym_index, index;
+            Elf32_Sym *esym, *sym_end;
+            
+            if (file_type == TCC_OUTPUT_EXE) {
+                char *ptr;
+                /* add interpreter section only if executable */
+                interp = new_section(s1, ".interp", SHT_PROGBITS, SHF_ALLOC);
+                interp->sh_addralign = 1;
+                ptr = section_ptr_add(interp, sizeof(elf_interp));
+                strcpy(ptr, elf_interp);
+            }
+        
+            /* add dynamic symbol table */
+            s1->dynsym = new_symtab(s1, ".dynsym", SHT_DYNSYM, SHF_ALLOC,
+                                    ".dynstr", 
+                                    ".hash", SHF_ALLOC);
+            dynstr = s1->dynsym->link;
+            
+            /* add dynamic section */
+            dynamic = new_section(s1, ".dynamic", SHT_DYNAMIC, 
+                                  SHF_ALLOC | SHF_WRITE);
+            dynamic->link = dynstr;
+            dynamic->sh_entsize = sizeof(Elf32_Dyn);
+        
+            /* add PLT */
+            s1->plt = new_section(s1, ".plt", SHT_PROGBITS, 
+                                  SHF_ALLOC | SHF_EXECINSTR);
+            s1->plt->sh_entsize = 4;
+
+            build_got(s1);
+
+            /* scan for undefined symbols and see if they are in the
+               dynamic symbols. If a symbol STT_FUNC is found, then we
+               add it in the PLT. If a symbol STT_OBJECT is found, we
+               add it in the .bss section with a suitable relocation */
+            sym_end = (Elf32_Sym *)(symtab_section->data + 
+                                    symtab_section->data_offset);
+            if (file_type == TCC_OUTPUT_EXE) {
+                for(sym = (Elf32_Sym *)symtab_section->data + 1; 
+                    sym < sym_end;
+                    sym++) {
+                    if (sym->st_shndx == SHN_UNDEF) {
+                        name = symtab_section->link->data + sym->st_name;
+                        sym_index = find_elf_sym(s1->dynsymtab_section, name);
+                        if (sym_index) {
+                            esym = &((Elf32_Sym *)s1->dynsymtab_section->data)[sym_index];
+                            type = ELF32_ST_TYPE(esym->st_info);
+                            if (type == STT_FUNC) {
+                                put_got_entry(s1, R_JMP_SLOT, esym->st_size, 
+                                              esym->st_info, 
+                                              sym - (Elf32_Sym *)symtab_section->data);
+                            } else if (type == STT_OBJECT) {
+                                unsigned long offset;
+                                offset = bss_section->data_offset;
+                                /* XXX: which alignment ? */
+                                offset = (offset + 16 - 1) & -16;
+                                index = put_elf_sym(s1->dynsym, offset, esym->st_size, 
+                                                    esym->st_info, 0, 
+                                                    bss_section->sh_num, name);
+                                put_elf_reloc(s1->dynsym, bss_section, 
+                                              offset, R_COPY, index);
+                                offset += esym->st_size;
+                                bss_section->data_offset = offset;
+                            }
+                        } else {
+                                /* STB_WEAK undefined symbols are accepted */
+                                /* XXX: _fp_hw seems to be part of the ABI, so we ignore
+                                   it */
+                            if (ELF32_ST_BIND(sym->st_info) == STB_WEAK ||
+                                !strcmp(name, "_fp_hw")) {
+                            } else {
+                                error_noabort("undefined symbol '%s'", name);
+                            }
+                        }
+                    } else if (s1->rdynamic && 
+                               ELF32_ST_BIND(sym->st_info) != STB_LOCAL) {
+                        /* if -rdynamic option, then export all non
+                           local symbols */
+                        name = symtab_section->link->data + sym->st_name;
+                        put_elf_sym(s1->dynsym, sym->st_value, sym->st_size, 
+                                    sym->st_info, 0, 
+                                    sym->st_shndx, name);
+                    }
+                }
+            
+                if (s1->nb_errors)
+                    goto fail;
+
+                /* now look at unresolved dynamic symbols and export
+                   corresponding symbol */
+                sym_end = (Elf32_Sym *)(s1->dynsymtab_section->data + 
+                                        s1->dynsymtab_section->data_offset);
+                for(esym = (Elf32_Sym *)s1->dynsymtab_section->data + 1; 
+                    esym < sym_end;
+                    esym++) {
+                    if (esym->st_shndx == SHN_UNDEF) {
+                        name = s1->dynsymtab_section->link->data + esym->st_name;
+                        sym_index = find_elf_sym(symtab_section, name);
+                        if (sym_index) {
+                            /* XXX: avoid adding a symbol if already
+                               present because of -rdynamic ? */
+                            sym = &((Elf32_Sym *)symtab_section->data)[sym_index];
+                            put_elf_sym(s1->dynsym, sym->st_value, sym->st_size, 
+                                        sym->st_info, 0, 
+                                        sym->st_shndx, name);
+                        } else {
+                            if (ELF32_ST_BIND(esym->st_info) == STB_WEAK) {
+                                /* weak symbols can stay undefined */
+                            } else {
+                                warning("undefined dynamic symbol '%s'", name);
+                            }
+                        }
+                    }
+                }
+            } else {
+                int nb_syms;
+                /* shared library case : we simply export all the global symbols */
+                nb_syms = symtab_section->data_offset / sizeof(Elf32_Sym);
+                s1->symtab_to_dynsym = tcc_mallocz(sizeof(int) * nb_syms);
+                for(sym = (Elf32_Sym *)symtab_section->data + 1; 
+                    sym < sym_end;
+                    sym++) {
+                    if (ELF32_ST_BIND(sym->st_info) != STB_LOCAL) {
+                        name = symtab_section->link->data + sym->st_name;
+                        index = put_elf_sym(s1->dynsym, sym->st_value, sym->st_size, 
+                                            sym->st_info, 0, 
+                                            sym->st_shndx, name);
+                        s1->symtab_to_dynsym[sym - 
+                                            (Elf32_Sym *)symtab_section->data] = 
+                            index;
+                    }
+                }
+            }
+
+            build_got_entries(s1);
+        
+            /* add a list of needed dlls */
+            for(i = 0; i < s1->nb_loaded_dlls; i++) {
+                DLLReference *dllref = s1->loaded_dlls[i];
+                if (dllref->level == 0)
+                    put_dt(dynamic, DT_NEEDED, put_elf_str(dynstr, dllref->name));
+            }
+            /* XXX: currently, since we do not handle PIC code, we
+               must relocate the readonly segments */
+            if (file_type == TCC_OUTPUT_DLL)
+                put_dt(dynamic, DT_TEXTREL, 0);
+
+            /* add necessary space for other entries */
+            saved_dynamic_data_offset = dynamic->data_offset;
+            dynamic->data_offset += 8 * 9;
+        } else {
+            /* still need to build got entries in case of static link */
+            build_got_entries(s1);
+        }
+    }
+
+    memset(&ehdr, 0, sizeof(ehdr));
+
+    /* we add a section for symbols */
+    strsec = new_section(s1, ".shstrtab", SHT_STRTAB, 0);
+    put_elf_str(strsec, "");
+    
+    /* compute number of sections */
+    shnum = s1->nb_sections;
+
+    /* this array is used to reorder sections in the output file */
+    section_order = tcc_malloc(sizeof(int) * shnum);
+    section_order[0] = 0;
+    sh_order_index = 1;
+    
+    /* compute number of program headers */
+    switch(file_type) {
+    default:
+    case TCC_OUTPUT_OBJ:
+        phnum = 0;
+        break;
+    case TCC_OUTPUT_EXE:
+        if (!s1->static_link)
+            phnum = 4;
+        else
+            phnum = 2;
+        break;
+    case TCC_OUTPUT_DLL:
+        phnum = 3;
+        break;
+    }
+
+    /* allocate strings for section names and decide if an unallocated
+       section should be output */
+    /* NOTE: the strsec section comes last, so its size is also
+       correct ! */
+    for(i = 1; i < s1->nb_sections; i++) {
+        s = s1->sections[i];
+        s->sh_name = put_elf_str(strsec, s->name);
+        /* when generating a DLL, we include relocations but we may
+           patch them */
+        if (file_type == TCC_OUTPUT_DLL && 
+            s->sh_type == SHT_REL && 
+            !(s->sh_flags & SHF_ALLOC)) {
+            prepare_dynamic_rel(s1, s);
+        } else if (do_debug || 
+            file_type == TCC_OUTPUT_OBJ || 
+            (s->sh_flags & SHF_ALLOC) ||
+            i == (s1->nb_sections - 1)) {
+            /* we output all sections if debug or object file */
+            s->sh_size = s->data_offset;
+        }
+    }
+
+    /* allocate program segment headers */
+    phdr = tcc_mallocz(phnum * sizeof(Elf32_Phdr));
+        
+    if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) {
+        file_offset = sizeof(Elf32_Ehdr) + phnum * sizeof(Elf32_Phdr);
+    } else {
+        file_offset = 0;
+    }
+    if (phnum > 0) {
+        /* compute section to program header mapping */
+        if (s1->has_text_addr) { 
+            int a_offset, p_offset;
+            addr = s1->text_addr;
+            /* we ensure that (addr % ELF_PAGE_SIZE) == file_offset %
+               ELF_PAGE_SIZE */
+            a_offset = addr & (ELF_PAGE_SIZE - 1);
+            p_offset = file_offset & (ELF_PAGE_SIZE - 1);
+            if (a_offset < p_offset) 
+                a_offset += ELF_PAGE_SIZE;
+            file_offset += (a_offset - p_offset);
+        } else {
+            if (file_type == TCC_OUTPUT_DLL)
+                addr = 0;
+            else
+                addr = ELF_START_ADDR;
+            /* compute address after headers */
+            addr += (file_offset & (ELF_PAGE_SIZE - 1));
+        }
+        
+        /* dynamic relocation table information, for .dynamic section */
+        rel_size = 0;
+        rel_addr = 0;
+
+        /* leave one program header for the program interpreter */
+        ph = &phdr[0];
+        if (interp)
+            ph++;
+
+        for(j = 0; j < 2; j++) {
+            ph->p_type = PT_LOAD;
+            if (j == 0)
+                ph->p_flags = PF_R | PF_X;
+            else
+                ph->p_flags = PF_R | PF_W;
+            ph->p_align = ELF_PAGE_SIZE;
+            
+            /* we do the following ordering: interp, symbol tables,
+               relocations, progbits, nobits */
+            /* XXX: do faster and simpler sorting */
+            for(k = 0; k < 5; k++) {
+                for(i = 1; i < s1->nb_sections; i++) {
+                    s = s1->sections[i];
+                    /* compute if section should be included */
+                    if (j == 0) {
+                        if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) != 
+                            SHF_ALLOC)
+                            continue;
+                    } else {
+                        if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) != 
+                            (SHF_ALLOC | SHF_WRITE))
+                            continue;
+                    }
+                    if (s == interp) {
+                        if (k != 0)
+                            continue;
+                    } else if (s->sh_type == SHT_DYNSYM ||
+                               s->sh_type == SHT_STRTAB ||
+                               s->sh_type == SHT_HASH) {
+                        if (k != 1)
+                            continue;
+                    } else if (s->sh_type == SHT_REL) {
+                        if (k != 2)
+                            continue;
+                    } else if (s->sh_type == SHT_NOBITS) {
+                        if (k != 4)
+                            continue;
+                    } else {
+                        if (k != 3)
+                            continue;
+                    }
+                    section_order[sh_order_index++] = i;
+
+                    /* section matches: we align it and add its size */
+                    tmp = addr;
+                    addr = (addr + s->sh_addralign - 1) & 
+                        ~(s->sh_addralign - 1);
+                    file_offset += addr - tmp;
+                    s->sh_offset = file_offset;
+                    s->sh_addr = addr;
+                    
+                    /* update program header infos */
+                    if (ph->p_offset == 0) {
+                        ph->p_offset = file_offset;
+                        ph->p_vaddr = addr;
+                        ph->p_paddr = ph->p_vaddr;
+                    }
+                    /* update dynamic relocation infos */
+                    if (s->sh_type == SHT_REL) {
+                        if (rel_size == 0)
+                            rel_addr = addr;
+                        rel_size += s->sh_size;
+                    }
+                    addr += s->sh_size;
+                    if (s->sh_type != SHT_NOBITS)
+                        file_offset += s->sh_size;
+                }
+            }
+            ph->p_filesz = file_offset - ph->p_offset;
+            ph->p_memsz = addr - ph->p_vaddr;
+            ph++;
+            if (j == 0) {
+                if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) {
+                    /* if in the middle of a page, we duplicate the page in
+                       memory so that one copy is RX and the other is RW */
+                    if ((addr & (ELF_PAGE_SIZE - 1)) != 0)
+                        addr += ELF_PAGE_SIZE;
+                } else {
+                    addr = (addr + ELF_PAGE_SIZE - 1) & ~(ELF_PAGE_SIZE - 1);
+                    file_offset = (file_offset + ELF_PAGE_SIZE - 1) & 
+                        ~(ELF_PAGE_SIZE - 1);
+                }
+            }
+        }
+
+        /* if interpreter, then add corresponing program header */
+        if (interp) {
+            ph = &phdr[0];
+            
+            ph->p_type = PT_INTERP;
+            ph->p_offset = interp->sh_offset;
+            ph->p_vaddr = interp->sh_addr;
+            ph->p_paddr = ph->p_vaddr;
+            ph->p_filesz = interp->sh_size;
+            ph->p_memsz = interp->sh_size;
+            ph->p_flags = PF_R;
+            ph->p_align = interp->sh_addralign;
+        }
+        
+        /* if dynamic section, then add corresponing program header */
+        if (dynamic) {
+            Elf32_Sym *sym_end;
+
+            ph = &phdr[phnum - 1];
+            
+            ph->p_type = PT_DYNAMIC;
+            ph->p_offset = dynamic->sh_offset;
+            ph->p_vaddr = dynamic->sh_addr;
+            ph->p_paddr = ph->p_vaddr;
+            ph->p_filesz = dynamic->sh_size;
+            ph->p_memsz = dynamic->sh_size;
+            ph->p_flags = PF_R | PF_W;
+            ph->p_align = dynamic->sh_addralign;
+
+            /* put GOT dynamic section address */
+            put32(s1->got->data, dynamic->sh_addr);
+
+            /* relocate the PLT */
+            if (file_type == TCC_OUTPUT_EXE) {
+                uint8_t *p, *p_end;
+
+                p = s1->plt->data;
+                p_end = p + s1->plt->data_offset;
+                if (p < p_end) {
+#if defined(TCC_TARGET_I386)
+                    put32(p + 2, get32(p + 2) + s1->got->sh_addr);
+                    put32(p + 8, get32(p + 8) + s1->got->sh_addr);
+                    p += 16;
+                    while (p < p_end) {
+                        put32(p + 2, get32(p + 2) + s1->got->sh_addr);
+                        p += 16;
+                    }
+#elif defined(TCC_TARGET_ARM)
+                   int x;
+                   x=s1->got->sh_addr - s1->plt->sh_addr - 12;
+                   p +=16;
+                   while (p < p_end) {
+                       put32(p + 12, x + get32(p + 12) + s1->plt->data - p);
+                       p += 16;
+                   }
+#elif defined(TCC_TARGET_C67)
+                    /* XXX: TODO */
+#else
+#error unsupported CPU
+#endif
+                }
+            }
+
+            /* relocate symbols in .dynsym */
+            sym_end = (Elf32_Sym *)(s1->dynsym->data + s1->dynsym->data_offset);
+            for(sym = (Elf32_Sym *)s1->dynsym->data + 1; 
+                sym < sym_end;
+                sym++) {
+                if (sym->st_shndx == SHN_UNDEF) {
+                    /* relocate to the PLT if the symbol corresponds
+                       to a PLT entry */
+                    if (sym->st_value)
+                        sym->st_value += s1->plt->sh_addr;
+                } else if (sym->st_shndx < SHN_LORESERVE) {
+                    /* do symbol relocation */
+                    sym->st_value += s1->sections[sym->st_shndx]->sh_addr;
+                }
+            }
+
+            /* put dynamic section entries */
+            dynamic->data_offset = saved_dynamic_data_offset;
+            put_dt(dynamic, DT_HASH, s1->dynsym->hash->sh_addr);
+            put_dt(dynamic, DT_STRTAB, dynstr->sh_addr);
+            put_dt(dynamic, DT_SYMTAB, s1->dynsym->sh_addr);
+            put_dt(dynamic, DT_STRSZ, dynstr->data_offset);
+            put_dt(dynamic, DT_SYMENT, sizeof(Elf32_Sym));
+            put_dt(dynamic, DT_REL, rel_addr);
+            put_dt(dynamic, DT_RELSZ, rel_size);
+            put_dt(dynamic, DT_RELENT, sizeof(Elf32_Rel));
+            put_dt(dynamic, DT_NULL, 0);
+        }
+
+        ehdr.e_phentsize = sizeof(Elf32_Phdr);
+        ehdr.e_phnum = phnum;
+        ehdr.e_phoff = sizeof(Elf32_Ehdr);
+    }
+
+    /* all other sections come after */
+    for(i = 1; i < s1->nb_sections; i++) {
+        s = s1->sections[i];
+        if (phnum > 0 && (s->sh_flags & SHF_ALLOC))
+            continue;
+        section_order[sh_order_index++] = i;
+        
+        file_offset = (file_offset + s->sh_addralign - 1) & 
+            ~(s->sh_addralign - 1);
+        s->sh_offset = file_offset;
+        if (s->sh_type != SHT_NOBITS)
+            file_offset += s->sh_size;
+    }
+    
+    /* if building executable or DLL, then relocate each section
+       except the GOT which is already relocated */
+    if (file_type != TCC_OUTPUT_OBJ) {
+        relocate_syms(s1, 0);
+
+        if (s1->nb_errors != 0) {
+        fail:
+            ret = -1;
+            goto the_end;
+        }
+
+        /* relocate sections */
+        /* XXX: ignore sections with allocated relocations ? */
+        for(i = 1; i < s1->nb_sections; i++) {
+            s = s1->sections[i];
+            if (s->reloc && s != s1->got)
+                relocate_section(s1, s);
+        }
+
+        /* relocate relocation entries if the relocation tables are
+           allocated in the executable */
+        for(i = 1; i < s1->nb_sections; i++) {
+            s = s1->sections[i];
+            if ((s->sh_flags & SHF_ALLOC) &&
+                s->sh_type == SHT_REL) {
+                relocate_rel(s1, s);
+            }
+        }
+
+        /* get entry point address */
+        if (file_type == TCC_OUTPUT_EXE)
+            ehdr.e_entry = (unsigned long)tcc_get_symbol_err(s1, "_start");
+        else
+            ehdr.e_entry = text_section->sh_addr; /* XXX: is it correct ? */
+    }
+
+    /* write elf file */
+    if (file_type == TCC_OUTPUT_OBJ)
+        mode = 0666;
+    else
+        mode = 0777;
+    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode); 
+    if (fd < 0) {
+        error_noabort("could not write '%s'", filename);
+        goto fail;
+    }
+    f = fdopen(fd, "wb");
+
+#ifdef TCC_TARGET_COFF
+    if (s1->output_format == TCC_OUTPUT_FORMAT_COFF) {
+        tcc_output_coff(s1, f);
+    } else
+#endif
+    if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) {
+        sort_syms(s1, symtab_section);
+        
+        /* align to 4 */
+        file_offset = (file_offset + 3) & -4;
+    
+        /* fill header */
+        ehdr.e_ident[0] = ELFMAG0;
+        ehdr.e_ident[1] = ELFMAG1;
+        ehdr.e_ident[2] = ELFMAG2;
+        ehdr.e_ident[3] = ELFMAG3;
+        ehdr.e_ident[4] = ELFCLASS32;
+        ehdr.e_ident[5] = ELFDATA2LSB;
+        ehdr.e_ident[6] = EV_CURRENT;
+#ifdef __FreeBSD__
+        ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD;
+#endif
+#ifdef TCC_TARGET_ARM
+        ehdr.e_ident[EI_OSABI] = ELFOSABI_ARM;
+#endif
+        switch(file_type) {
+        default:
+        case TCC_OUTPUT_EXE:
+            ehdr.e_type = ET_EXEC;
+            break;
+        case TCC_OUTPUT_DLL:
+            ehdr.e_type = ET_DYN;
+            break;
+        case TCC_OUTPUT_OBJ:
+            ehdr.e_type = ET_REL;
+            break;
+        }
+        ehdr.e_machine = EM_TCC_TARGET;
+        ehdr.e_version = EV_CURRENT;
+        ehdr.e_shoff = file_offset;
+        ehdr.e_ehsize = sizeof(Elf32_Ehdr);
+        ehdr.e_shentsize = sizeof(Elf32_Shdr);
+        ehdr.e_shnum = shnum;
+        ehdr.e_shstrndx = shnum - 1;
+        
+        fwrite(&ehdr, 1, sizeof(Elf32_Ehdr), f);
+        fwrite(phdr, 1, phnum * sizeof(Elf32_Phdr), f);
+        offset = sizeof(Elf32_Ehdr) + phnum * sizeof(Elf32_Phdr);
+
+        for(i=1;i<s1->nb_sections;i++) {
+            s = s1->sections[section_order[i]];
+            if (s->sh_type != SHT_NOBITS) {
+                while (offset < s->sh_offset) {
+                    fputc(0, f);
+                    offset++;
+                }
+                size = s->sh_size;
+                fwrite(s->data, 1, size, f);
+                offset += size;
+            }
+        }
+
+        /* output section headers */
+        while (offset < ehdr.e_shoff) {
+            fputc(0, f);
+            offset++;
+        }
+    
+        for(i=0;i<s1->nb_sections;i++) {
+            sh = &shdr;
+            memset(sh, 0, sizeof(Elf32_Shdr));
+            s = s1->sections[i];
+            if (s) {
+                sh->sh_name = s->sh_name;
+                sh->sh_type = s->sh_type;
+                sh->sh_flags = s->sh_flags;
+                sh->sh_entsize = s->sh_entsize;
+                sh->sh_info = s->sh_info;
+                if (s->link)
+                    sh->sh_link = s->link->sh_num;
+                sh->sh_addralign = s->sh_addralign;
+                sh->sh_addr = s->sh_addr;
+                sh->sh_offset = s->sh_offset;
+                sh->sh_size = s->sh_size;
+            }
+            fwrite(sh, 1, sizeof(Elf32_Shdr), f);
+        }
+    } else {
+        tcc_output_binary(s1, f, section_order);
+    }
+    fclose(f);
+
+    ret = 0;
+ the_end:
+    tcc_free(s1->symtab_to_dynsym);
+    tcc_free(section_order);
+    tcc_free(phdr);
+    tcc_free(s1->got_offsets);
+    return ret;
+}
+
+static void *load_data(int fd, unsigned long file_offset, unsigned long size)
+{
+    void *data;
+
+    data = tcc_malloc(size);
+    lseek(fd, file_offset, SEEK_SET);
+    read(fd, data, size);
+    return data;
+}
+
+typedef struct SectionMergeInfo {
+    Section *s;            /* corresponding existing section */
+    unsigned long offset;  /* offset of the new section in the existing section */
+    uint8_t new_section;       /* true if section 's' was added */
+    uint8_t link_once;         /* true if link once section */
+} SectionMergeInfo;
+
+/* load an object file and merge it with current files */
+/* XXX: handle correctly stab (debug) info */
+static int tcc_load_object_file(TCCState *s1, 
+                                int fd, unsigned long file_offset)
+{ 
+    Elf32_Ehdr ehdr;
+    Elf32_Shdr *shdr, *sh;
+    int size, i, j, offset, offseti, nb_syms, sym_index, ret;
+    unsigned char *strsec, *strtab;
+    int *old_to_new_syms;
+    char *sh_name, *name;
+    SectionMergeInfo *sm_table, *sm;
+    Elf32_Sym *sym, *symtab;
+    Elf32_Rel *rel, *rel_end;
+    Section *s;
+
+    if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr))
+        goto fail1;
+    if (ehdr.e_ident[0] != ELFMAG0 ||
+        ehdr.e_ident[1] != ELFMAG1 ||
+        ehdr.e_ident[2] != ELFMAG2 ||
+        ehdr.e_ident[3] != ELFMAG3)
+        goto fail1;
+    /* test if object file */
+    if (ehdr.e_type != ET_REL)
+        goto fail1;
+    /* test CPU specific stuff */
+    if (ehdr.e_ident[5] != ELFDATA2LSB ||
+        ehdr.e_machine != EM_TCC_TARGET) {
+    fail1:
+        error_noabort("invalid object file");
+        return -1;
+    }
+    /* read sections */
+    shdr = load_data(fd, file_offset + ehdr.e_shoff, 
+                     sizeof(Elf32_Shdr) * ehdr.e_shnum);
+    sm_table = tcc_mallocz(sizeof(SectionMergeInfo) * ehdr.e_shnum);
+    
+    /* load section names */
+    sh = &shdr[ehdr.e_shstrndx];
+    strsec = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
+
+    /* load symtab and strtab */
+    old_to_new_syms = NULL;
+    symtab = NULL;
+    strtab = NULL;
+    nb_syms = 0;
+    for(i = 1; i < ehdr.e_shnum; i++) {
+        sh = &shdr[i];
+        if (sh->sh_type == SHT_SYMTAB) {
+            if (symtab) {
+                error_noabort("object must contain only one symtab");
+            fail:
+                ret = -1;
+                goto the_end;
+            }
+            nb_syms = sh->sh_size / sizeof(Elf32_Sym);
+            symtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
+            sm_table[i].s = symtab_section;
+
+            /* now load strtab */
+            sh = &shdr[sh->sh_link];
+            strtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
+        }
+    }
+        
+    /* now examine each section and try to merge its content with the
+       ones in memory */
+    for(i = 1; i < ehdr.e_shnum; i++) {
+        /* no need to examine section name strtab */
+        if (i == ehdr.e_shstrndx)
+            continue;
+        sh = &shdr[i];
+        sh_name = strsec + sh->sh_name;
+        /* ignore sections types we do not handle */
+        if (sh->sh_type != SHT_PROGBITS &&
+            sh->sh_type != SHT_REL && 
+            sh->sh_type != SHT_NOBITS)
+            continue;
+        if (sh->sh_addralign < 1)
+            sh->sh_addralign = 1;
+        /* find corresponding section, if any */
+        for(j = 1; j < s1->nb_sections;j++) {
+            s = s1->sections[j];
+            if (!strcmp(s->name, sh_name)) {
+                if (!strncmp(sh_name, ".gnu.linkonce", 
+                             sizeof(".gnu.linkonce") - 1)) {
+                    /* if a 'linkonce' section is already present, we
+                       do not add it again. It is a little tricky as
+                       symbols can still be defined in
+                       it. */
+                    sm_table[i].link_once = 1;
+                    goto next;
+                } else {
+                    goto found;
+                }
+            }
+        }
+        /* not found: create new section */
+        s = new_section(s1, sh_name, sh->sh_type, sh->sh_flags);
+        /* take as much info as possible from the section. sh_link and
+           sh_info will be updated later */
+        s->sh_addralign = sh->sh_addralign;
+        s->sh_entsize = sh->sh_entsize;
+        sm_table[i].new_section = 1;
+    found:
+        if (sh->sh_type != s->sh_type) {
+            error_noabort("invalid section type");
+            goto fail;
+        }
+
+        /* align start of section */
+        offset = s->data_offset;
+        size = sh->sh_addralign - 1;
+        offset = (offset + size) & ~size;
+        if (sh->sh_addralign > s->sh_addralign)
+            s->sh_addralign = sh->sh_addralign;
+        s->data_offset = offset;
+        sm_table[i].offset = offset;
+        sm_table[i].s = s;
+        /* concatenate sections */
+        size = sh->sh_size;
+        if (sh->sh_type != SHT_NOBITS) {
+            unsigned char *ptr;
+            lseek(fd, file_offset + sh->sh_offset, SEEK_SET);
+            ptr = section_ptr_add(s, size);
+            read(fd, ptr, size);
+        } else {
+            s->data_offset += size;
+        }
+    next: ;
+    }
+
+    /* second short pass to update sh_link and sh_info fields of new
+       sections */
+    sm = sm_table;
+    for(i = 1; i < ehdr.e_shnum; i++) {
+        s = sm_table[i].s;
+        if (!s || !sm_table[i].new_section)
+            continue;
+        sh = &shdr[i];
+        if (sh->sh_link > 0)
+            s->link = sm_table[sh->sh_link].s;
+        if (sh->sh_type == SHT_REL) {
+            s->sh_info = sm_table[sh->sh_info].s->sh_num;
+            /* update backward link */
+            s1->sections[s->sh_info]->reloc = s;
+        }
+    }
+
+    /* resolve symbols */
+    old_to_new_syms = tcc_mallocz(nb_syms * sizeof(int));
+
+    sym = symtab + 1;
+    for(i = 1; i < nb_syms; i++, sym++) {
+        if (sym->st_shndx != SHN_UNDEF &&
+            sym->st_shndx < SHN_LORESERVE) {
+            sm = &sm_table[sym->st_shndx];
+            if (sm->link_once) {
+                /* if a symbol is in a link once section, we use the
+                   already defined symbol. It is very important to get
+                   correct relocations */
+                if (ELF32_ST_BIND(sym->st_info) != STB_LOCAL) {
+                    name = strtab + sym->st_name;
+                    sym_index = find_elf_sym(symtab_section, name);
+                    if (sym_index)
+                        old_to_new_syms[i] = sym_index;
+                }
+                continue;
+            }
+            /* if no corresponding section added, no need to add symbol */
+            if (!sm->s)
+                continue;
+            /* convert section number */
+            sym->st_shndx = sm->s->sh_num;
+            /* offset value */
+            sym->st_value += sm->offset;
+        }
+        /* add symbol */
+        name = strtab + sym->st_name;
+        sym_index = add_elf_sym(symtab_section, sym->st_value, sym->st_size, 
+                                sym->st_info, sym->st_other, 
+                                sym->st_shndx, name);
+        old_to_new_syms[i] = sym_index;
+    }
+
+    /* third pass to patch relocation entries */
+    for(i = 1; i < ehdr.e_shnum; i++) {
+        s = sm_table[i].s;
+        if (!s)
+            continue;
+        sh = &shdr[i];
+        offset = sm_table[i].offset;
+        switch(s->sh_type) {
+        case SHT_REL:
+            /* take relocation offset information */
+            offseti = sm_table[sh->sh_info].offset;
+            rel_end = (Elf32_Rel *)(s->data + s->data_offset);
+            for(rel = (Elf32_Rel *)(s->data + offset);
+                rel < rel_end;
+                rel++) {
+                int type;
+                unsigned sym_index;
+                /* convert symbol index */
+                type = ELF32_R_TYPE(rel->r_info);
+                sym_index = ELF32_R_SYM(rel->r_info);
+                /* NOTE: only one symtab assumed */
+                if (sym_index >= nb_syms)
+                    goto invalid_reloc;
+                sym_index = old_to_new_syms[sym_index];
+                if (!sym_index) {
+                invalid_reloc:
+                    error_noabort("Invalid relocation entry");
+                    goto fail;
+                }
+                rel->r_info = ELF32_R_INFO(sym_index, type);
+                /* offset the relocation offset */
+                rel->r_offset += offseti;
+            }
+            break;
+        default:
+            break;
+        }
+    }
+    
+    ret = 0;
+ the_end:
+    tcc_free(symtab);
+    tcc_free(strtab);
+    tcc_free(old_to_new_syms);
+    tcc_free(sm_table);
+    tcc_free(strsec);
+    tcc_free(shdr);
+    return ret;
+}
+
+#define ARMAG  "!<arch>\012"   /* For COFF and a.out archives */
+
+typedef struct ArchiveHeader {
+    char ar_name[16];          /* name of this member */
+    char ar_date[12];          /* file mtime */
+    char ar_uid[6];            /* owner uid; printed as decimal */
+    char ar_gid[6];            /* owner gid; printed as decimal */
+    char ar_mode[8];           /* file mode, printed as octal   */
+    char ar_size[10];          /* file size, printed as decimal */
+    char ar_fmag[2];           /* should contain ARFMAG */
+} ArchiveHeader;
+
+static int get_be32(const uint8_t *b)
+{
+    return b[3] | (b[2] << 8) | (b[1] << 16) | (b[0] << 24);
+}
+
+/* load only the objects which resolve undefined symbols */
+static int tcc_load_alacarte(TCCState *s1, int fd, int size)
+{
+    int i, bound, nsyms, sym_index, off, ret;
+    uint8_t *data;
+    const char *ar_names, *p;
+    const uint8_t *ar_index;
+    Elf32_Sym *sym;
+
+    data = tcc_malloc(size);
+    if (read(fd, data, size) != size)
+        goto fail;
+    nsyms = get_be32(data);
+    ar_index = data + 4;
+    ar_names = ar_index + nsyms * 4;
+
+    do {
+       bound = 0;
+       for(p = ar_names, i = 0; i < nsyms; i++, p += strlen(p)+1) {
+           sym_index = find_elf_sym(symtab_section, p);
+           if(sym_index) {
+               sym = &((Elf32_Sym *)symtab_section->data)[sym_index];
+               if(sym->st_shndx == SHN_UNDEF) {
+                   off = get_be32(ar_index + i * 4) + sizeof(ArchiveHeader);
+#if 0
+                   printf("%5d\t%s\t%08x\n", i, p, sym->st_shndx);
+#endif
+                   ++bound;
+                   lseek(fd, off, SEEK_SET);
+                   if(tcc_load_object_file(s1, fd, off) < 0) {
+                    fail:
+                        ret = -1;
+                        goto the_end;
+                    }
+               }
+           }
+       }
+    } while(bound);
+    ret = 0;
+ the_end:
+    tcc_free(data);
+    return ret;
+}
+
+/* load a '.a' file */
+static int tcc_load_archive(TCCState *s1, int fd)
+{
+    ArchiveHeader hdr;
+    char ar_size[11];
+    char ar_name[17];
+    char magic[8];
+    int size, len, i;
+    unsigned long file_offset;
+
+    /* skip magic which was already checked */
+    read(fd, magic, sizeof(magic));
+    
+    for(;;) {
+        len = read(fd, &hdr, sizeof(hdr));
+        if (len == 0)
+            break;
+        if (len != sizeof(hdr)) {
+            error_noabort("invalid archive");
+            return -1;
+        }
+        memcpy(ar_size, hdr.ar_size, sizeof(hdr.ar_size));
+        ar_size[sizeof(hdr.ar_size)] = '\0';
+        size = strtol(ar_size, NULL, 0);
+        memcpy(ar_name, hdr.ar_name, sizeof(hdr.ar_name));
+        for(i = sizeof(hdr.ar_name) - 1; i >= 0; i--) {
+            if (ar_name[i] != ' ')
+                break;
+        }
+        ar_name[i + 1] = '\0';
+        //        printf("name='%s' size=%d %s\n", ar_name, size, ar_size);
+        file_offset = lseek(fd, 0, SEEK_CUR);
+        /* align to even */
+        size = (size + 1) & ~1;
+        if (!strcmp(ar_name, "/")) {
+            /* coff symbol table : we handle it */
+           if(s1->alacarte_link)
+               return tcc_load_alacarte(s1, fd, size);
+        } else if (!strcmp(ar_name, "//") ||
+                   !strcmp(ar_name, "__.SYMDEF") ||
+                   !strcmp(ar_name, "__.SYMDEF/") ||
+                   !strcmp(ar_name, "ARFILENAMES/")) {
+            /* skip symbol table or archive names */
+        } else {
+            if (tcc_load_object_file(s1, fd, file_offset) < 0)
+                return -1;
+        }
+        lseek(fd, file_offset + size, SEEK_SET);
+    }
+    return 0;
+}
+
+/* load a DLL and all referenced DLLs. 'level = 0' means that the DLL
+   is referenced by the user (so it should be added as DT_NEEDED in
+   the generated ELF file) */
+static int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level)
+{ 
+    Elf32_Ehdr ehdr;
+    Elf32_Shdr *shdr, *sh, *sh1;
+    int i, nb_syms, nb_dts, sym_bind, ret;
+    Elf32_Sym *sym, *dynsym;
+    Elf32_Dyn *dt, *dynamic;
+    unsigned char *dynstr;
+    const char *name, *soname, *p;
+    DLLReference *dllref;
+    
+    read(fd, &ehdr, sizeof(ehdr));
+
+    /* test CPU specific stuff */
+    if (ehdr.e_ident[5] != ELFDATA2LSB ||
+        ehdr.e_machine != EM_TCC_TARGET) {
+        error_noabort("bad architecture");
+        return -1;
+    }
+
+    /* read sections */
+    shdr = load_data(fd, ehdr.e_shoff, sizeof(Elf32_Shdr) * ehdr.e_shnum);
+
+    /* load dynamic section and dynamic symbols */
+    nb_syms = 0;
+    nb_dts = 0;
+    dynamic = NULL;
+    dynsym = NULL; /* avoid warning */
+    dynstr = NULL; /* avoid warning */
+    for(i = 0, sh = shdr; i < ehdr.e_shnum; i++, sh++) {
+        switch(sh->sh_type) {
+        case SHT_DYNAMIC:
+            nb_dts = sh->sh_size / sizeof(Elf32_Dyn);
+            dynamic = load_data(fd, sh->sh_offset, sh->sh_size);
+            break;
+        case SHT_DYNSYM:
+            nb_syms = sh->sh_size / sizeof(Elf32_Sym);
+            dynsym = load_data(fd, sh->sh_offset, sh->sh_size);
+            sh1 = &shdr[sh->sh_link];
+            dynstr = load_data(fd, sh1->sh_offset, sh1->sh_size);
+            break;
+        default:
+            break;
+        }
+    }
+    
+    /* compute the real library name */
+    soname = filename;
+    p = strrchr(soname, '/');
+    if (p)
+        soname = p + 1;
+        
+    for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) {
+        if (dt->d_tag == DT_SONAME) {
+            soname = dynstr + dt->d_un.d_val;
+        }
+    }
+
+    /* if the dll is already loaded, do not load it */
+    for(i = 0; i < s1->nb_loaded_dlls; i++) {
+        dllref = s1->loaded_dlls[i];
+        if (!strcmp(soname, dllref->name)) {
+            /* but update level if needed */
+            if (level < dllref->level)
+                dllref->level = level;
+            ret = 0;
+            goto the_end;
+        }
+    }
+    
+    //    printf("loading dll '%s'\n", soname);
+
+    /* add the dll and its level */
+    dllref = tcc_malloc(sizeof(DLLReference) + strlen(soname));
+    dllref->level = level;
+    strcpy(dllref->name, soname);
+    dynarray_add((void ***)&s1->loaded_dlls, &s1->nb_loaded_dlls, dllref);
+
+    /* add dynamic symbols in dynsym_section */
+    for(i = 1, sym = dynsym + 1; i < nb_syms; i++, sym++) {
+        sym_bind = ELF32_ST_BIND(sym->st_info);
+        if (sym_bind == STB_LOCAL)
+            continue;
+        name = dynstr + sym->st_name;
+        add_elf_sym(s1->dynsymtab_section, sym->st_value, sym->st_size,
+                    sym->st_info, sym->st_other, sym->st_shndx, name);
+    }
+
+    /* load all referenced DLLs */
+    for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) {
+        switch(dt->d_tag) {
+        case DT_NEEDED:
+            name = dynstr + dt->d_un.d_val;
+            for(i = 0; i < s1->nb_loaded_dlls; i++) {
+                dllref = s1->loaded_dlls[i];
+                if (!strcmp(name, dllref->name))
+                    goto already_loaded;
+            }
+            if (tcc_add_dll(s1, name, AFF_REFERENCED_DLL) < 0) {
+                error_noabort("referenced dll '%s' not found", name);
+                ret = -1;
+                goto the_end;
+            }
+        already_loaded:
+            break;
+        }
+    }
+    ret = 0;
+ the_end:
+    tcc_free(dynstr);
+    tcc_free(dynsym);
+    tcc_free(dynamic);
+    tcc_free(shdr);
+    return ret;
+}
+
+#define LD_TOK_NAME 256
+#define LD_TOK_EOF  (-1)
+
+/* return next ld script token */
+static int ld_next(TCCState *s1, char *name, int name_size)
+{
+    int c;
+    char *q;
+
+ redo:
+    switch(ch) {
+    case ' ':
+    case '\t':
+    case '\f':
+    case '\v':
+    case '\r':
+    case '\n':
+        inp();
+        goto redo;
+    case '/':
+        minp();
+        if (ch == '*') {
+            file->buf_ptr = parse_comment(file->buf_ptr);
+            ch = file->buf_ptr[0];
+            goto redo;
+        } else {
+            q = name;
+            *q++ = '/';
+            goto parse_name;
+        }
+        break;
+    case 'a' ... 'z':
+    case 'A' ... 'Z':
+    case '_':
+    case '\\':
+    case '.':
+    case '$':
+    case '~':
+        q = name;
+    parse_name:
+        for(;;) {
+            if (!((ch >= 'a' && ch <= 'z') ||
+                  (ch >= 'A' && ch <= 'Z') ||
+                  (ch >= '0' && ch <= '9') ||
+                  strchr("/.-_+=$:\\,~", ch)))
+                break;
+            if ((q - name) < name_size - 1) {
+                *q++ = ch;
+            }
+            minp();
+        }
+        *q = '\0';
+        c = LD_TOK_NAME;
+        break;
+    case CH_EOF:
+        c = LD_TOK_EOF;
+        break;
+    default:
+        c = ch;
+        inp();
+        break;
+    }
+#if 0
+    printf("tok=%c %d\n", c, c);
+    if (c == LD_TOK_NAME)
+        printf("  name=%s\n", name);
+#endif
+    return c;
+}
+
+/* interpret a subset of GNU ldscripts to handle the dummy libc.so
+   files */
+static int tcc_load_ldscript(TCCState *s1)
+{
+    char cmd[64];
+    char filename[1024];
+    int t;
+    
+    ch = file->buf_ptr[0];
+    ch = handle_eob();
+    for(;;) {
+        t = ld_next(s1, cmd, sizeof(cmd));
+        if (t == LD_TOK_EOF)
+            return 0;
+        else if (t != LD_TOK_NAME)
+            return -1;
+        if (!strcmp(cmd, "INPUT") ||
+            !strcmp(cmd, "GROUP")) {
+            t = ld_next(s1, cmd, sizeof(cmd));
+            if (t != '(')
+                expect("(");
+            t = ld_next(s1, filename, sizeof(filename));
+            for(;;) {
+                if (t == LD_TOK_EOF) {
+                    error_noabort("unexpected end of file");
+                    return -1;
+                } else if (t == ')') {
+                    break;
+                } else if (t != LD_TOK_NAME) {
+                    error_noabort("filename expected");
+                    return -1;
+                } 
+                tcc_add_file(s1, filename);
+                t = ld_next(s1, filename, sizeof(filename));
+                if (t == ',') {
+                    t = ld_next(s1, filename, sizeof(filename));
+                }
+            }
+        } else if (!strcmp(cmd, "OUTPUT_FORMAT") ||
+                   !strcmp(cmd, "TARGET")) {
+            /* ignore some commands */
+            t = ld_next(s1, cmd, sizeof(cmd));
+            if (t != '(')
+                expect("(");
+            for(;;) {
+                t = ld_next(s1, filename, sizeof(filename));
+                if (t == LD_TOK_EOF) {
+                    error_noabort("unexpected end of file");
+                    return -1;
+                } else if (t == ')') {
+                    break;
+                }
+            }
+        } else {
+            return -1;
+        }
+    }
+    return 0;
+}
+//---------------------------------------------------------------------------
+
+#ifdef TCC_TARGET_COFF
+#include "tcccoff.c"
+#endif
+
+#ifdef TCC_TARGET_PE
+#include "tccpe.c"
+#endif
+
+/* print the position in the source file of PC value 'pc' by reading
+   the stabs debug information */
+static void rt_printline(unsigned long wanted_pc)
+{
+    Stab_Sym *sym, *sym_end;
+    char func_name[128], last_func_name[128];
+    unsigned long func_addr, last_pc, pc;
+    const char *incl_files[INCLUDE_STACK_SIZE];
+    int incl_index, len, last_line_num, i;
+    const char *str, *p;
+
+    fprintf(stderr, "0x%08lx:", wanted_pc);
+
+    func_name[0] = '\0';
+    func_addr = 0;
+    incl_index = 0;
+    last_func_name[0] = '\0';
+    last_pc = 0xffffffff;
+    last_line_num = 1;
+    sym = (Stab_Sym *)stab_section->data + 1;
+    sym_end = (Stab_Sym *)(stab_section->data + stab_section->data_offset);
+    while (sym < sym_end) {
+        switch(sym->n_type) {
+            /* function start or end */
+        case N_FUN:
+            if (sym->n_strx == 0) {
+                /* we test if between last line and end of function */
+                pc = sym->n_value + func_addr;
+                if (wanted_pc >= last_pc && wanted_pc < pc)
+                    goto found;
+                func_name[0] = '\0';
+                func_addr = 0;
+            } else {
+                str = stabstr_section->data + sym->n_strx;
+                p = strchr(str, ':');
+                if (!p) {
+                    pstrcpy(func_name, sizeof(func_name), str);
+                } else {
+                    len = p - str;
+                    if (len > sizeof(func_name) - 1)
+                        len = sizeof(func_name) - 1;
+                    memcpy(func_name, str, len);
+                    func_name[len] = '\0';
+                }
+                func_addr = sym->n_value;
+            }
+            break;
+            /* line number info */
+        case N_SLINE:
+            pc = sym->n_value + func_addr;
+            if (wanted_pc >= last_pc && wanted_pc < pc)
+                goto found;
+            last_pc = pc;
+            last_line_num = sym->n_desc;
+            /* XXX: slow! */
+            strcpy(last_func_name, func_name);
+            break;
+            /* include files */
+        case N_BINCL:
+            str = stabstr_section->data + sym->n_strx;
+        add_incl:
+            if (incl_index < INCLUDE_STACK_SIZE) {
+                incl_files[incl_index++] = str;
+            }
+            break;
+        case N_EINCL:
+            if (incl_index > 1)
+                incl_index--;
+            break;
+        case N_SO:
+            if (sym->n_strx == 0) {
+                incl_index = 0; /* end of translation unit */
+            } else {
+                str = stabstr_section->data + sym->n_strx;
+                /* do not add path */
+                len = strlen(str);
+                if (len > 0 && str[len - 1] != '/')
+                    goto add_incl;
+            }
+            break;
+        }
+        sym++;
+    }
+
+    /* second pass: we try symtab symbols (no line number info) */
+    incl_index = 0;
+    {
+        Elf32_Sym *sym, *sym_end;
+        int type;
+
+        sym_end = (Elf32_Sym *)(symtab_section->data + symtab_section->data_offset);
+        for(sym = (Elf32_Sym *)symtab_section->data + 1; 
+            sym < sym_end;
+            sym++) {
+            type = ELF32_ST_TYPE(sym->st_info);
+            if (type == STT_FUNC) {
+                if (wanted_pc >= sym->st_value &&
+                    wanted_pc < sym->st_value + sym->st_size) {
+                    pstrcpy(last_func_name, sizeof(last_func_name),
+                            strtab_section->data + sym->st_name);
+                    goto found;
+                }
+            }
+        }
+    }
+    /* did not find any info: */
+    fprintf(stderr, " ???\n");
+    return;
+ found:
+    if (last_func_name[0] != '\0') {
+        fprintf(stderr, " %s()", last_func_name);
+    }
+    if (incl_index > 0) {
+        fprintf(stderr, " (%s:%d", 
+                incl_files[incl_index - 1], last_line_num);
+        for(i = incl_index - 2; i >= 0; i--)
+            fprintf(stderr, ", included from %s", incl_files[i]);
+        fprintf(stderr, ")");
+    }
+    fprintf(stderr, "\n");
+}
+
+#if !defined(WIN32) && !defined(CONFIG_TCCBOOT)
+
+#ifdef __i386__
+
+/* fix for glibc 2.1 */
+#ifndef REG_EIP
+#define REG_EIP EIP
+#define REG_EBP EBP
+#endif
+
+/* return the PC at frame level 'level'. Return non zero if not found */
+static int rt_get_caller_pc(unsigned long *paddr, 
+                            ucontext_t *uc, int level)
+{
+    unsigned long fp;
+    int i;
+
+    if (level == 0) {
+#if defined(__FreeBSD__)
+        *paddr = uc->uc_mcontext.mc_eip;
+#elif defined(__dietlibc__)
+        *paddr = uc->uc_mcontext.eip;
+#else
+        *paddr = uc->uc_mcontext.gregs[REG_EIP];
+#endif
+        return 0;
+    } else {
+#if defined(__FreeBSD__) 
+        fp = uc->uc_mcontext.mc_ebp;
+#elif defined(__dietlibc__)
+        fp = uc->uc_mcontext.ebp;
+#else
+        fp = uc->uc_mcontext.gregs[REG_EBP];
+#endif
+        for(i=1;i<level;i++) {
+            /* XXX: check address validity with program info */
+            if (fp <= 0x1000 || fp >= 0xc0000000)
+                return -1;
+            fp = ((unsigned long *)fp)[0];
+        }
+        *paddr = ((unsigned long *)fp)[1];
+        return 0;
+    }
+}
+#else
+
+#warning add arch specific rt_get_caller_pc()
+
+static int rt_get_caller_pc(unsigned long *paddr,
+                            ucontext_t *uc, int level)
+{
+    return -1;
+}
+#endif
+
+/* emit a run time error at position 'pc' */
+void rt_error(ucontext_t *uc, const char *fmt, ...)
+{
+    va_list ap;
+    unsigned long pc;
+    int i;
+
+    va_start(ap, fmt);
+    fprintf(stderr, "Runtime error: ");
+    vfprintf(stderr, fmt, ap);
+    fprintf(stderr, "\n");
+    for(i=0;i<num_callers;i++) {
+        if (rt_get_caller_pc(&pc, uc, i) < 0)
+            break;
+        if (i == 0)
+            fprintf(stderr, "at ");
+        else
+            fprintf(stderr, "by ");
+        rt_printline(pc);
+    }
+    exit(255);
+    va_end(ap);
+}
+
+/* signal handler for fatal errors */
+static void sig_error(int signum, siginfo_t *siginf, void *puc)
+{
+    ucontext_t *uc = puc;
+
+    switch(signum) {
+    case SIGFPE:
+        switch(siginf->si_code) {
+        case FPE_INTDIV:
+        case FPE_FLTDIV:
+            rt_error(uc, "division by zero");
+            break;
+        default:
+            rt_error(uc, "floating point exception");
+            break;
+        }
+        break;
+    case SIGBUS:
+    case SIGSEGV:
+        if (rt_bound_error_msg && *rt_bound_error_msg)
+            rt_error(uc, *rt_bound_error_msg);
+        else
+            rt_error(uc, "dereferencing invalid pointer");
+        break;
+    case SIGILL:
+        rt_error(uc, "illegal instruction");
+        break;
+    case SIGABRT:
+        rt_error(uc, "abort() called");
+        break;
+    default:
+        rt_error(uc, "caught signal %d", signum);
+        break;
+    }
+    exit(255);
+}
+#endif
+
+/* do all relocations (needed before using tcc_get_symbol()) */
+int tcc_relocate(TCCState *s1)
+{
+    Section *s;
+    int i;
+
+    s1->nb_errors = 0;
+    
+#ifdef TCC_TARGET_PE
+    pe_add_runtime(s1);
+#else
+    tcc_add_runtime(s1);
+#endif
+
+    relocate_common_syms();
+
+    tcc_add_linker_symbols(s1);
+
+    build_got_entries(s1);
+    
+    /* compute relocation address : section are relocated in place. We
+       also alloc the bss space */
+    for(i = 1; i < s1->nb_sections; i++) {
+        s = s1->sections[i];
+        if (s->sh_flags & SHF_ALLOC) {
+            if (s->sh_type == SHT_NOBITS)
+                s->data = tcc_mallocz(s->data_offset);
+            s->sh_addr = (unsigned long)s->data;
+        }
+    }
+
+    relocate_syms(s1, 1);
+
+    if (s1->nb_errors != 0)
+        return -1;
+
+    /* relocate each section */
+    for(i = 1; i < s1->nb_sections; i++) {
+        s = s1->sections[i];
+        if (s->reloc)
+            relocate_section(s1, s);
+    }
+    return 0;
+}
+
+/* launch the compiled program with the given arguments */
+int tcc_run(TCCState *s1, int argc, char **argv)
+{
+    int (*prog_main)(int, char **);
+
+    if (tcc_relocate(s1) < 0)
+        return -1;
+
+    prog_main = tcc_get_symbol_err(s1, "main");
+    
+    if (do_debug) {
+#if defined(WIN32) || defined(CONFIG_TCCBOOT)
+        error("debug mode currently not available for Windows");
+#else        
+        struct sigaction sigact;
+        /* install TCC signal handlers to print debug info on fatal
+           runtime errors */
+        sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
+        sigact.sa_sigaction = sig_error;
+        sigemptyset(&sigact.sa_mask);
+        sigaction(SIGFPE, &sigact, NULL);
+        sigaction(SIGILL, &sigact, NULL);
+        sigaction(SIGSEGV, &sigact, NULL);
+        sigaction(SIGBUS, &sigact, NULL);
+        sigaction(SIGABRT, &sigact, NULL);
+#endif
+    }
+
+#ifdef CONFIG_TCC_BCHECK
+    if (do_bounds_check) {
+        void (*bound_init)(void);
+
+        /* set error function */
+        rt_bound_error_msg = (void *)tcc_get_symbol_err(s1, 
+                                                        "__bound_error_msg");
+
+        /* XXX: use .init section so that it also work in binary ? */
+        bound_init = (void *)tcc_get_symbol_err(s1, "__bound_init");
+        bound_init();
+    }
+#endif
+    return (*prog_main)(argc, argv);
+}
+
+TCCState *tcc_new(void)
+{
+    const char *p, *r;
+    TCCState *s;
+    TokenSym *ts;
+    int i, c;
+
+    s = tcc_mallocz(sizeof(TCCState));
+    if (!s)
+        return NULL;
+    tcc_state = s;
+    s->output_type = TCC_OUTPUT_MEMORY;
+
+    /* init isid table */
+    for(i=0;i<256;i++)
+        isidnum_table[i] = isid(i) || isnum(i);
+
+    /* add all tokens */
+    table_ident = NULL;
+    memset(hash_ident, 0, TOK_HASH_SIZE * sizeof(TokenSym *));
+    
+    tok_ident = TOK_IDENT;
+    p = tcc_keywords;
+    while (*p) {
+        r = p;
+        for(;;) {
+            c = *r++;
+            if (c == '\0')
+                break;
+        }
+        ts = tok_alloc(p, r - p - 1);
+        p = r;
+    }
+
+    /* we add dummy defines for some special macros to speed up tests
+       and to have working defined() */
+    define_push(TOK___LINE__, MACRO_OBJ, NULL, NULL);
+    define_push(TOK___FILE__, MACRO_OBJ, NULL, NULL);
+    define_push(TOK___DATE__, MACRO_OBJ, NULL, NULL);
+    define_push(TOK___TIME__, MACRO_OBJ, NULL, NULL);
+
+    /* standard defines */
+    tcc_define_symbol(s, "__STDC__", NULL);
+#if defined(TCC_TARGET_I386)
+    tcc_define_symbol(s, "__i386__", NULL);
+#endif
+#if defined(TCC_TARGET_ARM)
+    tcc_define_symbol(s, "__ARM_ARCH_4__", NULL);
+    tcc_define_symbol(s, "__arm_elf__", NULL);
+    tcc_define_symbol(s, "__arm_elf", NULL);
+    tcc_define_symbol(s, "arm_elf", NULL);
+    tcc_define_symbol(s, "__arm__", NULL);
+    tcc_define_symbol(s, "__arm", NULL);
+    tcc_define_symbol(s, "arm", NULL);
+    tcc_define_symbol(s, "__APCS_32__", NULL);
+#endif
+#if defined(linux)
+    tcc_define_symbol(s, "__linux__", NULL);
+    tcc_define_symbol(s, "linux", NULL);
+#endif
+    /* tiny C specific defines */
+    tcc_define_symbol(s, "__TINYC__", NULL);
+
+    /* tiny C & gcc defines */
+    tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned int");
+    tcc_define_symbol(s, "__PTRDIFF_TYPE__", "int");
+    tcc_define_symbol(s, "__WCHAR_TYPE__", "int");
+    
+    /* default library paths */
+#ifdef TCC_TARGET_PE
+    {
+        char buf[1024];
+        snprintf(buf, sizeof(buf), "%s/lib", tcc_lib_path);
+        tcc_add_library_path(s, buf);
+    }
+#else
+    tcc_add_library_path(s, "/usr/local/lib");
+    tcc_add_library_path(s, "/usr/lib");
+    tcc_add_library_path(s, "/lib");
+#endif
+
+    /* no section zero */
+    dynarray_add((void ***)&s->sections, &s->nb_sections, NULL);
+
+    /* create standard sections */
+    text_section = new_section(s, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR);
+    data_section = new_section(s, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
+    bss_section = new_section(s, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE);
+
+    /* symbols are always generated for linking stage */
+    symtab_section = new_symtab(s, ".symtab", SHT_SYMTAB, 0,
+                                ".strtab",
+                                ".hashtab", SHF_PRIVATE); 
+    strtab_section = symtab_section->link;
+    
+    /* private symbol table for dynamic symbols */
+    s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE,
+                                      ".dynstrtab", 
+                                      ".dynhashtab", SHF_PRIVATE);
+    s->alacarte_link = 1;
+
+#ifdef CHAR_IS_UNSIGNED
+    s->char_is_unsigned = 1;
+#endif
+#if defined(TCC_TARGET_PE) && 0
+    /* XXX: currently the PE linker is not ready to support that */
+    s->leading_underscore = 1;
+#endif
+    return s;
+}
+
+void tcc_delete(TCCState *s1)
+{
+    int i, n;
+
+    /* free -D defines */
+    free_defines(NULL);
+
+    /* free tokens */
+    n = tok_ident - TOK_IDENT;
+    for(i = 0; i < n; i++)
+        tcc_free(table_ident[i]);
+    tcc_free(table_ident);
+
+    /* free all sections */
+
+    free_section(symtab_section->hash);
+
+    free_section(s1->dynsymtab_section->hash);
+    free_section(s1->dynsymtab_section->link);
+    free_section(s1->dynsymtab_section);
+
+    for(i = 1; i < s1->nb_sections; i++)
+        free_section(s1->sections[i]);
+    tcc_free(s1->sections);
+    
+    /* free loaded dlls array */
+    for(i = 0; i < s1->nb_loaded_dlls; i++)
+        tcc_free(s1->loaded_dlls[i]);
+    tcc_free(s1->loaded_dlls);
+
+    /* library paths */
+    for(i = 0; i < s1->nb_library_paths; i++)
+        tcc_free(s1->library_paths[i]);
+    tcc_free(s1->library_paths);
+
+    /* cached includes */
+    for(i = 0; i < s1->nb_cached_includes; i++)
+        tcc_free(s1->cached_includes[i]);
+    tcc_free(s1->cached_includes);
+
+    for(i = 0; i < s1->nb_include_paths; i++)
+        tcc_free(s1->include_paths[i]);
+    tcc_free(s1->include_paths);
+
+    for(i = 0; i < s1->nb_sysinclude_paths; i++)
+        tcc_free(s1->sysinclude_paths[i]);
+    tcc_free(s1->sysinclude_paths);
+
+    tcc_free(s1);
+}
+
+int tcc_add_include_path(TCCState *s1, const char *pathname)
+{
+    char *pathname1;
+    
+    pathname1 = tcc_strdup(pathname);
+    dynarray_add((void ***)&s1->include_paths, &s1->nb_include_paths, pathname1);
+    return 0;
+}
+
+int tcc_add_sysinclude_path(TCCState *s1, const char *pathname)
+{
+    char *pathname1;
+    
+    pathname1 = tcc_strdup(pathname);
+    dynarray_add((void ***)&s1->sysinclude_paths, &s1->nb_sysinclude_paths, pathname1);
+    return 0;
+}
+
+static int tcc_add_file_internal(TCCState *s1, const char *filename, int flags)
+{
+    const char *ext, *filename1;
+    Elf32_Ehdr ehdr;
+    int fd, ret;
+    BufferedFile *saved_file;
+
+    /* find source file type with extension */
+    filename1 = strrchr(filename, '/');
+    if (filename1)
+        filename1++;
+    else
+        filename1 = filename;
+    ext = strrchr(filename1, '.');
+    if (ext)
+        ext++;
+
+    /* open the file */
+    saved_file = file;
+    file = tcc_open(s1, filename);
+    if (!file) {
+        if (flags & AFF_PRINT_ERROR) {
+            error_noabort("file '%s' not found", filename);
+        }
+        ret = -1;
+        goto fail1;
+    }
+
+    if (!ext || !strcmp(ext, "c")) {
+        /* C file assumed */
+        ret = tcc_compile(s1);
+    } else 
+#ifdef CONFIG_TCC_ASM
+    if (!strcmp(ext, "S")) {
+        /* preprocessed assembler */
+        ret = tcc_assemble(s1, 1);
+    } else if (!strcmp(ext, "s")) {
+        /* non preprocessed assembler */
+        ret = tcc_assemble(s1, 0);
+    } else 
+#endif
+#ifdef TCC_TARGET_PE
+    if (!strcmp(ext, "def")) {
+        ret = pe_load_def_file(s1, fdopen(file->fd, "rb"));
+    } else
+#endif
+    {
+        fd = file->fd;
+        /* assume executable format: auto guess file type */
+        ret = read(fd, &ehdr, sizeof(ehdr));
+        lseek(fd, 0, SEEK_SET);
+        if (ret <= 0) {
+            error_noabort("could not read header");
+            goto fail;
+        } else if (ret != sizeof(ehdr)) {
+            goto try_load_script;
+        }
+
+        if (ehdr.e_ident[0] == ELFMAG0 &&
+            ehdr.e_ident[1] == ELFMAG1 &&
+            ehdr.e_ident[2] == ELFMAG2 &&
+            ehdr.e_ident[3] == ELFMAG3) {
+            file->line_num = 0; /* do not display line number if error */
+            if (ehdr.e_type == ET_REL) {
+                ret = tcc_load_object_file(s1, fd, 0);
+            } else if (ehdr.e_type == ET_DYN) {
+                if (s1->output_type == TCC_OUTPUT_MEMORY) {
+#ifdef TCC_TARGET_PE
+                    ret = -1;
+#else
+                    void *h;
+                    h = dlopen(filename, RTLD_GLOBAL | RTLD_LAZY);
+                    if (h)
+                        ret = 0;
+                    else
+                        ret = -1;
+#endif
+                } else {
+                    ret = tcc_load_dll(s1, fd, filename, 
+                                       (flags & AFF_REFERENCED_DLL) != 0);
+                }
+            } else {
+                error_noabort("unrecognized ELF file");
+                goto fail;
+            }
+        } else if (memcmp((char *)&ehdr, ARMAG, 8) == 0) {
+            file->line_num = 0; /* do not display line number if error */
+            ret = tcc_load_archive(s1, fd);
+        } else 
+#ifdef TCC_TARGET_COFF
+        if (*(uint16_t *)(&ehdr) == COFF_C67_MAGIC) {
+            ret = tcc_load_coff(s1, fd);
+        } else
+#endif
+        {
+            /* as GNU ld, consider it is an ld script if not recognized */
+        try_load_script:
+            ret = tcc_load_ldscript(s1);
+            if (ret < 0) {
+                error_noabort("unrecognized file type");
+                goto fail;
+            }
+        }
+    }
+ the_end:
+    tcc_close(file);
+ fail1:
+    file = saved_file;
+    return ret;
+ fail:
+    ret = -1;
+    goto the_end;
+}
+
+int tcc_add_file(TCCState *s, const char *filename)
+{
+    return tcc_add_file_internal(s, filename, AFF_PRINT_ERROR);
+}
+
+int tcc_add_library_path(TCCState *s, const char *pathname)
+{
+    char *pathname1;
+    
+    pathname1 = tcc_strdup(pathname);
+    dynarray_add((void ***)&s->library_paths, &s->nb_library_paths, pathname1);
+    return 0;
+}
+
+/* find and load a dll. Return non zero if not found */
+/* XXX: add '-rpath' option support ? */
+static int tcc_add_dll(TCCState *s, const char *filename, int flags)
+{
+    char buf[1024];
+    int i;
+
+    for(i = 0; i < s->nb_library_paths; i++) {
+        snprintf(buf, sizeof(buf), "%s/%s", 
+                 s->library_paths[i], filename);
+        if (tcc_add_file_internal(s, buf, flags) == 0)
+            return 0;
+    }
+    return -1;
+}
+
+/* the library name is the same as the argument of the '-l' option */
+int tcc_add_library(TCCState *s, const char *libraryname)
+{
+    char buf[1024];
+    int i;
+    
+    /* first we look for the dynamic library if not static linking */
+    if (!s->static_link) {
+#ifdef TCC_TARGET_PE
+        snprintf(buf, sizeof(buf), "%s.def", libraryname);
+#else
+        snprintf(buf, sizeof(buf), "lib%s.so", libraryname);
+#endif
+        if (tcc_add_dll(s, buf, 0) == 0)
+            return 0;
+    }
+
+    /* then we look for the static library */
+    for(i = 0; i < s->nb_library_paths; i++) {
+        snprintf(buf, sizeof(buf), "%s/lib%s.a", 
+                 s->library_paths[i], libraryname);
+        if (tcc_add_file_internal(s, buf, 0) == 0)
+            return 0;
+    }
+    return -1;
+}
+
+int tcc_add_symbol(TCCState *s, const char *name, unsigned long val)
+{
+    add_elf_sym(symtab_section, val, 0, 
+                ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
+                SHN_ABS, name);
+    return 0;
+}
+
+int tcc_set_output_type(TCCState *s, int output_type)
+{
+    s->output_type = output_type;
+
+    if (!s->nostdinc) {
+        char buf[1024];
+
+        /* default include paths */
+        /* XXX: reverse order needed if -isystem support */
+#ifndef TCC_TARGET_PE
+        tcc_add_sysinclude_path(s, "/usr/local/include");
+        tcc_add_sysinclude_path(s, "/usr/include");
+#endif
+        snprintf(buf, sizeof(buf), "%s/include", tcc_lib_path);
+        tcc_add_sysinclude_path(s, buf);
+#ifdef TCC_TARGET_PE
+        snprintf(buf, sizeof(buf), "%s/include/winapi", tcc_lib_path);
+        tcc_add_sysinclude_path(s, buf);
+#endif
+    }
+
+    /* if bound checking, then add corresponding sections */
+#ifdef CONFIG_TCC_BCHECK
+    if (do_bounds_check) {
+        /* define symbol */
+        tcc_define_symbol(s, "__BOUNDS_CHECKING_ON", NULL);
+        /* create bounds sections */
+        bounds_section = new_section(s, ".bounds", 
+                                     SHT_PROGBITS, SHF_ALLOC);
+        lbounds_section = new_section(s, ".lbounds", 
+                                      SHT_PROGBITS, SHF_ALLOC);
+    }
+#endif
+
+    if (s->char_is_unsigned) {
+        tcc_define_symbol(s, "__CHAR_UNSIGNED__", NULL);
+    }
+
+    /* add debug sections */
+    if (do_debug) {
+        /* stab symbols */
+        stab_section = new_section(s, ".stab", SHT_PROGBITS, 0);
+        stab_section->sh_entsize = sizeof(Stab_Sym);
+        stabstr_section = new_section(s, ".stabstr", SHT_STRTAB, 0);
+        put_elf_str(stabstr_section, "");
+        stab_section->link = stabstr_section;
+        /* put first entry */
+        put_stabs("", 0, 0, 0, 0);
+    }
+
+    /* add libc crt1/crti objects */
+#ifndef TCC_TARGET_PE
+    if ((output_type == TCC_OUTPUT_EXE || output_type == TCC_OUTPUT_DLL) &&
+        !s->nostdlib) {
+        if (output_type != TCC_OUTPUT_DLL)
+            tcc_add_file(s, CONFIG_TCC_CRT_PREFIX "/crt1.o");
+        tcc_add_file(s, CONFIG_TCC_CRT_PREFIX "/crti.o");
+    }
+#endif
+    return 0;
+}
+
+#define WD_ALL    0x0001 /* warning is activated when using -Wall */
+#define FD_INVERT 0x0002 /* invert value before storing */
+
+typedef struct FlagDef {
+    uint16_t offset;
+    uint16_t flags;
+    const char *name;
+} FlagDef;
+
+static const FlagDef warning_defs[] = {
+    { offsetof(TCCState, warn_unsupported), 0, "unsupported" },
+    { offsetof(TCCState, warn_write_strings), 0, "write-strings" },
+    { offsetof(TCCState, warn_error), 0, "error" },
+    { offsetof(TCCState, warn_implicit_function_declaration), WD_ALL,
+      "implicit-function-declaration" },
+};
+
+static int set_flag(TCCState *s, const FlagDef *flags, int nb_flags,
+                    const char *name, int value)
+{
+    int i;
+    const FlagDef *p;
+    const char *r;
+
+    r = name;
+    if (r[0] == 'n' && r[1] == 'o' && r[2] == '-') {
+        r += 3;
+        value = !value;
+    }
+    for(i = 0, p = flags; i < nb_flags; i++, p++) {
+        if (!strcmp(r, p->name))
+            goto found;
+    }
+    return -1;
+ found:
+    if (p->flags & FD_INVERT)
+        value = !value;
+    *(int *)((uint8_t *)s + p->offset) = value;
+    return 0;
+}
+
+
+/* set/reset a warning */
+int tcc_set_warning(TCCState *s, const char *warning_name, int value)
+{
+    int i;
+    const FlagDef *p;
+
+    if (!strcmp(warning_name, "all")) {
+        for(i = 0, p = warning_defs; i < countof(warning_defs); i++, p++) {
+            if (p->flags & WD_ALL)
+                *(int *)((uint8_t *)s + p->offset) = 1;
+        }
+        return 0;
+    } else {
+        return set_flag(s, warning_defs, countof(warning_defs),
+                        warning_name, value);
+    }
+}
+
+static const FlagDef flag_defs[] = {
+    { offsetof(TCCState, char_is_unsigned), 0, "unsigned-char" },
+    { offsetof(TCCState, char_is_unsigned), FD_INVERT, "signed-char" },
+    { offsetof(TCCState, nocommon), FD_INVERT, "common" },
+    { offsetof(TCCState, leading_underscore), 0, "leading-underscore" },
+};
+
+/* set/reset a flag */
+int tcc_set_flag(TCCState *s, const char *flag_name, int value)
+{
+    return set_flag(s, flag_defs, countof(flag_defs),
+                    flag_name, value);
+}
+
+#if !defined(LIBTCC)
+
+/* extract the basename of a file */
+static const char *tcc_basename(const char *name)
+{
+    const char *p;
+    p = strrchr(name, '/');
+#ifdef WIN32
+    if (!p)
+        p = strrchr(name, '\\');
+#endif    
+    if (!p)
+        p = name;
+    else 
+        p++;
+    return p;
+}
+
+static int64_t getclock_us(void)
+{
+#ifdef WIN32
+    struct _timeb tb;
+    _ftime(&tb);
+    return (tb.time * 1000LL + tb.millitm) * 1000LL;
+#else
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    return tv.tv_sec * 1000000LL + tv.tv_usec;
+#endif
+}
+
+void help(void)
+{
+    printf("tcc version " TCC_VERSION " - Tiny C Compiler - Copyright (C) 2001-2005 Fabrice Bellard\n"
+           "usage: tcc [-v] [-c] [-o outfile] [-Bdir] [-bench] [-Idir] [-Dsym[=val]] [-Usym]\n"
+           "           [-Wwarn] [-g] [-b] [-bt N] [-Ldir] [-llib] [-shared] [-static]\n"
+           "           [infile1 infile2...] [-run infile args...]\n"
+           "\n"
+           "General options:\n"
+           "  -v          display current version\n"
+           "  -c          compile only - generate an object file\n"
+           "  -o outfile  set output filename\n"
+           "  -Bdir       set tcc internal library path\n"
+           "  -bench      output compilation statistics\n"
+          "  -run        run compiled source\n"
+           "  -fflag      set or reset (with 'no-' prefix) 'flag' (see man page)\n"
+           "  -Wwarning   set or reset (with 'no-' prefix) 'warning' (see man page)\n"
+           "  -w          disable all warnings\n"
+           "Preprocessor options:\n"
+           "  -Idir       add include path 'dir'\n"
+           "  -Dsym[=val] define 'sym' with value 'val'\n"
+           "  -Usym       undefine 'sym'\n"
+           "Linker options:\n"
+           "  -Ldir       add library path 'dir'\n"
+           "  -llib       link with dynamic or static library 'lib'\n"
+           "  -shared     generate a shared library\n"
+           "  -static     static linking\n"
+           "  -rdynamic   export all global symbols to dynamic linker\n"
+           "  -r          relocatable output\n"
+           "Debugger options:\n"
+           "  -g          generate runtime debug info\n"
+#ifdef CONFIG_TCC_BCHECK
+           "  -b          compile with built-in memory and bounds checker (implies -g)\n"
+#endif
+           "  -bt N       show N callers in stack traces\n"
+           );
+}
+
+#define TCC_OPTION_HAS_ARG 0x0001
+#define TCC_OPTION_NOSEP   0x0002 /* cannot have space before option and arg */
+
+typedef struct TCCOption {
+    const char *name;
+    uint16_t index;
+    uint16_t flags;
+} TCCOption;
+
+enum {
+    TCC_OPTION_HELP,
+    TCC_OPTION_I,
+    TCC_OPTION_D,
+    TCC_OPTION_U,
+    TCC_OPTION_L,
+    TCC_OPTION_B,
+    TCC_OPTION_l,
+    TCC_OPTION_bench,
+    TCC_OPTION_bt,
+    TCC_OPTION_b,
+    TCC_OPTION_g,
+    TCC_OPTION_c,
+    TCC_OPTION_static,
+    TCC_OPTION_shared,
+    TCC_OPTION_o,
+    TCC_OPTION_r,
+    TCC_OPTION_Wl,
+    TCC_OPTION_W,
+    TCC_OPTION_O,
+    TCC_OPTION_m,
+    TCC_OPTION_f,
+    TCC_OPTION_nostdinc,
+    TCC_OPTION_nostdlib,
+    TCC_OPTION_print_search_dirs,
+    TCC_OPTION_rdynamic,
+    TCC_OPTION_run,
+    TCC_OPTION_v,
+    TCC_OPTION_w,
+    TCC_OPTION_pipe,
+};
+
+static const TCCOption tcc_options[] = {
+    { "h", TCC_OPTION_HELP, 0 },
+    { "?", TCC_OPTION_HELP, 0 },
+    { "I", TCC_OPTION_I, TCC_OPTION_HAS_ARG },
+    { "D", TCC_OPTION_D, TCC_OPTION_HAS_ARG },
+    { "U", TCC_OPTION_U, TCC_OPTION_HAS_ARG },
+    { "L", TCC_OPTION_L, TCC_OPTION_HAS_ARG },
+    { "B", TCC_OPTION_B, TCC_OPTION_HAS_ARG },
+    { "l", TCC_OPTION_l, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
+    { "bench", TCC_OPTION_bench, 0 },
+    { "bt", TCC_OPTION_bt, TCC_OPTION_HAS_ARG },
+#ifdef CONFIG_TCC_BCHECK
+    { "b", TCC_OPTION_b, 0 },
+#endif
+    { "g", TCC_OPTION_g, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
+    { "c", TCC_OPTION_c, 0 },
+    { "static", TCC_OPTION_static, 0 },
+    { "shared", TCC_OPTION_shared, 0 },
+    { "o", TCC_OPTION_o, TCC_OPTION_HAS_ARG },
+    { "run", TCC_OPTION_run, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
+    { "rdynamic", TCC_OPTION_rdynamic, 0 },
+    { "r", TCC_OPTION_r, 0 },
+    { "Wl,", TCC_OPTION_Wl, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
+    { "W", TCC_OPTION_W, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
+    { "O", TCC_OPTION_O, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
+    { "m", TCC_OPTION_m, TCC_OPTION_HAS_ARG },
+    { "f", TCC_OPTION_f, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
+    { "nostdinc", TCC_OPTION_nostdinc, 0 },
+    { "nostdlib", TCC_OPTION_nostdlib, 0 },
+    { "print-search-dirs", TCC_OPTION_print_search_dirs, 0 }, 
+    { "v", TCC_OPTION_v, 0 },
+    { "w", TCC_OPTION_w, 0 },
+    { "pipe", TCC_OPTION_pipe, 0},
+    { NULL },
+};
+
+/* convert 'str' into an array of space separated strings */
+static int expand_args(char ***pargv, const char *str)
+{
+    const char *s1;
+    char **argv, *arg;
+    int argc, len;
+
+    argc = 0;
+    argv = NULL;
+    for(;;) {
+        while (is_space(*str))
+            str++;
+        if (*str == '\0')
+            break;
+        s1 = str;
+        while (*str != '\0' && !is_space(*str))
+            str++;
+        len = str - s1;
+        arg = tcc_malloc(len + 1);
+        memcpy(arg, s1, len);
+        arg[len] = '\0';
+        dynarray_add((void ***)&argv, &argc, arg);
+    }
+    *pargv = argv;
+    return argc;
+}
+
+static char **files;
+static int nb_files, nb_libraries;
+static int multiple_files;
+static int print_search_dirs;
+static int output_type;
+static int reloc_output;
+static const char *outfile;
+
+int parse_args(TCCState *s, int argc, char **argv)
+{
+    int optind;
+    const TCCOption *popt;
+    const char *optarg, *p1, *r1;
+    char *r;
+
+    optind = 0;
+    while (1) {
+        if (optind >= argc) {
+            if (nb_files == 0 && !print_search_dirs)
+                goto show_help;
+            else
+                break;
+        }
+        r = argv[optind++];
+        if (r[0] != '-') {
+            /* add a new file */
+            dynarray_add((void ***)&files, &nb_files, r);
+            if (!multiple_files) {
+                optind--;
+                /* argv[0] will be this file */
+                break;
+            }
+        } else {
+            /* find option in table (match only the first chars */
+            popt = tcc_options;
+            for(;;) {
+                p1 = popt->name;
+                if (p1 == NULL)
+                    error("invalid option -- '%s'", r);
+                r1 = r + 1;
+                for(;;) {
+                    if (*p1 == '\0')
+                        goto option_found;
+                    if (*r1 != *p1)
+                        break;
+                    p1++;
+                    r1++;
+                }
+                popt++;
+            }
+        option_found:
+            if (popt->flags & TCC_OPTION_HAS_ARG) {
+                if (*r1 != '\0' || (popt->flags & TCC_OPTION_NOSEP)) {
+                    optarg = r1;
+                } else {
+                    if (optind >= argc)
+                        error("argument to '%s' is missing", r);
+                    optarg = argv[optind++];
+                }
+            } else {
+                if (*r1 != '\0')
+                    goto show_help;
+                optarg = NULL;
+            }
+                
+            switch(popt->index) {
+            case TCC_OPTION_HELP:
+            show_help:
+                help();
+                exit(1);
+            case TCC_OPTION_I:
+                if (tcc_add_include_path(s, optarg) < 0)
+                    error("too many include paths");
+                break;
+            case TCC_OPTION_D:
+                {
+                    char *sym, *value;
+                    sym = (char *)optarg;
+                    value = strchr(sym, '=');
+                    if (value) {
+                        *value = '\0';
+                        value++;
+                    }
+                    tcc_define_symbol(s, sym, value);
+                }
+                break;
+            case TCC_OPTION_U:
+                tcc_undefine_symbol(s, optarg);
+                break;
+            case TCC_OPTION_L:
+                tcc_add_library_path(s, optarg);
+                break;
+            case TCC_OPTION_B:
+                /* set tcc utilities path (mainly for tcc development) */
+                tcc_lib_path = optarg;
+                break;
+            case TCC_OPTION_l:
+                dynarray_add((void ***)&files, &nb_files, r);
+                nb_libraries++;
+                break;
+            case TCC_OPTION_bench:
+                do_bench = 1;
+                break;
+            case TCC_OPTION_bt:
+                num_callers = atoi(optarg);
+                break;
+#ifdef CONFIG_TCC_BCHECK
+            case TCC_OPTION_b:
+                do_bounds_check = 1;
+                do_debug = 1;
+                break;
+#endif
+            case TCC_OPTION_g:
+                do_debug = 1;
+                break;
+            case TCC_OPTION_c:
+                multiple_files = 1;
+                output_type = TCC_OUTPUT_OBJ;
+                break;
+            case TCC_OPTION_static:
+                s->static_link = 1;
+                break;
+            case TCC_OPTION_shared:
+                output_type = TCC_OUTPUT_DLL;
+                break;
+            case TCC_OPTION_o:
+                multiple_files = 1;
+                outfile = optarg;
+                break;
+            case TCC_OPTION_r:
+                /* generate a .o merging several output files */
+                reloc_output = 1;
+                output_type = TCC_OUTPUT_OBJ;
+                break;
+            case TCC_OPTION_nostdinc:
+                s->nostdinc = 1;
+                break;
+            case TCC_OPTION_nostdlib:
+                s->nostdlib = 1;
+                break;
+            case TCC_OPTION_print_search_dirs:
+                print_search_dirs = 1;
+                break;
+            case TCC_OPTION_run:
+                {
+                    int argc1;
+                    char **argv1;
+                    argc1 = expand_args(&argv1, optarg);
+                    if (argc1 > 0) {
+                        parse_args(s, argc1, argv1);
+                    }
+                    multiple_files = 0;
+                    output_type = TCC_OUTPUT_MEMORY;
+                }
+                break;
+            case TCC_OPTION_v:
+                printf("tcc version %s\n", TCC_VERSION);
+                exit(0);
+            case TCC_OPTION_f:
+                if (tcc_set_flag(s, optarg, 1) < 0 && s->warn_unsupported)
+                    goto unsupported_option;
+                break;
+            case TCC_OPTION_W:
+                if (tcc_set_warning(s, optarg, 1) < 0 && 
+                    s->warn_unsupported)
+                    goto unsupported_option;
+                break;
+            case TCC_OPTION_w:
+                s->warn_none = 1;
+                break;
+            case TCC_OPTION_rdynamic:
+                s->rdynamic = 1;
+                break;
+            case TCC_OPTION_Wl:
+                {
+                    const char *p;
+                    if (strstart(optarg, "-Ttext,", &p)) {
+                        s->text_addr = strtoul(p, NULL, 16);
+                        s->has_text_addr = 1;
+                    } else if (strstart(optarg, "--oformat,", &p)) {
+                        if (strstart(p, "elf32-", NULL)) {
+                            s->output_format = TCC_OUTPUT_FORMAT_ELF;
+                        } else if (!strcmp(p, "binary")) {
+                            s->output_format = TCC_OUTPUT_FORMAT_BINARY;
+                        } else
+#ifdef TCC_TARGET_COFF
+                        if (!strcmp(p, "coff")) {
+                            s->output_format = TCC_OUTPUT_FORMAT_COFF;
+                        } else
+#endif
+                        {
+                            error("target %s not found", p);
+                        }
+                    } else {
+                        error("unsupported linker option '%s'", optarg);
+                    }
+                }
+                break;
+            default:
+                if (s->warn_unsupported) {
+                unsupported_option:
+                    warning("unsupported option '%s'", r);
+                }
+                break;
+            }
+        }
+    }
+    return optind;
+}
+
+// njn: renamed main() as main2() in order to repeat it multiple times.
+int main2(int argc, char **argv)
+{
+    int i;
+    TCCState *s;
+    int nb_objfiles, ret, optind;
+    char objfilename[1024];
+    int64_t start_time = 0;
+
+#ifdef WIN32
+    /* on win32, we suppose the lib and includes are at the location
+       of 'tcc.exe' */
+    {
+        static char path[1024];
+        char *p, *d;
+        
+        GetModuleFileNameA(NULL, path, sizeof path);
+        p = d = strlwr(path);
+        while (*d)
+        {
+            if (*d == '\\') *d = '/', p = d;
+            ++d;
+        }
+        *p = '\0';
+        tcc_lib_path = path;
+    }
+#endif
+
+    s = tcc_new();
+    output_type = TCC_OUTPUT_EXE;
+    outfile = NULL;
+    multiple_files = 1;
+    files = NULL;
+    nb_files = 0;
+    nb_libraries = 0;
+    reloc_output = 0;
+    print_search_dirs = 0;
+
+    optind = parse_args(s, argc - 1, argv + 1) + 1;
+
+    if (print_search_dirs) {
+        /* enough for Linux kernel */
+        printf("install: %s/\n", tcc_lib_path);
+        return 0;
+    }
+
+    nb_objfiles = nb_files - nb_libraries;
+
+    /* if outfile provided without other options, we output an
+       executable */
+    if (outfile && output_type == TCC_OUTPUT_MEMORY)
+        output_type = TCC_OUTPUT_EXE;
+
+    /* check -c consistency : only single file handled. XXX: checks file type */
+    if (output_type == TCC_OUTPUT_OBJ && !reloc_output) {
+        /* accepts only a single input file */
+        if (nb_objfiles != 1)
+            error("cannot specify multiple files with -c");
+        if (nb_libraries != 0)
+            error("cannot specify libraries with -c");
+    }
+    
+    if (output_type != TCC_OUTPUT_MEMORY) {
+        if (!outfile) {
+    /* compute default outfile name */
+            pstrcpy(objfilename, sizeof(objfilename) - 1, 
+                    /* strip path */
+                    tcc_basename(files[0]));
+#ifdef TCC_TARGET_PE
+            pe_guess_outfile(objfilename, output_type);
+#else
+            if (output_type == TCC_OUTPUT_OBJ && !reloc_output) {
+                char *ext = strrchr(objfilename, '.');
+            if (!ext)
+                goto default_outfile;
+                /* add .o extension */
+            strcpy(ext + 1, "o");
+        } else {
+        default_outfile:
+            pstrcpy(objfilename, sizeof(objfilename), "a.out");
+        }
+#endif
+        outfile = objfilename;
+        }
+    }
+
+    if (do_bench) {
+        start_time = getclock_us();
+    }
+
+    tcc_set_output_type(s, output_type);
+
+    /* compile or add each files or library */
+    for(i = 0;i < nb_files; i++) {
+        const char *filename;
+
+        filename = files[i];
+        if (filename[0] == '-') {
+            if (tcc_add_library(s, filename + 2) < 0)
+                error("cannot find %s", filename);
+        } else {
+            if (tcc_add_file(s, filename) < 0) {
+                ret = 1;
+                goto the_end;
+            }
+        }
+    }
+
+    /* free all files */
+    tcc_free(files);
+
+    if (do_bench) {
+        double total_time;
+        total_time = (double)(getclock_us() - start_time) / 1000000.0;
+        if (total_time < 0.001)
+            total_time = 0.001;
+        if (total_bytes < 1)
+            total_bytes = 1;
+        printf("%d idents, %d lines, %d bytes, %0.3f s, %d lines/s, %0.1f MB/s\n", 
+               tok_ident - TOK_IDENT, total_lines, total_bytes,
+               total_time, (int)(total_lines / total_time), 
+               total_bytes / total_time / 1000000.0); 
+    }
+
+    if (s->output_type == TCC_OUTPUT_MEMORY) {
+        ret = tcc_run(s, argc - optind, argv + optind);
+    } else
+#ifdef TCC_TARGET_PE
+    if (s->output_type != TCC_OUTPUT_OBJ) {
+        ret = tcc_output_pe(s, outfile);
+    } else
+#endif
+    {
+        tcc_output_file(s, outfile);
+        ret = 0;
+    }
+ the_end:
+    /* XXX: cannot do it with bound checking because of the malloc hooks */
+    if (!do_bounds_check)
+        tcc_delete(s);
+
+#ifdef MEM_DEBUG
+    if (do_bench) {
+        printf("memory: %d bytes, max = %d bytes\n", mem_cur_size, mem_max_size);
+    }
+#endif
+    return ret;
+}
+
+// njn: created this wrapper main() function to execute compilation multiple
+// times.  TinyCC is fast!
+// Nb: we get a link error, and TinyCC would normally return non-zero.  But
+// the link error is not a problem for benchmarking purposes, so we return
+// zero here (as required by vg_perf).
+int main(int argc, char **argv)
+{
+   #define REPS   10
+   int i;
+   for (i = 0; i < REPS; i++) {
+      main2(argc, argv);
+   }
+   return 0;
+}
+
+#endif
+
+// njn: copied these in from libtcc1.c to avoid link errors when libtcc1.a
+// is not present.
+unsigned short __tcc_fpu_control = 0x137f;
+unsigned short __tcc_int_fpu_control = 0x137f | 0x0c00;
+long long __shldi3(long long a, int b)
+{
+#ifdef __TINYC__ 
+    DWunion u;
+    u.ll = a;
+    if (b >= 32) {
+        u.s.high = (unsigned)u.s.low << (b - 32);
+        u.s.low = 0;
+    } else if (b != 0) {
+        u.s.high = ((unsigned)u.s.high << b) | (u.s.low >> (32 - b));
+        u.s.low = (unsigned)u.s.low << b;
+    }
+    return u.ll;
+#else
+    return a << b;
+#endif
+}
+
+
+
diff --git a/perf/tinycc.vgperf b/perf/tinycc.vgperf
new file mode 100644 (file)
index 0000000..83b5721
--- /dev/null
@@ -0,0 +1,3 @@
+prog: tinycc
+args: tinycc.c -I. -ldl
+tools: none memcheck