From 7a51d5d5497c8978fabc2e5a83d9163d885f9c3f Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 22 Aug 2014 21:20:57 -0700 Subject: [PATCH 1/6] Remove unused variables. --- sourcepawn/compiler/sc1.cpp | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/sourcepawn/compiler/sc1.cpp b/sourcepawn/compiler/sc1.cpp index 4baca471..bd543af3 100644 --- a/sourcepawn/compiler/sc1.cpp +++ b/sourcepawn/compiler/sc1.cpp @@ -2069,8 +2069,6 @@ static void declglb(declinfo_t *decl,int fpublic,int fstatic,int fstock) static void declloc(int tokid) { symbol *sym; - cell val; - char *str; value lval = {0}; int cur_lit=0; int staging_start; @@ -2817,7 +2815,7 @@ static void decl_const(int vclass) char constname[sNAMEMAX+1]; cell val; token_t tok; - int tag,exprtag; + int exprtag; int symbolline; symbol *sym; @@ -2895,7 +2893,6 @@ static void declstruct(void) char *str; int tok; pstruct_t *pstruct; - int size; /* get the explicit tag (required!) */ tok = lex(&val,&str); @@ -3777,7 +3774,6 @@ methodmap_method_t *parse_method(methodmap_t *map) typeinfo_t type; memset(&type, 0, sizeof(type)); - token_t tok; if (matchtoken('~')) { // We got something like "public ~Blah = X" is_bind = TRUE; @@ -5042,9 +5038,8 @@ static cell fix_char_size(declinfo_t *decl) static symbol *funcstub(int tokid, declinfo_t *decl, const int *thistag) { - int tok; char *str; - cell val,size; + cell val; symbol *sym; int fnative = (tokid == tNATIVE || tokid == tMETHODMAP); int fpublic = (tokid == tPUBLIC); @@ -5148,10 +5143,9 @@ static symbol *funcstub(int tokid, declinfo_t *decl, const int *thistag) static int newfunc(declinfo_t *decl, const int *thistag, int fpublic, int fstatic, int stock, symbol **symp) { symbol *sym; - int argcnt,tok,funcline; + int argcnt,funcline; int opererror; - char *str; - cell val,cidx,glbdecl; + cell cidx,glbdecl; short filenum; assert(litidx==0); /* literal queue should be empty */ @@ -5422,7 +5416,7 @@ static int argcompare(arginfo *a1,arginfo *a2) static int declargs(symbol *sym, int chkshadow, const int *thistag) { char *ptr; - int argcnt,oldargcnt,tok; + int argcnt,oldargcnt; arginfo arg, *arglist; char name[sNAMEMAX+1]; int fpublic; From c4056aea5dcf5800f47c9e07bd85eb99bbecf6e9 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sat, 23 Aug 2014 01:57:13 -0700 Subject: [PATCH 2/6] Rewrite the assembly pipeline. This patch uses SmxBuilder from spcomp2 to replace the old assemble() pipeline. Instead of generating into an old AMX structure, and then decoding that into SMX, we now directly generate into SMX. This greatly simplifies code generation and smx building. --- public/amtl/am-utility.h | 19 + sourcepawn/compiler/AMBuilder | 6 +- sourcepawn/compiler/libpawnc.cpp | 41 +- sourcepawn/compiler/memory-buffer.h | 94 +++ sourcepawn/compiler/pawncc.cpp | 542 +------------ sourcepawn/compiler/sc.h | 9 +- sourcepawn/compiler/sc1.cpp | 51 +- sourcepawn/compiler/sc6.cpp | 1064 +++++++++---------------- sourcepawn/compiler/smx-builder.cpp | 111 +++ sourcepawn/compiler/smx-builder.h | 180 +++++ sourcepawn/compiler/sp_file.cpp | 213 ----- sourcepawn/compiler/sp_file.h | 81 -- sourcepawn/compiler/sp_file_headers.h | 291 ------- sourcepawn/compiler/sp_symhash.cpp | 1 - sourcepawn/compiler/string-pool.h | 139 ++++ sourcepawn/include/smx/smx-headers.h | 41 +- sourcepawn/jit/Makefile.shell | 2 +- 17 files changed, 961 insertions(+), 1924 deletions(-) create mode 100644 sourcepawn/compiler/memory-buffer.h create mode 100644 sourcepawn/compiler/smx-builder.cpp create mode 100644 sourcepawn/compiler/smx-builder.h delete mode 100644 sourcepawn/compiler/sp_file.cpp delete mode 100644 sourcepawn/compiler/sp_file.h delete mode 100644 sourcepawn/compiler/sp_file_headers.h create mode 100644 sourcepawn/compiler/string-pool.h diff --git a/public/amtl/am-utility.h b/public/amtl/am-utility.h index 474c0052..41f69dc7 100644 --- a/public/amtl/am-utility.h +++ b/public/amtl/am-utility.h @@ -339,6 +339,25 @@ class StorageBuffer }; }; +template +class SaveAndSet +{ + public: + SaveAndSet(T *location, const T &value) + : location_(location), + old_(*location) + { + *location_ = value; + } + ~SaveAndSet() { + *location_ = old_; + } + + private: + T *location_; + T old_; +}; + #if defined(_MSC_VER) # define KE_SIZET_FMT "%Iu" #elif defined(__GNUC__) diff --git a/sourcepawn/compiler/AMBuilder b/sourcepawn/compiler/AMBuilder index 4d13ec91..b716d6bd 100644 --- a/sourcepawn/compiler/AMBuilder +++ b/sourcepawn/compiler/AMBuilder @@ -45,6 +45,7 @@ compiler.includes += [ os.path.join(builder.sourcePath, 'public', 'amtl'), os.path.join(builder.sourcePath, 'public', 'sourcepawn'), os.path.join(builder.sourcePath, 'sourcepawn', 'compiler'), + os.path.join(builder.sourcePath, 'sourcepawn', 'include'), os.path.join(builder.buildPath, 'includes'), os.path.join(builder.buildPath, builder.buildFolder), ] @@ -54,7 +55,8 @@ if compiler.cc.behavior == 'gcc': compiler.cflags += ['-Wno-format'] compiler.c_only_flags += ['-std=c99'] if builder.target_platform == 'linux': - compiler.postlink += ['-lgcc', '-lm'] + compiler.postlink += ['-lm'] + compiler.postlink += ['-lstdc++'] elif compiler.cc.behavior == 'msvc': compiler.linkflags.remove('/SUBSYSTEM:WINDOWS') compiler.linkflags.append('/SUBSYSTEM:CONSOLE') @@ -95,7 +97,7 @@ binary.sources += [ 'scstate.cpp', 'sctracker.cpp', 'scvars.cpp', - 'sp_file.cpp', + 'smx-builder.cpp', 'zlib/adler32.c', 'zlib/compress.c', 'zlib/crc32.c', diff --git a/sourcepawn/compiler/libpawnc.cpp b/sourcepawn/compiler/libpawnc.cpp index f99086d9..76a8cca2 100644 --- a/sourcepawn/compiler/libpawnc.cpp +++ b/sourcepawn/compiler/libpawnc.cpp @@ -1,4 +1,4 @@ -// vim: set sts=8 ts=4 sw=4 tw=99 noet: +// vim: set sts=8 ts=2 sw=2 tw=99 noet: /* LIBPAWNC.C * * A "glue file" for building the Pawn compiler as a DLL or shared library. @@ -341,42 +341,3 @@ char *pc_readasm(void *handle, char *string, int maxchars) { return mfgets((MEMFILE*)handle,string,maxchars); } - -extern memfile_t *bin_file; - -/* Should return a pointer, which is used as a "magic cookie" to all I/O - * functions; return NULL for failure. - */ -void *pc_openbin(char *filename) -{ - return memfile_creat(filename, 1); -} - -void pc_closebin(void *handle,int deletefile) -{ - if (deletefile) { - memfile_destroy((memfile_t *)handle); - bin_file = NULL; - } else { - bin_file = (memfile_t *)handle; - } -} - -/* pc_resetbin() - * Can seek to any location in the file. - * The offset is always from the start of the file. - */ -void pc_resetbin(void *handle,long offset) -{ - memfile_seek((memfile_t *)handle, offset); -} - -int pc_writebin(void *handle,void *buffer,int size) -{ - return memfile_write((memfile_t *)handle, buffer, size); -} - -long pc_lengthbin(void *handle) -{ - return memfile_tell((memfile_t *)handle); -} diff --git a/sourcepawn/compiler/memory-buffer.h b/sourcepawn/compiler/memory-buffer.h new file mode 100644 index 00000000..3800f921 --- /dev/null +++ b/sourcepawn/compiler/memory-buffer.h @@ -0,0 +1,94 @@ +// vim: set sts=2 ts=8 sw=2 tw=99 et: +// +// Copyright (C) 2012-2014 AlliedModders LLC, David Anderson +// +// This file is part of SourcePawn. +// +// SourcePawn is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with +// SourcePawn. If not, see http://www.gnu.org/licenses/. +#ifndef _include_sp_memory_buffer_h_ +#define _include_sp_memory_buffer_h_ + +#include +#include +#include "smx-builder.h" + +class MemoryBuffer : public ke::ISmxBuffer +{ + static const size_t kDefaultSize = 4096; + + public: + MemoryBuffer() { + buffer_ = (uint8_t *)calloc(kDefaultSize, 1); + pos_ = buffer_; + end_ = buffer_ + kDefaultSize; + } + ~MemoryBuffer() { + free(buffer_); + } + + bool write(const void *bytes, size_t len) KE_OVERRIDE { + if (pos_ + len > end_) + grow(len); + memcpy(pos_, bytes, len); + pos_ += len; + return true; + } + + size_t pos() const KE_OVERRIDE { + return pos_ - buffer_; + } + + uint8_t *bytes() const { + return buffer_; + } + size_t size() const { + return pos(); + } + void rewind(size_t newpos) { + assert(newpos < pos()); + pos_ = buffer_ + newpos; + } + + private: + void grow(size_t len) { + if (!ke::IsUintPtrAddSafe(pos(), len)) { + fprintf(stderr, "Allocation overflow!\n"); + abort(); + } + + size_t new_maxsize = end_ - buffer_; + while (pos() + len > new_maxsize) { + if (!ke::IsUintPtrMultiplySafe(new_maxsize, 2)) { + fprintf(stderr, "Allocation overflow!\n"); + abort(); + } + new_maxsize *= 2; + } + + uint8_t *newbuffer = (uint8_t *)realloc(buffer_, new_maxsize); + if (!newbuffer) { + fprintf(stderr, "Out of memory!\n"); + abort(); + } + pos_ = newbuffer + (pos_ - buffer_); + end_ = newbuffer + new_maxsize; + buffer_ = newbuffer; + } + + private: + uint8_t *buffer_; + uint8_t *pos_; + uint8_t *end_; +}; + +#endif // _include_sp_memory_buffer_h_ diff --git a/sourcepawn/compiler/pawncc.cpp b/sourcepawn/compiler/pawncc.cpp index 10ceaa96..d98bcd75 100644 --- a/sourcepawn/compiler/pawncc.cpp +++ b/sourcepawn/compiler/pawncc.cpp @@ -3,557 +3,19 @@ #include #include #include "memfile.h" -#include "sp_file.h" -#include "amx.h" -#include "amxdbg.h" #include "osdefs.h" -#include "zlib/zlib.h" #if defined LINUX || defined DARWIN #include #elif defined WIN32 #include #endif - -enum FileSections -{ - FS_Code, /* required */ - FS_Data, /* required */ - FS_Publics, - FS_Pubvars, - FS_Natives, - FS_Nametable, /* required */ - FS_DbgFile, - FS_DbgSymbol, - FS_DbgLine, - FS_DbgTags, - FS_DbgNatives, - FS_DbgAutomaton, - FS_DbgState, - FS_DbgStrings, - FS_DbgInfo, - FS_Tags, - /* --- */ - FS_Number, -}; - -int pc_printf(const char *message,...); -int pc_compile(int argc, char **argv); -void sfwrite(const void *buf, size_t size, size_t count, sp_file_t *spf); - -memfile_t *bin_file = NULL; -jmp_buf brkout; - -#define sARGS_MAX 32 /* number of arguments a function can have, max */ -#define sDIMEN_MAX 4 /* maximum number of array dimensions */ - -typedef struct t_arg_s -{ - uint8_t ident; - int16_t tag; - char *name; - uint16_t dimcount; - sp_fdbg_arraydim_t dims[sDIMEN_MAX]; -} t_arg; - -typedef struct t_native_s -{ - char *name; - int16_t ret_tag; - uint16_t num_args; - t_arg args[sARGS_MAX]; -} t_native; - -t_native *native_list = NULL; +#include "sc.h" int main(int argc, char *argv[]) { - if (pc_compile(argc,argv) != 0) - return 1; - - AMX_HEADER *hdr; - AMX_DBG_HDR *dbg = NULL; - int err; - uint32_t i; - sp_file_t *spf; - memfile_t *dbgtab = NULL; //dbgcrab - unsigned char *dbgptr = NULL; - uint32_t sections[FS_Number] = {1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0}; - FILE *fp; - - if (bin_file == NULL) - return 0; - - hdr = (AMX_HEADER *)bin_file->base; - - if ((spf=spfw_create(bin_file->name, NULL)) == NULL) { - pc_printf("Error creating binary file!\n"); - memfile_destroy(bin_file); - return 0; - } - - if ((err=setjmp(brkout))!=0) - goto write_error; - - spfw_add_section(spf, ".code"); - spfw_add_section(spf, ".data"); - - sections[FS_Publics] = (hdr->natives - hdr->publics) / hdr->defsize; - if (sections[FS_Publics]) - spfw_add_section(spf, ".publics"); - sections[FS_Pubvars] = (hdr->tags - hdr->pubvars) / hdr->defsize; - if (sections[FS_Pubvars]) - spfw_add_section(spf, ".pubvars"); - sections[FS_Natives] = (hdr->libraries - hdr->natives) / hdr->defsize; - if (sections[FS_Natives]) - spfw_add_section(spf, ".natives"); - sections[FS_Tags] = (hdr->nametable - hdr->tags) / hdr->defsize; - if (sections[FS_Tags]) - spfw_add_section(spf, ".tags"); - - spfw_add_section(spf, ".names"); - - if (hdr->flags & AMX_FLAG_DEBUG) { - dbg = (AMX_DBG_HDR *)((unsigned char *)hdr + hdr->size); - if (dbg->magic != AMX_DBG_MAGIC) { - pc_printf("Error reading AMX_DBG_HDR, debug data will not be written."); - } else { - dbgtab = memfile_creat("", 512); - dbgptr = (unsigned char *)dbg + sizeof(AMX_DBG_HDR); - if ((sections[FS_DbgNatives] = sections[FS_Natives]) > 0) - spfw_add_section(spf, ".dbg.natives"); - if (dbg->files) { - spfw_add_section(spf, ".dbg.files"); - sections[FS_DbgFile] = dbg->files; - } - if (dbg->lines) { - spfw_add_section(spf, ".dbg.lines"); - sections[FS_DbgLine] = dbg->lines; - } - if (dbg->symbols) { - spfw_add_section(spf, ".dbg.symbols"); - sections[FS_DbgSymbol] = dbg->symbols; - } - sections[FS_DbgInfo] = 1; - sections[FS_DbgStrings] = 1; - spfw_add_section(spf, ".dbg.info"); - spfw_add_section(spf, ".dbg.strings"); - } - } - - spfw_finalize_header(spf); - - /** - * Begin writing each of our known tables out - */ - - if (sections[FS_Code]) { - sp_file_code_t cod; - unsigned char *cbase; - - cod.cellsize = sizeof(cell); - - cod.codesize = hdr->dat - hdr->cod; - cod.codeversion = hdr->amx_version; - cod.flags = 0; - if (hdr->flags & AMX_FLAG_DEBUG) - { - cod.flags |= SP_FLAG_DEBUG; - } - cod.code = sizeof(cod); - cod.main = hdr->cip; - - /* write the code */ - cbase = (unsigned char *)hdr + hdr->cod; - sfwrite(&cod, sizeof(cod), 1, spf); - sfwrite(cbase, cod.codesize, 1, spf); - - spfw_next_section(spf); - } - - if (sections[FS_Data]) { - sp_file_data_t dat; - unsigned char *dbase = (unsigned char *)hdr + hdr->dat; - - dat.datasize = hdr->hea - hdr->dat; - dat.memsize = hdr->stp; - dat.data = sizeof(dat); - - /* write header */ - sfwrite(&dat, sizeof(dat), 1, spf); - - if (dat.datasize) { - /* write data */ - sfwrite(dbase, dat.datasize, 1, spf); - } - - spfw_next_section(spf); - } - - if (sections[FS_Publics]) { - sp_file_publics_t *pbtbl; - AMX_FUNCSTUBNT *stub; - unsigned char *stubptr; - uint32_t publics = sections[FS_Publics]; - - pbtbl = (sp_file_publics_t *)malloc(sizeof(sp_file_publics_t) * publics); - stubptr = (unsigned char *)hdr + hdr->publics; - - for (i=0; iaddress; - pbtbl[i].name = stub->nameofs - (hdr->nametable + sizeof(uint16_t)); - - stubptr += hdr->defsize; - } - if (publics) - sfwrite(pbtbl, sizeof(sp_file_publics_t), publics, spf); - free(pbtbl); - - spfw_next_section(spf); - } - - if (sections[FS_Pubvars]) { - sp_file_pubvars_t *pbvars; - AMX_FUNCSTUBNT *stub; - unsigned char *stubptr; - uint32_t pubvars = sections[FS_Pubvars]; - - pbvars = (sp_file_pubvars_t *)malloc(sizeof(sp_file_pubvars_t) * pubvars); - stubptr = (unsigned char *)hdr + hdr->pubvars; - - for (i=0; iaddress; - pbvars[i].name = stub->nameofs - (hdr->nametable + sizeof(uint16_t)); - - stubptr += hdr->defsize; - } - if (pubvars) - sfwrite(pbvars, sizeof(sp_file_pubvars_t), pubvars, spf); - free(pbvars); - spfw_next_section(spf); - } - - if (sections[FS_Natives]) { - sp_file_natives_t *nvtbl; - AMX_FUNCSTUBNT *stub; - unsigned char *stubptr; - uint32_t natives = (hdr->libraries - hdr->natives) / hdr->defsize; - - nvtbl = (sp_file_natives_t *)malloc(sizeof(sp_file_natives_t) * natives); - stubptr = (unsigned char *)hdr + hdr->natives; - - for (i=0; inameofs - (hdr->nametable + sizeof(uint16_t)); - - stubptr += hdr->defsize; - } - if (natives) { - sfwrite(nvtbl, sizeof(sp_file_natives_t), natives, spf); - } - free(nvtbl); - spfw_next_section(spf); - } - - if (sections[FS_Tags]) { - uint32_t numTags = (uint32_t)sections[FS_Tags]; - AMX_FUNCSTUBNT *stub; - sp_file_tag_t tag; - - for (i=0; itags + (i * hdr->defsize)); - tag.tag_id = stub->address; - tag.name = stub->nameofs - (hdr->nametable + sizeof(uint16_t)); - sfwrite(&tag, sizeof(sp_file_tag_t), 1, spf); - } - spfw_next_section(spf); - } - - if (sections[FS_Nametable]) { - unsigned char *base; - uint32_t namelen; - - /* write the entire block */ - base = (unsigned char *)hdr + hdr->nametable + sizeof(uint16_t); - /** - * note - the name table will be padded to sizeof(cell) bytes. - * this may clip at most an extra three bytes in! - */ - namelen = hdr->cod - hdr->nametable; - sfwrite(base, namelen, 1, spf); - spfw_next_section(spf); - } - - if (hdr->flags & AMX_FLAG_DEBUG) { - sp_fdbg_info_t info; - - memset(&info, 0, sizeof(sp_fdbg_info_t)); - - if (sections[FS_Natives]) { - uint16_t j; - uint32_t idx; - uint32_t name; - uint32_t natives = (hdr->libraries - hdr->natives) / hdr->defsize; - - sfwrite(&natives, sizeof(uint32_t), 1, spf); - for (idx=0; idxname); - /* store */ - dbgfile.addr = _ptr->address; - dbgfile.name = (uint32_t)memfile_tell(dbgtab); - sfwrite(&dbgfile, sizeof(sp_fdbg_file_t), 1, spf); - /* write to tab, then move to next */ - memfile_write(dbgtab, _ptr->name, len + 1); - dbgptr += sizeof(AMX_DBG_FILE) + len; - info.num_files++; - } - spfw_next_section(spf); - } - - if (sections[FS_DbgLine]) { - uint32_t idx; - AMX_DBG_LINE *line; - sp_fdbg_line_t dbgline; - for (idx=0; idxaddress; - dbgline.line = (uint32_t)line->line; - sfwrite(&dbgline, sizeof(sp_fdbg_line_t), 1, spf); - /* move to next */ - dbgptr += sizeof(AMX_DBG_LINE); - info.num_lines++; - } - spfw_next_section(spf); - } - - if (sections[FS_DbgSymbol]) { - uint32_t idx; - uint32_t dnum; - AMX_DBG_SYMBOL *sym; - AMX_DBG_SYMDIM *dim; - sp_fdbg_symbol_t dbgsym; - sp_fdbg_arraydim_t dbgdim; - uint32_t len; - - for (idx=0; idxaddress; - dbgsym.tagid = sym->tag; - dbgsym.codestart = (uint32_t)sym->codestart; - dbgsym.codeend = (uint32_t)sym->codeend; - dbgsym.dimcount = (uint16_t)sym->dim; - dbgsym.vclass = (uint8_t)sym->vclass; - dbgsym.ident = (uint8_t)sym->ident; - dbgsym.name = (uint32_t)memfile_tell(dbgtab); - sfwrite(&dbgsym, sizeof(sp_fdbg_symbol_t), 1, spf); - /* write to tab */ - len = strlen(sym->name); - memfile_write(dbgtab, sym->name, len + 1); - /* move to next */ - dbgptr += sizeof(AMX_DBG_SYMBOL) + len; - /* look for any dimensions */ - info.num_syms++; - for (dnum=0; dnumsize; - dbgdim.tagid = dim->tag; - sfwrite(&dbgdim, sizeof(sp_fdbg_arraydim_t), 1, spf); - /* move to next */ - dbgptr += sizeof(AMX_DBG_SYMDIM); - info.num_arrays++; - } - } - spfw_next_section(spf); - } - - sfwrite(&info, sizeof(sp_fdbg_info_t), 1, spf); - spfw_next_section(spf); - - if (sections[FS_DbgStrings]) { - sfwrite(dbgtab->base, sizeof(char), dbgtab->usedoffs, spf); - spfw_next_section(spf); - } - } - - spfw_finalize_all(spf); - - /** - * do compression - * new block for scoping only - */ - { - memfile_t *pOrig = (memfile_t *)spf->handle; - sp_file_hdr_t *pHdr; - unsigned char *proper; - size_t size; - Bytef *zcmp; - uLong disksize; - size_t header_size; - int err = Z_OK; - - /* reuse this memory block! */ - memfile_reset(bin_file); - - /* copy tip of header */ - memfile_write(bin_file, pOrig->base, sizeof(sp_file_hdr_t)); - - /* get pointer to header */ - pHdr = (sp_file_hdr_t *)bin_file->base; - - /* copy the rest of the header */ - memfile_write(bin_file, - (unsigned char *)pOrig->base + sizeof(sp_file_hdr_t), - pHdr->dataoffs - sizeof(sp_file_hdr_t)); - - header_size = pHdr->dataoffs; - size = pHdr->imagesize - header_size; - proper = (unsigned char *)pOrig->base + header_size; - - /* get initial size estimate */ - disksize = compressBound(pHdr->imagesize); - pHdr->disksize = (uint32_t)disksize; - zcmp = (Bytef *)malloc(pHdr->disksize); - - if ((err=compress2(zcmp, - &disksize, - (Bytef *)proper, - (uLong)size, - Z_BEST_COMPRESSION)) - != Z_OK) - { - free(zcmp); - pc_printf("Unable to compress (Z): error %d\n", err); - pc_printf("Falling back to no compression."); - memfile_write(bin_file, - proper, - size); - } else { - pHdr->disksize = (uint32_t)disksize + header_size; - pHdr->compression = SPFILE_COMPRESSION_GZ; - memfile_write(bin_file, - (unsigned char *)zcmp, - disksize); - free(zcmp); - } - } - - spfw_destroy(spf); - memfile_destroy(dbgtab); - - /** - * write file - */ - if ((fp=fopen(bin_file->name, "wb")) != NULL) { - fwrite(bin_file->base, bin_file->usedoffs, 1, fp); - fclose(fp); - } else { - pc_printf("Unable to open %s for writing!", bin_file->name); - } - - memfile_destroy(bin_file); - - return 0; - -write_error: - pc_printf("Error writing to file: %s", bin_file->name); - - spfw_destroy(spf); - unlink(bin_file->name); - memfile_destroy(bin_file); - memfile_destroy(dbgtab); - - return 1; + return pc_compile(argc,argv); } -void sfwrite(const void *buf, size_t size, size_t count, sp_file_t *spf) -{ - if (spf->funcs.fnWrite(buf, size, count, spf->handle) != count) - longjmp(brkout, 1); -} - -void sp_fdbg_ntv_start(int num_natives) -{ - if (num_natives == 0) - return; - - native_list = (t_native *)malloc(sizeof(t_native) * num_natives); - memset(native_list, 0, sizeof(t_native) * num_natives); -} - -#include "sc.h" -void sp_fdbg_ntv_hook(int index, symbol *sym) -{ - int i, j; - t_native *native; - - native = &native_list[index]; - native->name = strdup(sym->name); - - for (i = 0; i < sMAXARGS; i++) { - if (sym->dim.arglist[i].ident == 0) - break; - native->num_args++; - native->args[i].tag = sym->dim.arglist[i].tags == NULL ? 0 : sym->dim.arglist[i].tags[0]; - native->args[i].name = strdup(sym->dim.arglist[i].name); - native->args[i].ident = sym->dim.arglist[i].ident; - native->args[i].dimcount = sym->dim.arglist[i].numdim; - for (j = 0; j < native->args[i].dimcount; j++) { - native->args[i].dims[j].size = sym->dim.arglist[i].dim[j]; - native->args[i].dims[j].tagid = sym->dim.arglist[i].idxtag[j]; - } - } - - native->ret_tag = sym->tag; -} - - #if defined __linux__ || defined __APPLE__ extern "C" void __cxa_pure_virtual(void) { diff --git a/sourcepawn/compiler/sc.h b/sourcepawn/compiler/sc.h index aaab83c3..0a6b6fb5 100644 --- a/sourcepawn/compiler/sc.h +++ b/sourcepawn/compiler/sc.h @@ -578,13 +578,6 @@ void pc_resetasm(void *handle); int pc_writeasm(void *handle,const char *str); char *pc_readasm(void *handle,char *target,int maxchars); -/* output to binary (.AMX) file */ -void *pc_openbin(char *filename); -void pc_closebin(void *handle,int deletefile); -void pc_resetbin(void *handle,long offset); -int pc_writebin(void *handle,void *buffer,int size); -long pc_lengthbin(void *handle); /* return the length of the file */ - void sp_fdbg_ntv_start(int num_natives); void sp_fdbg_ntv_hook(int index, symbol *sym); @@ -767,7 +760,7 @@ int error(int number,...); void errorset(int code,int line); /* function prototypes in SC6.C */ -int assemble(void *fout,void *fin); +void assemble(const char *outname, void *fin); /* function prototypes in SC7.C */ void stgbuffer_cleanup(void); diff --git a/sourcepawn/compiler/sc1.cpp b/sourcepawn/compiler/sc1.cpp index bd543af3..95114951 100644 --- a/sourcepawn/compiler/sc1.cpp +++ b/sourcepawn/compiler/sc1.cpp @@ -200,16 +200,11 @@ int pc_compile(int argc, char *argv[]) char incfname[_MAX_PATH]; char reportname[_MAX_PATH]; char codepage[MAXCODEPAGE+1]; - FILE *binf; void *inpfmark; int lcl_packstr,lcl_needsemicolon,lcl_tabsize,lcl_require_newdecls; - #if !defined SC_LIGHT - int hdrsize=0; - #endif char *ptr; /* set global variables to their initial value */ - binf=NULL; initglobals(); errorset(sRESET,0); errorset(sEXPRRELEASE,0); @@ -315,14 +310,6 @@ int pc_compile(int argc, char *argv[]) outf=(FILE*)pc_openasm(outfname); /* first write to assembler file (may be temporary) */ if (outf==NULL) error(161,outfname); - /* immediately open the binary file, for other programs to check */ - if (sc_asmfile || sc_listing) { - binf=NULL; - } else { - binf=(FILE*)pc_openbin(binfname); - if (binf==NULL) - error(161,binfname); - } /* if */ setconstants(); /* set predefined constants and tagnames */ for (i=0; i0) { - long totalsize=hdrsize+code_idx; + long totalsize=code_idx; if (pc_amxram==0) totalsize+=(glb_declared+pc_stksize)*sizeof(cell); if (totalsize>=pc_amxlimit) @@ -498,20 +479,10 @@ cleanup: if (pc_amxram>0 && (glb_declared+pc_stksize)*sizeof(cell)>=(unsigned long)pc_amxram) flag_exceed=1; if ((!norun && (sc_debug & sSYMBOLIC)!=0) || verbosity>=2 || flag_exceed) { - pc_printf("Header size: %8ld bytes\n", (long)hdrsize); pc_printf("Code size: %8ld bytes\n", (long)code_idx); pc_printf("Data size: %8ld bytes\n", (long)glb_declared*sizeof(cell)); pc_printf("Stack/heap size: %8ld bytes\n", (long)pc_stksize*sizeof(cell)); -#if 0 - pc_printf("estimated max. usage"); - if (recursion) - pc_printf(": unknown, due to recursion\n"); - else if ((pc_memflags & suSLEEP_INSTR)!=0) - pc_printf(": unknown, due to the \"sleep\" instruction\n"); - else - pc_printf("=%ld cells (%ld bytes)\n",stacksize,stacksize*sizeof(cell)); -#endif - pc_printf("Total requirements:%8ld bytes\n", (long)hdrsize+(long)code_idx+(long)glb_declared*sizeof(cell)+(long)pc_stksize*sizeof(cell)); + pc_printf("Total requirements:%8ld bytes\n", (long)code_idx+(long)glb_declared*sizeof(cell)+(long)pc_stksize*sizeof(cell)); } /* if */ if (flag_exceed) error(166,pc_amxlimit+pc_amxram); /* this causes a jump back to label "cleanup" */ diff --git a/sourcepawn/compiler/sc6.cpp b/sourcepawn/compiler/sc6.cpp index 8e677f0e..88acfe30 100644 --- a/sourcepawn/compiler/sc6.cpp +++ b/sourcepawn/compiler/sc6.cpp @@ -1,3 +1,4 @@ +// vim: set sts=2 ts=8 sw=2 tw=99 et: /* Pawn compiler - Binary code generation (the "assembler") * * Copyright (c) ITB CompuPhase, 1997-2006 @@ -35,12 +36,18 @@ #if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ #include #endif +#include +#include +#include +#include "smx-builder.h" +#include "memory-buffer.h" +using namespace sp; +using namespace ke; static void append_dbginfo(void *fout); - -typedef cell (*OPCODE_PROC)(void *fbin,char *params,cell opcode); +typedef cell (*OPCODE_PROC)(Vector *buffer, char *params, cell opcode); typedef struct { cell opcode; @@ -50,7 +57,7 @@ typedef struct { } OPCODEC; static cell codeindex; /* similar to "code_idx" */ -static cell *lbltab; /* label table */ +static cell *LabelTable; /* label table */ static int writeerror; static int bytes_in, bytes_out; static jmp_buf compact_err; @@ -107,74 +114,6 @@ static ucell getparam(const char *s,char **n) return result; } -#if BYTE_ORDER==BIG_ENDIAN -static uint16_t *align16(uint16_t *v) -{ - unsigned char *s = (unsigned char *)v; - unsigned char t; - - /* swap two bytes */ - t=s[0]; - s[0]=s[1]; - s[1]=t; - return v; -} - -static uint32_t *align32(uint32_t *v) -{ - unsigned char *s = (unsigned char *)v; - unsigned char t; - - /* swap outer two bytes */ - t=s[0]; - s[0]=s[3]; - s[3]=t; - /* swap inner two bytes */ - t=s[1]; - s[1]=s[2]; - s[2]=t; - return v; -} - -#if PAWN_CELL_SIZE>=64 -static uint64_t *align64(uint64_t *v) -{ - unsigned char *s = (unsigned char *)v; - unsigned char t; - - t=s[0]; - s[0]=s[7]; - s[7]=t; - - t=s[1]; - s[1]=s[6]; - s[6]=t; - - t=s[2]; - s[2]=s[5]; - s[5]=t; - - t=s[3]; - s[3]=s[4]; - s[4]=t; - - return v; -} -#endif - - #if PAWN_CELL_SIZE==16 - #define aligncell(v) align16(v) - #elif PAWN_CELL_SIZE==32 - #define aligncell(v) align32(v) - #elif PAWN_CELL_SIZE==64 - #define aligncell(v) align64(v) - #endif -#else - #define align16(v) (v) - #define align32(v) (v) - #define aligncell(v) (v) -#endif - static char *skipwhitespace(char *str) { while (isspace(*str)) @@ -192,266 +131,178 @@ static char *stripcomment(char *str) return str; } -static void write_encoded(void *fbin,ucell *c,int num) -{ - #if PAWN_CELL_SIZE == 16 - #define ENC_MAX 3 /* a 16-bit cell is encoded in max. 3 bytes */ - #define ENC_MASK 0x03 /* after 2x7 bits, 2 bits remain to make 16 bits */ - #elif PAWN_CELL_SIZE == 32 - #define ENC_MAX 5 /* a 32-bit cell is encoded in max. 5 bytes */ - #define ENC_MASK 0x0f /* after 4x7 bits, 4 bits remain to make 32 bits */ - #elif PAWN_CELL_SIZE == 64 - #define ENC_MAX 10 /* a 32-bit cell is encoded in max. 10 bytes */ - #define ENC_MASK 0x01 /* after 9x7 bits, 1 bit remains to make 64 bits */ - #endif - - assert(fbin!=NULL); - while (num-->0) { - if (sc_compress) { - ucell p=(ucell)*c; - unsigned char t[ENC_MAX]; - unsigned char code; - int index; - for (index=0; index>=7; - } /* for */ - /* skip leading zeros */ - while (index>1 && t[index-1]==0 && (t[index-2] & 0x40)==0) - index--; - /* skip leading -1s */ - if (index==ENC_MAX && t[index-1]==ENC_MASK && (t[index-2] & 0x40)!=0) - index--; - while (index>1 && t[index-1]==0x7f && (t[index-2] & 0x40)!=0) - index--; - /* write high byte first, write continuation bits */ - assert(index>0); - while (index-->0) { - code=(unsigned char)((index==0) ? t[index] : (t[index]|0x80)); - writeerror |= !pc_writebin(fbin,&code,1); - bytes_out++; - } /* while */ - bytes_in+=sizeof *c; - assert(AMX_COMPACTMARGIN>2); - if (bytes_out-bytes_in>=AMX_COMPACTMARGIN-2) - longjmp(compact_err,1); - } else { - assert((pc_lengthbin(fbin) % sizeof(cell)) == 0); - writeerror |= !pc_writebin(fbin,aligncell(c),sizeof *c); - } /* if */ - c++; - } /* while */ -} - -#if defined __BORLANDC__ || defined __WATCOMC__ - #pragma argsused -#endif -static cell noop(void *fbin,char *params,cell opcode) +static cell noop(Vector *buffer, char *params, cell opcode) { return 0; } -#if defined __BORLANDC__ || defined __WATCOMC__ - #pragma argsused -#endif -static cell set_currentfile(void *fbin,char *params,cell opcode) +static cell set_currentfile(Vector *buffer, char *params, cell opcode) { fcurrent=(short)getparam(params,NULL); return 0; } -#if defined __BORLANDC__ || defined __WATCOMC__ - #pragma argsused -#endif -static cell parm0(void *fbin,char *params,cell opcode) +static cell parm0(Vector *buffer, char *params, cell opcode) { - if (fbin!=NULL) - write_encoded(fbin,(ucell*)&opcode,1); + if (buffer) + buffer->append(opcode); return opcodes(1); } -static cell parm1(void *fbin,char *params,cell opcode) +static cell parm1(Vector *buffer, char *params, cell opcode) { - ucell p=getparam(params,NULL); - if (fbin!=NULL) { - write_encoded(fbin,(ucell*)&opcode,1); - write_encoded(fbin,&p,1); - } /* if */ - return opcodes(1)+opargs(1); + ucell p = getparam(params, nullptr); + if (buffer) { + buffer->append(opcode); + buffer->append(p); + } + return opcodes(1) + opargs(1); } -static cell parm2(void *fbin,char *params,cell opcode) +static cell parm2(Vector *buffer, char *params, cell opcode) { - ucell p1=getparam(params,¶ms); - ucell p2=getparam(params,NULL); - if (fbin!=NULL) { - write_encoded(fbin,(ucell*)&opcode,1); - write_encoded(fbin,&p1,1); - write_encoded(fbin,&p2,1); - } /* if */ - return opcodes(1)+opargs(2); + ucell p1 = getparam(params, ¶ms); + ucell p2 = getparam(params, nullptr); + if (buffer) { + buffer->append(opcode); + buffer->append(p1); + buffer->append(p2); + } + return opcodes(1) + opargs(2); } -static cell parm3(void *fbin,char *params,cell opcode) +static cell parm3(Vector *buffer, char *params, cell opcode) { - ucell p1=getparam(params,¶ms); - ucell p2=getparam(params,¶ms); - ucell p3=getparam(params,NULL); - if (fbin!=NULL) { - write_encoded(fbin,(ucell*)&opcode,1); - write_encoded(fbin,&p1,1); - write_encoded(fbin,&p2,1); - write_encoded(fbin,&p3,1); - } /* if */ - return opcodes(1)+opargs(3); + ucell p1 = getparam(params, ¶ms); + ucell p2 = getparam(params, ¶ms); + ucell p3 = getparam(params, nullptr); + if (buffer) { + buffer->append(opcode); + buffer->append(p1); + buffer->append(p2); + buffer->append(p3); + } + return opcodes(1) + opargs(3); } -static cell parm4(void *fbin,char *params,cell opcode) +static cell parm4(Vector *buffer, char *params, cell opcode) { - ucell p1=getparam(params,¶ms); - ucell p2=getparam(params,¶ms); - ucell p3=getparam(params,¶ms); - ucell p4=getparam(params,NULL); - if (fbin!=NULL) { - write_encoded(fbin,(ucell*)&opcode,1); - write_encoded(fbin,&p1,1); - write_encoded(fbin,&p2,1); - write_encoded(fbin,&p3,1); - write_encoded(fbin,&p4,1); - } /* if */ - return opcodes(1)+opargs(4); + ucell p1 = getparam(params, ¶ms); + ucell p2 = getparam(params, ¶ms); + ucell p3 = getparam(params, ¶ms); + ucell p4 = getparam(params, nullptr); + if (buffer) { + buffer->append(opcode); + buffer->append(p1); + buffer->append(p2); + buffer->append(p3); + buffer->append(p4); + } + return opcodes(1) + opargs(4); } -static cell parm5(void *fbin,char *params,cell opcode) +static cell parm5(Vector *buffer, char *params, cell opcode) { - ucell p1=getparam(params,¶ms); - ucell p2=getparam(params,¶ms); - ucell p3=getparam(params,¶ms); - ucell p4=getparam(params,¶ms); - ucell p5=getparam(params,NULL); - if (fbin!=NULL) { - write_encoded(fbin,(ucell*)&opcode,1); - write_encoded(fbin,&p1,1); - write_encoded(fbin,&p2,1); - write_encoded(fbin,&p3,1); - write_encoded(fbin,&p4,1); - write_encoded(fbin,&p5,1); - } /* if */ - return opcodes(1)+opargs(5); + ucell p1 = getparam(params, ¶ms); + ucell p2 = getparam(params, ¶ms); + ucell p3 = getparam(params, ¶ms); + ucell p4 = getparam(params, ¶ms); + ucell p5 = getparam(params, nullptr); + if (buffer) { + buffer->append(opcode); + buffer->append(p1); + buffer->append(p2); + buffer->append(p3); + buffer->append(p4); + buffer->append(p5); + } + return opcodes(1) + opargs(5); } -#if defined __BORLANDC__ || defined __WATCOMC__ - #pragma argsused -#endif -static cell do_dump(void *fbin,char *params,cell opcode) +static cell do_dump(Vector *buffer, char *params, cell opcode) { - ucell p; int num = 0; - while (*params!='\0') { - p=getparam(params,¶ms); - if (fbin!=NULL) - write_encoded(fbin,&p,1); + while (*params != '\0') { + ucell p = getparam(params, ¶ms); + if (buffer) + buffer->append(p); num++; while (isspace(*params)) params++; - } /* while */ - return num*sizeof(cell); + } + return num * sizeof(cell); } -static cell do_call(void *fbin,char *params,cell opcode) +static cell do_call(Vector *buffer, char *params, cell opcode) { char name[sNAMEMAX+1]; - int i; - symbol *sym; - ucell p; + int i; for (i=0; !isspace(*params); i++,params++) { - assert(*params!='\0'); - assert(i=0 && i= 0 && val < sc_labnum); + p = LabelTable[val]; } else { - /* look up the function address; note that the correct file number must - * already have been set (in order for static globals to be found). - */ - sym=findglb(name,sGLOBAL); - assert(sym!=NULL); - assert(sym->ident==iFUNCTN || sym->ident==iREFFUNC); - assert(sym->vclass==sGLOBAL); - p=sym->addr; - } /* if */ + // Look up the function address; note that the correct file number must + // already have been set (in order for static globals to be found). + symbol *sym = findglb(name, sGLOBAL); + assert(sym->ident == iFUNCTN || sym->ident == iREFFUNC); + assert(sym->vclass == sGLOBAL); + p = sym->addr; + } - if (fbin!=NULL) { - write_encoded(fbin,(ucell*)&opcode,1); - write_encoded(fbin,&p,1); - } /* if */ - return opcodes(1)+opargs(1); + if (buffer) { + buffer->append(opcode); + buffer->append(p); + } + return opcodes(1) + opargs(1); } -static cell do_jump(void *fbin,char *params,cell opcode) +static cell do_jump(Vector *buffer, char *params, cell opcode) { - int i; - ucell p; + int i = (int)hex2long(params, nullptr); + assert(i >= 0 && i < sc_labnum); - i=(int)hex2long(params,NULL); - assert(i>=0 && iappend(opcode); + buffer->append(LabelTable[i]); + } + return opcodes(1) + opargs(1); } -static cell do_switch(void *fbin,char *params,cell opcode) +static cell do_switch(Vector *buffer, char *params, cell opcode) { - int i; - ucell p; + int i = (int)hex2long(params, nullptr); + assert(i >= 0 && i < sc_labnum); - i=(int)hex2long(params,NULL); - assert(i>=0 && iappend(opcode); + buffer->append(LabelTable[i]); + } + return opcodes(1) + opargs(1); } -#if defined __BORLANDC__ || defined __WATCOMC__ - #pragma argsused -#endif -static cell do_case(void *fbin,char *params,cell opcode) +static cell do_case(Vector *buffer, char *params, cell opcode) { - int i; - ucell p,v; + cell v = hex2long(params ,¶ms); + int i = (int)hex2long(params, nullptr); + assert(i >= 0 && i < sc_labnum); - v=hex2long(params,¶ms); - i=(int)hex2long(params,NULL); - assert(i>=0 && iappend(v); + buffer->append(LabelTable[i]); + } + return opcodes(0) + opargs(2); } static OPCODEC opcodelist[] = { @@ -659,403 +510,275 @@ static int findopcode(char *instr,int maxlen) return 0; /* not found, return special index */ } -int assemble(void *fout, void *fin) +// This pass is necessary because the code addresses of labels is only known +// after the peephole optimization flag. Labels can occur inside expressions +// (e.g. the conditional operator), which are optimized. +static void relocate_labels(void *fin) { - AMX_HEADER hdr; - AMX_FUNCSTUBNT func; - int numpublics,numnatives,numlibraries,numpubvars,numtags,padding; - long nametablesize,nameofs; - #if PAWN_CELL_SIZE > 32 - char line[512]; - #else - char line[256]; - #endif - char *instr,*params; - int i,pass,size; - int16_t count; - symbol *sym, **nativelist; - constvalue *constptr; - cell mainaddr; - char nullchar; - char testalias[sNAMEMAX+1]; + if (sc_labnum <= 0) + return; - /* if compression failed, restart the assembly with compaction switched off */ - if (setjmp(compact_err)!=0) { - assert(sc_compress); /* cannot arrive here if compact encoding was disabled */ - sc_compress=FALSE; - pc_resetbin(fout,0); - error(232); /* disabled compact encoding */ - } /* if */ + assert(!LabelTable); + LabelTable = (cell *)calloc(sc_labnum, sizeof(cell)); - #if !defined NDEBUG - /* verify that the opcode list is sorted (skip entry 1; it is reserved - * for a non-existant opcode) - */ + char line[256]; + cell codeindex = 0; + + pc_resetasm(fin); + while (pc_readasm(fin, line, sizeof(line))) { + stripcomment(line); + + char *instr = skipwhitespace(line); + if (*instr == '\0') // Ignore empty lines. + continue; + + if (tolower(*instr) == 'l' && *(instr + 1) == '.') { + int lindex = (int)hex2long(instr + 2, nullptr); + assert(lindex >= 0 && lindex < sc_labnum); + LabelTable[lindex] = codeindex; + } else { + // Get to the end of the instruction (make use of the '\n' that fgets() + // added at the end of the line; this way we *always* drop on a whitespace + // character. + char *params; + for (params = instr; *params != '\0' && !isspace(*params); params++) { + // Nothing. + } + assert(params > instr); + + int op_index = findopcode(instr, (int)(params - instr)); + OPCODEC &op = opcodelist[op_index]; + if (!op.name) { + *params = '\0'; + error(104, instr); + } + + if (op.segment == sIN_CSEG) + codeindex += op.func(nullptr, skipwhitespace(params), op.opcode); + } + } +} + +// Generate code or data into a buffer. +static void generate_segment(Vector *buffer, void *fin, int pass) +{ + pc_resetasm(fin); + + char line[255]; + while (pc_readasm(fin, line, sizeof(line))) { + stripcomment(line); + char *instr = skipwhitespace(line); + + // Ignore empty lines and labels. + if (*instr=='\0' || (tolower(*instr) == 'l' && *(instr + 1)=='.')) + continue; + + // Get to the end of the instruction (make use of the '\n' that fgets() + // added at the end of the line; this way we will *always* drop on a + // whitespace character) */ + char *params; + for (params=instr; *params != '\0' && !isspace(*params); params++) { + // Do nothing. + } + assert(params > instr); + + int op_index = findopcode(instr, (int)(params-instr)); + OPCODEC &op = opcodelist[op_index]; + assert(op.name != nullptr); + + if (op.segment != pass) + continue; + + op.func(buffer, skipwhitespace(params), op.opcode); + } +} + +#if !defined NDEBUG +// The opcode list should be sorted by name. +class VerifyOpcodeSorting +{ + public: + VerifyOpcodeSorting() { assert(opcodelist[1].name!=NULL); - for (i=2; i<(sizeof opcodelist / sizeof opcodelist[0]); i++) { + for (int i = 2; i<(sizeof opcodelist / sizeof opcodelist[0]); i++) { assert(opcodelist[i].name!=NULL); assert(stricmp(opcodelist[i].name,opcodelist[i-1].name)>0); } /* for */ - #endif + } +} sVerifyOpcodeSorting; +#endif - writeerror=FALSE; - nametablesize=sizeof(int16_t); - numpublics=0; - numnatives=0; - numpubvars=0; - mainaddr=-1; - /* count number of public and native functions and public variables */ - for (sym=glbtab.next; sym!=NULL; sym=sym->next) { - int match=0; +static int sort_by_addr(const void *a1, const void *a2) +{ + symbol *s1 = *(symbol **)a1; + symbol *s2 = *(symbol **)a2; + return s1->addr - s2->addr; +} + +typedef SmxListSection SmxNativeSection; +typedef SmxListSection SmxPublicSection; +typedef SmxListSection SmxPubvarSection; +typedef SmxListSection SmxTagSection; +typedef SmxBlobSection SmxDataSection; +typedef SmxBlobSection SmxCodeSection; + +static void assemble_to_buffer(MemoryBuffer *buffer, void *fin) +{ + StringPool pool; + SmxBuilder builder; + Ref natives = new SmxNativeSection(".natives"); + Ref publics = new SmxPublicSection(".publics"); + Ref pubvars = new SmxPubvarSection(".pubvars"); + Ref tags = new SmxTagSection(".tags"); + Ref data = new SmxDataSection(".data"); + Ref code = new SmxCodeSection(".code"); + Ref names = new SmxNameTable(".names"); + + Vector nativeList; + + // Build the easy symbol tables. + for (symbol *sym=glbtab.next; sym; sym=sym->next) { if (sym->ident==iFUNCTN) { - if ((sym->usage & uNATIVE)!=0 && (sym->usage & uREAD)!=0 && sym->addr>=0) { - match=++numnatives; + if ((sym->usage & uNATIVE)!=0 && (sym->usage & uREAD)!=0 && sym->addr >= 0) { + // Natives require special handling, so we save them for later. + nativeList.append(sym); + } else if ((sym->usage & uPUBLIC)!=0 && (sym->usage & uDEFINE)!=0) { + sp_file_publics_t &pubfunc = publics->add(); + pubfunc.address = sym->addr; + pubfunc.name = names->add(pool, sym->name); } - if ((sym->usage & uPUBLIC)!=0 && (sym->usage & uDEFINE)!=0) - match=++numpublics; - if (strcmp(sym->name,uMAINFUNC)==0) { - assert(sym->vclass==sGLOBAL); - mainaddr=sym->addr; - } /* if */ } else if (sym->ident==iVARIABLE || sym->ident == iARRAY || sym->ident == iREFARRAY) { - if ((sym->usage & uPUBLIC)!=0 && (sym->usage & (uREAD | uWRITTEN))!=0) - match=++numpubvars; - } /* if */ - if (match) { - const char *aliasptr = sym->name; - assert(sym!=NULL); - if (((sym->usage & uNATIVE)!=0) && lookup_alias(testalias,sym->name)) { - aliasptr = "@"; - } /* if */ - nametablesize+=strlen(aliasptr)+1; - } /* if */ - } /* for */ - assert(numnatives==ntv_funcid); - - /* count number of libraries */ - numlibraries=0; - if (pc_addlibtable) { - for (constptr=libname_tab.next; constptr!=NULL; constptr=constptr->next) { - if (constptr->value>0) { - assert(strlen(constptr->name)>0); - numlibraries++; - nametablesize+=strlen(constptr->name)+1; - } /* if */ - } /* for */ - } /* if */ - - /* count number of public tags */ - numtags=0; - for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { - /*if ((constptr->value & PUBLICTAG)!=0) {*/ - assert(strlen(constptr->name)>0); - numtags++; - nametablesize+=strlen(constptr->name)+1; - /*} if */ - } /* for */ - - /* pad the header to sc_dataalign - * => thereby the code segment is aligned - * => since the code segment is padded to a sc_dataalign boundary, the data segment is aligned - * => and thereby the stack top is aligned too - */ - assert(sc_dataalign!=0); - padding= (int)(sc_dataalign - (sizeof hdr + nametablesize) % sc_dataalign); - if (padding==sc_dataalign) - padding=0; - - /* write the abstract machine header */ - memset(&hdr, 0, sizeof hdr); - hdr.magic=(unsigned short)AMX_MAGIC; - hdr.file_version=(char)((pc_optimize<=sOPTIMIZE_NOMACRO) ? MAX_FILE_VER_JIT : CUR_FILE_VERSION); - hdr.amx_version=(char)((pc_optimize<=sOPTIMIZE_NOMACRO) ? MIN_AMX_VER_JIT : MIN_AMX_VERSION); - hdr.flags=(short)(sc_debug & sSYMBOLIC); - if (sc_compress) - hdr.flags|=AMX_FLAG_COMPACT; - if (sc_debug==0) - hdr.flags|=AMX_FLAG_NOCHECKS; - if (pc_memflags & suSLEEP_INSTR) - hdr.flags|=AMX_FLAG_SLEEP; - hdr.defsize=sizeof(AMX_FUNCSTUBNT); - hdr.publics=sizeof hdr; /* public table starts right after the header */ - hdr.natives=hdr.publics + numpublics*sizeof(AMX_FUNCSTUBNT); - hdr.libraries=hdr.natives + numnatives*sizeof(AMX_FUNCSTUBNT); - hdr.pubvars=hdr.libraries + numlibraries*sizeof(AMX_FUNCSTUBNT); - hdr.tags=hdr.pubvars + numpubvars*sizeof(AMX_FUNCSTUBNT); - hdr.nametable=hdr.tags + numtags*sizeof(AMX_FUNCSTUBNT); - hdr.cod=hdr.nametable + nametablesize + padding; - hdr.dat=hdr.cod + code_idx; - hdr.hea=hdr.dat + glb_declared*sizeof(cell); - hdr.stp=hdr.hea + pc_stksize*sizeof(cell); - hdr.cip=mainaddr; - hdr.size=hdr.hea; /* preset, this is incorrect in case of compressed output */ - pc_writebin(fout,&hdr,sizeof hdr); - - /* dump zeros up to the rest of the header, so that we can easily "seek" */ - nullchar='\0'; - for (nameofs=sizeof hdr; nameofsnext) { - if (sym->ident==iFUNCTN - && (sym->usage & uPUBLIC)!=0 && (sym->usage & uDEFINE)!=0) - { - assert(sym->vclass==sGLOBAL); - func.address=sym->addr; - func.nameofs=nameofs; - #if BYTE_ORDER==BIG_ENDIAN - align32(&func.address); - align32(&func.nameofs); - #endif - pc_resetbin(fout,hdr.publics+count*sizeof(AMX_FUNCSTUBNT)); - pc_writebin(fout,&func,sizeof func); - pc_resetbin(fout,nameofs); - pc_writebin(fout,sym->name,strlen(sym->name)+1); - nameofs+=strlen(sym->name)+1; - count++; - } /* if */ - } /* for */ - - /* write the natives table */ - /* The native functions must be written in sorted order. (They are - * sorted on their "id", not on their name). A nested loop to find - * each successive function would be an O(n^2) operation. But we - * do not really need to sort, because the native function id's - * are sequential and there are no duplicates. So we first walk - * through the complete symbol list and store a pointer to every - * native function of interest in a temporary table, where its id - * serves as the index in the table. Now we can walk the table and - * have all native functions in sorted order. - */ - if (numnatives>0) { - nativelist=(symbol **)malloc(numnatives*sizeof(symbol *)); - if (nativelist==NULL) - error(103); /* insufficient memory */ - #if !defined NDEBUG - memset(nativelist,0,numnatives*sizeof(symbol *)); /* for NULL checking */ - #endif - for (sym=glbtab.next; sym!=NULL; sym=sym->next) { - if (sym->ident==iFUNCTN && (sym->usage & uNATIVE)!=0 && (sym->usage & uREAD)!=0 && sym->addr>=0) { - assert(sym->addr < numnatives); - nativelist[(int)sym->addr]=sym; - } /* if */ - } /* for */ - count=0; - sp_fdbg_ntv_start(numnatives); - for (i=0; iname; - if (lookup_alias(testalias,sym->name)) { - aliasptr = "@"; + if ((sym->usage & uPUBLIC)!=0 && (sym->usage & (uREAD | uWRITTEN))!=0) { + sp_file_pubvars_t &pubvar = pubvars->add(); + pubvar.address = sym->addr; + pubvar.name = names->add(pool, sym->name); } - assert(sym->vclass==sGLOBAL); - func.address=0; - func.nameofs=nameofs; - #if BYTE_ORDER==BIG_ENDIAN - align32(&func.address); - align32(&func.nameofs); - #endif - pc_resetbin(fout,hdr.natives+count*sizeof(AMX_FUNCSTUBNT)); - pc_writebin(fout,&func,sizeof func); - pc_resetbin(fout,nameofs); - pc_writebin(fout,(void *)aliasptr,strlen(aliasptr)+1); - nameofs+=strlen(aliasptr)+1; - count++; - } /* for */ - free(nativelist); - } /* if */ + } + } - /* write the libraries table */ - if (pc_addlibtable) { - count=0; - for (constptr=libname_tab.next; constptr!=NULL; constptr=constptr->next) { - if (constptr->value>0) { - assert(strlen(constptr->name)>0); - func.address=0; - func.nameofs=nameofs; - #if BYTE_ORDER==BIG_ENDIAN - align32(&func.address); - align32(&func.nameofs); - #endif - pc_resetbin(fout,hdr.libraries+count*sizeof(AMX_FUNCSTUBNT)); - pc_writebin(fout,&func,sizeof func); - pc_resetbin(fout,nameofs); - pc_writebin(fout,constptr->name,strlen(constptr->name)+1); - nameofs+=strlen(constptr->name)+1; - count++; - } /* if */ - } /* for */ - } /* if */ + // Build the tags table. + for (constvalue *constptr = tagname_tab.next; constptr; constptr = constptr->next) { + assert(strlen(constptr->name)>0); - /* write the public variables table */ - count=0; - for (sym=glbtab.next; sym!=NULL; sym=sym->next) { - if ((sym->ident==iVARIABLE || sym->ident==iARRAY || sym->ident==iREFARRAY) - && (sym->usage & uPUBLIC)!=0 && (sym->usage & (uREAD | uWRITTEN))!=0) { - //removed until structs don't seem to mess this up - //assert((sym->usage & uDEFINE)!=0); - assert(sym->vclass==sGLOBAL); - func.address=sym->addr; - func.nameofs=nameofs; - #if BYTE_ORDER==BIG_ENDIAN - align32(&func.address); - align32(&func.nameofs); - #endif - pc_resetbin(fout,hdr.pubvars+count*sizeof(AMX_FUNCSTUBNT)); - pc_writebin(fout,&func,sizeof func); - pc_resetbin(fout,nameofs); - pc_writebin(fout,sym->name,strlen(sym->name)+1); - nameofs+=strlen(sym->name)+1; - count++; - } /* if */ - } /* for */ + sp_file_tag_t &tag = tags->add(); + tag.tag_id = constptr->value; + tag.name = names->add(pool, constptr->name); + } - /* write the public tagnames table */ - count=0; - for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { - /*if ((constptr->value & PUBLICTAG)!=0) {*/ - assert(strlen(constptr->name)>0); - func.address=constptr->value & TAGMASK; - func.nameofs=nameofs; - #if BYTE_ORDER==BIG_ENDIAN - align32(&func.address); - align32(&func.nameofs); - #endif - pc_resetbin(fout,hdr.tags+count*sizeof(AMX_FUNCSTUBNT)); - pc_writebin(fout,&func,sizeof func); - pc_resetbin(fout,nameofs); - pc_writebin(fout,constptr->name,strlen(constptr->name)+1); - nameofs+=strlen(constptr->name)+1; - count++; - /*} if */ - } /* for */ + // Shuffle natives to be in address order. + qsort(nativeList.buffer(), nativeList.length(), sizeof(symbol *), sort_by_addr); + for (size_t i = 0; i < nativeList.length(); i++) { + symbol *sym = nativeList[i]; + assert(size_t(sym->addr) == i); - /* write the "maximum name length" field in the name table */ - assert(nameofs==hdr.nametable+nametablesize); - pc_resetbin(fout,hdr.nametable); - count=sNAMEMAX; - #if BYTE_ORDER==BIG_ENDIAN - align16(&count); - #endif - pc_writebin(fout,&count,sizeof count); - pc_resetbin(fout,hdr.cod); + sp_file_natives_t &entry = natives->add(); - /* First pass: relocate all labels */ - /* This pass is necessary because the code addresses of labels is only known - * after the peephole optimization flag. Labels can occur inside expressions - * (e.g. the conditional operator), which are optimized. - */ - lbltab=NULL; - if (sc_labnum>0) { - /* only very short programs have zero labels; no first pass is needed - * if there are no labels */ - lbltab=(cell *)malloc(sc_labnum*sizeof(cell)); - if (lbltab==NULL) - error(103); /* insufficient memory */ - codeindex=0; - pc_resetasm(fin); - while (pc_readasm(fin,line,sizeof line)!=NULL) { - stripcomment(line); - instr=skipwhitespace(line); - /* ignore empty lines */ - if (*instr=='\0') - continue; - if (tolower(*instr)=='l' && *(instr+1)=='.') { - int lindex=(int)hex2long(instr+2,NULL); - assert(lindex>=0 && lindexinstr); - i=findopcode(instr,(int)(params-instr)); - if (opcodelist[i].name==NULL) { - *params='\0'; - error(104,instr); /* invalid assembler instruction */ - } /* if */ - if (opcodelist[i].segment==sIN_CSEG) - codeindex+=opcodelist[i].func(NULL,skipwhitespace(params),opcodelist[i].opcode); - } /* if */ - } /* while */ - } /* if */ + char testalias[sNAMEMAX + 1]; + if (lookup_alias(testalias, sym->name)) + entry.name = names->add(pool, "@"); + else + entry.name = names->add(pool, sym->name); + } - /* Second pass (actually 2 more passes, one for all code and one for all data) */ - bytes_in=0; - bytes_out=0; - for (pass=sIN_CSEG; pass<=sIN_DSEG; pass++) { - pc_resetasm(fin); - while (pc_readasm(fin,line,sizeof line)!=NULL) { - stripcomment(line); - instr=skipwhitespace(line); - /* ignore empty lines and labels (labels have a special syntax, so these - * must be parsed separately) */ - if (*instr=='\0' || (tolower(*instr)=='l' && *(instr+1)=='.')) - continue; - /* get to the end of the instruction (make use of the '\n' that fgets() - * added at the end of the line; this way we will *always* drop on a - * whitespace character) */ - for (params=instr; *params!='\0' && !isspace(*params); params++) - /* nothing */; - assert(params>instr); - i=findopcode(instr,(int)(params-instr)); - assert(opcodelist[i].name!=NULL); - if (opcodelist[i].segment==pass) - opcodelist[i].func(fout,skipwhitespace(params),opcodelist[i].opcode); - } /* while */ - } /* for */ - if (bytes_out-bytes_in>0) - error(106); /* compression buffer overflow */ + // Relocate all labels in the assembly buffer. + relocate_labels(fin); - if (lbltab!=NULL) { - free(lbltab); - #if !defined NDEBUG - lbltab=NULL; - #endif - } /* if */ + // Generate buffers. + Vector code_buffer, data_buffer; + generate_segment(&code_buffer, fin, sIN_CSEG); + generate_segment(&data_buffer, fin, sIN_DSEG); - if (sc_compress) - hdr.size=pc_lengthbin(fout);/* get this value before appending debug info */ - if (!writeerror && (sc_debug & sSYMBOLIC)!=0) - append_dbginfo(fout); /* optionally append debug file */ + // Set up the code section. + code->header().codesize = code_buffer.length() * sizeof(cell); + code->header().cellsize = sizeof(cell); + code->header().codeversion = SmxConsts::CODE_VERSION_JIT2; + code->header().flags = 0; // :TODO: CODEFLAG_DEBUG; + code->header().main = 0; + code->header().code = sizeof(sp_file_code_t); + code->setBlob((uint8_t *)code_buffer.buffer(), code_buffer.length() * sizeof(cell)); - if (writeerror) - error(101,"disk full"); + // Set up the data section. Note pre-SourceMod 1.7, the |memsize| was + // computed as AMX::stp, which included the entire memory size needed to + // store the file. Here (in 1.7+), we allocate what is actually needed + // by the plugin. + data->header().datasize = data_buffer.length() * sizeof(cell); + data->header().memsize = + data->header().datasize + + glb_declared * sizeof(cell) + + pc_stksize * sizeof(cell); + data->header().data = sizeof(sp_file_data_t); + data->setBlob((uint8_t *)data_buffer.buffer(), data_buffer.length() * sizeof(cell)); - /* adjust the header */ - size=(int)hdr.cod; /* save, the value in the header may be swapped */ - #if BYTE_ORDER==BIG_ENDIAN - align32(&hdr.size); - align16(&hdr.magic); - align16(&hdr.flags); - align16(&hdr.defsize); - align32(&hdr.publics); - align32(&hdr.natives); - align32(&hdr.libraries); - align32(&hdr.pubvars); - align32(&hdr.tags); - align32(&hdr.nametable); - align32(&hdr.cod); - align32(&hdr.dat); - align32(&hdr.hea); - align32(&hdr.stp); - align32(&hdr.cip); - #endif - pc_resetbin(fout,0); - pc_writebin(fout,&hdr,sizeof hdr); + free(LabelTable); + LabelTable = nullptr; - /* return the size of the header (including name tables, but excluding code - * or data sections) - */ - return size; + // Add tables in the same order SourceMod 1.6 added them. + builder.add(code); + builder.add(data); + builder.add(publics); + builder.add(pubvars); + builder.add(natives); + builder.add(names); + builder.add(tags); + builder.write(buffer); +} + +static void splat_to_binary(const char *binfname, void *bytes, size_t size) +{ + // Note: error 161 will setjmp(), which skips destructors :( + FILE *fp = fopen(binfname, "wb"); + if (!fp) { + error(161, binfname); + return; + } + if (fwrite(bytes, 1, size, fp) != size) { + fclose(fp); + error(161, binfname); + return; + } + fclose(fp); +} + +void assemble(const char *binfname, void *fin) +{ + MemoryBuffer buffer; + assemble_to_buffer(&buffer, fin); + + // Buffer compression logic. + sp_file_hdr_t *header = (sp_file_hdr_t *)buffer.bytes(); + size_t region_size = header->imagesize - header->dataoffs; + size_t zbuf_max = compressBound(region_size); + Bytef *zbuf = (Bytef *)malloc(zbuf_max); + + uLong new_disksize = zbuf_max; + int err = compress2( + zbuf, + &new_disksize, + (Bytef *)(buffer.bytes() + header->dataoffs), + region_size, + Z_BEST_COMPRESSION + ); + if (err != Z_OK) { + free(zbuf); + pc_printf("Unable to compress, error %d\n", err); + pc_printf("Falling back to no compression.\n"); + splat_to_binary(binfname, buffer.bytes(), buffer.size()); + return; + } + + header->disksize = new_disksize + header->dataoffs; + header->compression = SmxConsts::FILE_COMPRESSION_GZ; + + buffer.rewind(header->dataoffs); + buffer.write(zbuf, new_disksize); + free(zbuf); + + splat_to_binary(binfname, buffer.bytes(), buffer.size()); +} + +int pc_writebin(void *handle,void *buffer,int size) { + assert(false); + return 1; } static void append_dbginfo(void *fout) @@ -1163,17 +886,6 @@ static void append_dbginfo(void *fout) /* pass 2: generate the tables */ - #if BYTE_ORDER==BIG_ENDIAN - align32((uint32_t*)&dbghdr.size); - align16(&dbghdr.magic); - align16(&dbghdr.flags); - align16(&dbghdr.files); - align16(&dbghdr.lines); - align16(&dbghdr.symbols); - align16(&dbghdr.tags); - align16(&dbghdr.automatons); - align16(&dbghdr.states); - #endif writeerror |= !pc_writebin(fout,&dbghdr,sizeof dbghdr); /* file table */ @@ -1188,9 +900,6 @@ static void append_dbginfo(void *fout) if (codeidx!=previdx) { if (prevstr!=NULL) { assert(prevname!=NULL); - #if BYTE_ORDER==BIG_ENDIAN - aligncell(&previdx); - #endif writeerror |= !pc_writebin(fout,&previdx,sizeof previdx); writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1); } /* if */ @@ -1202,9 +911,6 @@ static void append_dbginfo(void *fout) } /* for */ if (prevstr!=NULL) { assert(prevname!=NULL); - #if BYTE_ORDER==BIG_ENDIAN - aligncell(&previdx); - #endif writeerror |= !pc_writebin(fout,&previdx,sizeof previdx); writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1); } /* if */ @@ -1217,10 +923,6 @@ static void append_dbginfo(void *fout) if (str[0]=='L') { dbgline.address=hex2long(str+2,&str); dbgline.line=(int32_t)hex2long(str,NULL); - #if BYTE_ORDER==BIG_ENDIAN - aligncell(&dbgline.address); - align32(&dbgline.line); - #endif writeerror |= !pc_writebin(fout,&dbgline,sizeof dbgline); } /* if */ } /* for */ @@ -1256,20 +958,9 @@ static void append_dbginfo(void *fout) } /* while */ } /* if */ dbgsymdim = dbgsym.dim; - #if BYTE_ORDER==BIG_ENDIAN - aligncell(&dbgsym.address); - align16(&dbgsym.tag); - aligncell(&dbgsym.codestart); - aligncell(&dbgsym.codeend); - align16(&dbgsym.dim); - #endif writeerror |= !pc_writebin(fout,&dbgsym,offsetof(AMX_DBG_SYMBOL, name)); writeerror |= !pc_writebin(fout,symname,strlen(symname)+1); for (dim=0; dimnext) { assert(strlen(constptr->name)>0); id1=(int16_t)(constptr->value & TAGMASK); - #if BYTE_ORDER==BIG_ENDIAN - align16(&id1); - #endif writeerror |= !pc_writebin(fout,&id1,sizeof id1); writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); } /* for */ @@ -1291,10 +979,6 @@ static void append_dbginfo(void *fout) assert((constptr->index==0 && strlen(constptr->name)==0) || strlen(constptr->name)>0); id1=(int16_t)constptr->index; address=(ucell)constptr->value; - #if BYTE_ORDER==BIG_ENDIAN - align16(&id1); - aligncell(&address); - #endif writeerror |= !pc_writebin(fout,&id1,sizeof id1); writeerror |= !pc_writebin(fout,&address,sizeof address); writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); @@ -1306,10 +990,6 @@ static void append_dbginfo(void *fout) id1=(int16_t)constptr->value; id2=(int16_t)constptr->index; address=(ucell)constptr->value; - #if BYTE_ORDER==BIG_ENDIAN - align16(&id1); - align16(&id2); - #endif writeerror |= !pc_writebin(fout,&id1,sizeof id1); writeerror |= !pc_writebin(fout,&id2,sizeof id2); writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); diff --git a/sourcepawn/compiler/smx-builder.cpp b/sourcepawn/compiler/smx-builder.cpp new file mode 100644 index 00000000..d33f0330 --- /dev/null +++ b/sourcepawn/compiler/smx-builder.cpp @@ -0,0 +1,111 @@ +// vim: set sts=2 ts=8 sw=2 tw=99 et: +// +// Copyright (C) 2012-2014 David Anderson +// +// This file is part of SourcePawn. +// +// SourcePawn is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with +// SourcePawn. If not, see http://www.gnu.org/licenses/. +#include "smx-builder.h" + +using namespace ke; +using namespace sp; + +SmxBuilder::SmxBuilder() +{ +} + +bool +SmxBuilder::write(ISmxBuffer *buf) +{ + sp_file_hdr_t header; + header.magic = SmxConsts::FILE_MAGIC; + header.version = SmxConsts::SP1_VERSION_1_1; + header.compression = SmxConsts::FILE_COMPRESSION_NONE; + + header.disksize = sizeof(header) + + sizeof(sp_file_section_t) * sections_.length(); + + // Note that |dataoffs| here is just to mimic what it would be in earlier + // versions of Pawn. Its value does not actually matter per the SMX spec, + // aside from having to be >= sizeof(sp_file_hdr_t). Here, we hint that + // only the region after the section list and names should be compressed. + header.dataoffs = header.disksize; + + size_t current_string_offset = 0; + for (size_t i = 0; i < sections_.length(); i++) { + Ref section = sections_[i]; + header.disksize += section->length(); + current_string_offset += section->name().length() + 1; + } + header.disksize += current_string_offset; + header.dataoffs += current_string_offset; + + header.imagesize = header.disksize; + header.sections = sections_.length(); + + // We put the string table after the sections table. + header.stringtab = sizeof(header) + sizeof(sp_file_section_t) * sections_.length(); + + if (!buf->write(&header, sizeof(header))) + return false; + + size_t current_offset = sizeof(header); + size_t current_data_offset = header.stringtab + current_string_offset; + current_string_offset = 0; + for (size_t i = 0; i < sections_.length(); i++) { + sp_file_section_t s; + s.nameoffs = current_string_offset; + s.dataoffs = current_data_offset; + s.size = sections_[i]->length(); + if (!buf->write(&s, sizeof(s))) + return false; + + current_offset += sizeof(s); + current_data_offset += s.size; + current_string_offset += sections_[i]->name().length() + 1; + } + assert(buf->pos() == current_offset); + assert(current_offset == header.stringtab); + + for (size_t i = 0; i < sections_.length(); i++) { + const AString &name = sections_[i]->name(); + if (!buf->write(name.chars(), name.length() + 1)) + return false; + } + current_offset += current_string_offset; + + assert(buf->pos() == current_offset); + + for (size_t i = 0; i < sections_.length(); i++) { + if (!sections_[i]->write(buf)) + return false; + current_offset += sections_[i]->length(); + } + + assert(buf->pos() == current_offset); + assert(current_offset == header.disksize); + + return true; +} + +bool +SmxNameTable::write(ISmxBuffer *buf) +{ + for (size_t i = 0; i < names_.length(); i++) { + Atom *str = names_[i]; + if (!buf->write(str->chars(), str->length() + 1)) + return false; + } + return true; +} + diff --git a/sourcepawn/compiler/smx-builder.h b/sourcepawn/compiler/smx-builder.h new file mode 100644 index 00000000..1b165c52 --- /dev/null +++ b/sourcepawn/compiler/smx-builder.h @@ -0,0 +1,180 @@ +// vim: set sts=2 ts=8 sw=2 tw=99 et: +// +// Copyright (C) 2012-2014 AlliedModders LLC, David Anderson +// +// This file is part of SourcePawn. +// +// SourcePawn is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with +// SourcePawn. If not, see http://www.gnu.org/licenses/. +#ifndef _include_spcomp2_smx_builder_h_ +#define _include_spcomp2_smx_builder_h_ + +#include +#include +#include +#include +#include +#include "string-pool.h" + +namespace ke { + +class ISmxBuffer +{ + public: + virtual bool write(const void *bytes, size_t len) = 0; + virtual size_t pos() const = 0; +}; + +class SmxSection : public Refcounted +{ + public: + SmxSection(const char *name) + : name_(name) + { + } + + virtual bool write(ISmxBuffer *buf) = 0; + virtual size_t length() const = 0; + + const AString &name() const { + return name_; + } + + private: + AString name_; +}; + +template +class SmxBlobSection : public SmxSection +{ + public: + SmxBlobSection(const char *name) + : SmxSection(name) + { + } + + T &header() { + return t_; + } + void setBlob(uint8_t *blob, size_t len) { + extra_ = blob; + extra_len_ = len; + } + bool write(ISmxBuffer *buf) KE_OVERRIDE { + if (!buf->write(&t_, sizeof(t_))) + return false; + return buf->write(extra_, extra_len_); + } + size_t length() const KE_OVERRIDE { + return sizeof(t_) + extra_len_; + } + + private: + T t_; + uint8_t *extra_; + size_t extra_len_; +}; + +template +class SmxListSection : public SmxSection +{ + public: + SmxListSection(const char *name) + : SmxSection(name) + { + } + + void append(const T &t) { + list_.append(t); + } + T &add() { + list_.append(T()); + return list_.back(); + } + bool write(ISmxBuffer *buf) KE_OVERRIDE { + return buf->write(list_.buffer(), list_.length() * sizeof(T)); + } + size_t length() const KE_OVERRIDE { + return list_.length() * sizeof(T); + } + + private: + Vector list_; +}; + +class SmxNameTable : public SmxSection +{ + public: + SmxNameTable(const char *name) + : SmxSection(".names"), + buffer_size_(0) + { + name_table_.init(64); + } + + uint32_t add(StringPool &pool, const char *str) { + return add(pool.add(str)); + } + + uint32_t add(Atom *str) { + NameTable::Insert i = name_table_.findForAdd(str); + if (i.found()) + return i->value; + + assert(IsUint32AddSafe(buffer_size_, str->length() + 1)); + + uint32_t index = buffer_size_; + name_table_.add(i, str, index); + names_.append(str); + buffer_size_ += str->length() + 1; + return index; + } + + bool write(ISmxBuffer *buf) KE_OVERRIDE; + size_t length() const KE_OVERRIDE { + return buffer_size_; + } + + private: + struct HashPolicy { + static uint32_t hash(Atom *str) { + return HashPointer(str); + } + static bool matches(Atom *a, Atom *b) { + return a == b; + } + }; + typedef HashMap NameTable; + + NameTable name_table_; + Vector names_; + uint32_t buffer_size_; +}; + +class SmxBuilder +{ + public: + SmxBuilder(); + + bool write(ISmxBuffer *buf); + + void add(const Ref §ion) { + sections_.append(section); + } + + private: + Vector> sections_; +}; + +} // namespace ke + +#endif // _include_spcomp2_smx_builder_h_ diff --git a/sourcepawn/compiler/sp_file.cpp b/sourcepawn/compiler/sp_file.cpp deleted file mode 100644 index b849c311..00000000 --- a/sourcepawn/compiler/sp_file.cpp +++ /dev/null @@ -1,213 +0,0 @@ -#include -#include -#include -#include "sp_file.h" -#include "memfile.h" - -void *mf_open(const char *name); -void mf_close(void *handle); -size_t mf_write(const void *buf, size_t size, size_t count, void *handle); -size_t mf_read(void *buf, size_t size, size_t count, void *handle); -size_t mf_getpos(void *handle); -int mf_setpos(void *handle, size_t pos); - -sp_writefuncs_t cstd_funcs = -{ - mf_open, - mf_close, - mf_write, - mf_read, - mf_getpos, - mf_setpos -}; - -sp_file_t *spfw_create(const char *name, sp_writefuncs_t *optfuncs) -{ - sp_file_t file; - sp_file_t *pFile; - - if (!optfuncs) - optfuncs = &cstd_funcs; - - file.handle = optfuncs->fnOpen(name); - if (!file.handle) - return NULL; - - pFile = (sp_file_t *)malloc(sizeof(sp_file_t)); - - pFile->handle = file.handle; - memcpy(&pFile->funcs, optfuncs, sizeof(sp_writefuncs_t)); - pFile->curoffs = 0; - pFile->header.magic = SPFILE_MAGIC; - pFile->header.sections = 0; - pFile->header.stringtab = 0; - pFile->header.version = SPFILE_VERSION; - pFile->header.imagesize = 0; - pFile->header.disksize = 0; - pFile->header.compression = SPFILE_COMPRESSION_NONE; - pFile->header.dataoffs = 0; - pFile->lastsection = 0; - pFile->offsets = NULL; - pFile->sections = NULL; - pFile->state = -1; - pFile->nametab = NULL; - pFile->nametab_idx = 0; - - return pFile; -} - -void spfw_destroy(sp_file_t *spf) -{ - free(spf->sections); - free(spf->nametab); - free(spf->offsets); - spf->funcs.fnClose(spf->handle); - free(spf); -} - -uint8_t spfw_add_section(sp_file_t *spf, const char *name) -{ - size_t namelen; - uint8_t s; - if (spf->state != -1) - return 0; - - namelen = strlen(name) + 1; - - if (spf->header.sections == 0) - { - /** allocate for first section */ - spf->sections = (sp_file_section_t *)malloc(sizeof(sp_file_section_t)); - spf->offsets = (size_t *)malloc(sizeof(size_t)); - spf->nametab = (char *)malloc(namelen); - } else { - uint16_t num = spf->header.sections + 1; - spf->sections = (sp_file_section_t *)realloc(spf->sections, sizeof(sp_file_section_t) * num); - spf->offsets = (size_t *)realloc(spf->offsets, sizeof(size_t) * num); - spf->nametab = (char *)realloc(spf->nametab, spf->nametab_idx + namelen); - } - - s = spf->header.sections; - - spf->sections[s].nameoffs = spf->nametab_idx; - /** - * "fix" offset will be the second uint2 slot, which is after the previous sections after the header. - */ - spf->offsets[s] = sizeof(spf->header) + (sizeof(sp_file_section_t) * spf->header.sections) + sizeof(uint32_t); - strcpy(&spf->nametab[spf->nametab_idx], name); - spf->nametab_idx += namelen; - - return ++spf->header.sections; -} - -int spfw_finalize_header(sp_file_t *spf) -{ - uint32_t size; - if (spf->state != -1) - return -1; - - size = sizeof(sp_file_section_t) * spf->header.sections; - - spf->header.stringtab = sizeof(spf->header) + size; - spf->header.dataoffs = spf->header.stringtab + spf->nametab_idx; - if (spf->funcs.fnWrite(&spf->header, sizeof(spf->header), 1, spf->handle) != 1) - return -1; - if (spf->funcs.fnWrite(spf->sections, sizeof(sp_file_section_t), spf->header.sections, spf->handle) != - spf->header.sections) - return -1; - if (spf->funcs.fnWrite(spf->nametab, sizeof(char), spf->nametab_idx, spf->handle) != spf->nametab_idx) - return -1; - spf->curoffs = spf->funcs.fnGetPos(spf->handle); - spf->lastsection = spf->curoffs; - spf->state++; - - return 0; -} - -int spfw_next_section(sp_file_t *spf) -{ - uint8_t s; - uint32_t rest[2]; - - if (spf->state < 0 || spf->state > spf->header.sections) - return -1; - - if (spf->state == (int)spf->header.sections) - return 0; - - s = (uint8_t)spf->state; - - spf->curoffs = spf->funcs.fnGetPos(spf->handle); - spf->funcs.fnSetPos(spf->handle, spf->offsets[s]); - - rest[0] = spf->lastsection; - rest[1] = spf->curoffs - spf->lastsection; - if (spf->funcs.fnWrite(rest, sizeof(uint32_t), 2, spf->handle) != 2) - return -1; - - spf->funcs.fnSetPos(spf->handle, spf->curoffs); - spf->lastsection = spf->curoffs; - - spf->state++; - - return 1; -} - -int spfw_finalize_all(sp_file_t *spf) -{ - uint8_t offs; - - if (spf->state < spf->header.sections) - return -1; - - offs = offsetof(sp_file_hdr_t, imagesize); - spf->header.disksize = spf->funcs.fnGetPos(spf->handle); - spf->header.imagesize = spf->funcs.fnGetPos(spf->handle); - spf->funcs.fnSetPos(spf->handle, offs); - spf->funcs.fnWrite(&spf->header.imagesize, sizeof(uint32_t), 1, spf->handle); - spf->funcs.fnSetPos(spf->handle, spf->header.imagesize); - - return 1; -} - -/** - * More memory file operations - */ - -void *mf_open(const char *name) -{ - return memfile_creat(name, 1024); -} - -void mf_close(void *handle) -{ - memfile_destroy((memfile_t *)handle); -} - -size_t mf_write(const void *buf, size_t size, size_t count, void *handle) -{ - if (!count) - return 0; - - if (memfile_write((memfile_t *)handle, buf, size*count)) - return count; - - return 0; -} - -size_t mf_read(void *buf, size_t size, size_t count, void *handle) -{ - return memfile_read((memfile_t *)handle, buf, size*count) / count; -} - -size_t mf_getpos(void *handle) -{ - return (long)memfile_tell((memfile_t *)handle); -} - -int mf_setpos(void *handle, size_t pos) -{ - memfile_seek((memfile_t *)handle, (long)pos); - return 1; -} - diff --git a/sourcepawn/compiler/sp_file.h b/sourcepawn/compiler/sp_file.h deleted file mode 100644 index 2a9edcbe..00000000 --- a/sourcepawn/compiler/sp_file.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef _INCLUDE_SPFILE_H -#define _INCLUDE_SPFILE_H - -#include "sp_file_headers.h" - -/** - * Used for overwriting writing routines. - */ -typedef struct sp_writefuncs_s -{ - void *(*fnOpen)(const char *); /* filename, returns handle */ - void (*fnClose)(void *); /* handle */ - /* buffer, size, count, handle, returns count written */ - size_t (*fnWrite)(const void *, size_t, size_t, void *); - /* buffer, size, count, handle, returns count read */ - size_t (*fnRead)(void *, size_t, size_t, void *); - /* returns current position from start */ - size_t (*fnGetPos)(void *); - /* sets current position from start, return 0 for success, nonzero for error */ - int (*fnSetPos)(void *, size_t); -} sp_writefuncs_t; - -typedef struct sp_file_s -{ - sp_file_hdr_t header; - sp_file_section_t *sections; - size_t *offsets; - sp_writefuncs_t funcs; - size_t lastsection; - size_t curoffs; - void *handle; - int state; - char *nametab; - size_t nametab_idx; -} sp_file_t; - -/** - * Creates a new SourcePawn binary file. - * You may optionally specify alternative writing functions. - */ -sp_file_t *spfw_create(const char *name, sp_writefuncs_t *optfuncs); - -/** - * Closes file handle and frees memory. - */ -void spfw_destroy(sp_file_t *spf); - -/** - * Adds a section name to the header. - * Only valid BEFORE finalization. - * Returns the number of sections, or 0 on failure. - */ -uint8_t spfw_add_section(sp_file_t *spf, const char *name); - -/** - * Finalizes the section header. - * This means no more sections can be added after this call. - * Also, aligns the writer to the first section. - * Returns 0 on success, nonzero on error. - */ -int spfw_finalize_header(sp_file_t *spf); - -/** - * Finalizes the current section and advances to the next. - * In order for this to be accurate, the file pointer must - * reside at the end before calling this, because the size - * is calculated by differencing with the last known offset. - * Returns 1 if there are more sections left, 0 otherwise. - * Returns -1 if the file state is wrong. - */ -int spfw_next_section(sp_file_t *spf); - -/** - * Finalizes all sections. - * Cannot be called until all sections are used. - * Must be called with the file pointer at the end. - * Also does compression! - */ -int spfw_finalize_all(sp_file_t *spf); - -#endif //_INCLUDE_SPFILE_H diff --git a/sourcepawn/compiler/sp_file_headers.h b/sourcepawn/compiler/sp_file_headers.h deleted file mode 100644 index 13d556fa..00000000 --- a/sourcepawn/compiler/sp_file_headers.h +++ /dev/null @@ -1,291 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourcePawn - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#ifndef _INCLUDE_SPFILE_HEADERS_H -#define _INCLUDE_SPFILE_HEADERS_H - -/** - * @file sp_file_headers.h - * @brief Defines the structure present in a SourcePawn compiled binary. - * - * Note: These structures should be 1-byte packed to match the file format. - */ - -#include -#include - -#define SPFILE_MAGIC 0x53504646 /**< Source Pawn File Format (SPFF) */ -#define SPFILE_VERSION 0x0102 /**< File format version */ - -//:TODO: better compiler/nix support -#if defined __GNUC__ - #pragma pack(1) /* structures must be packed (byte-aligned) */ -#else - #pragma pack(push) - #pragma pack(1) /* structures must be packed (byte-aligned) */ -#endif - -#define SPFILE_COMPRESSION_NONE 0 /**< No compression in file */ -#define SPFILE_COMPRESSION_GZ 1 /**< GZ compression */ - -/** - * @brief File section header format. - */ -typedef struct sp_file_section_s -{ - uint32_t nameoffs; /**< Relative offset into global string table */ - uint32_t dataoffs; /**< Offset into the data section of the file */ - uint32_t size; /**< Size of the section's entry in the data section */ -} sp_file_section_t; - -/** - * @brief File header format. If compression is 0, then disksize may be 0 - * to mean that only the imagesize is needed. - */ -typedef struct sp_file_hdr_s -{ - uint32_t magic; /**< Magic number */ - uint16_t version; /**< Version code */ - uint8_t compression;/**< Compression algorithm */ - uint32_t disksize; /**< Size on disk */ - uint32_t imagesize; /**< Size in memory */ - uint8_t sections; /**< Number of sections */ - uint32_t stringtab; /**< Offset to string table */ - uint32_t dataoffs; /**< Offset to file proper (any compression starts here) */ -} sp_file_hdr_t; - -#define SP_FLAG_DEBUG (1<<0) /**< Debug information is present in the file */ - -#define SP_CODEVERS_JIT1 9 /**< Code version for JIT1 */ -#define SP_CODEVERS_JIT2 10 /**< Code version for JIT2 */ - -/** - * @brief File-encoded format of the ".code" section. - */ -typedef struct sp_file_code_s -{ - uint32_t codesize; /**< Codesize in bytes */ - uint8_t cellsize; /**< Cellsize in bytes */ - uint8_t codeversion; /**< Version of opcodes supported */ - uint16_t flags; /**< Flags */ - uint32_t main; /**< Address to "main," if any */ - uint32_t code; /**< Relative offset to code */ -} sp_file_code_t; - -/** - * @brief File-encoded format of the ".data" section. - */ -typedef struct sp_file_data_s -{ - uint32_t datasize; /**< Size of data section in memory */ - uint32_t memsize; /**< Total mem required (includes data) */ - uint32_t data; /**< File offset to data (helper) */ -} sp_file_data_t; - -/** - * @brief File-encoded format of the ".publics" section. - */ -typedef struct sp_file_publics_s -{ - uint32_t address; /**< Address relative to code section */ - uint32_t name; /**< Index into nametable */ -} sp_file_publics_t; - -/** - * @brief File-encoded format of the ".natives" section. - */ -typedef struct sp_file_natives_s -{ - uint32_t name; /**< Index into nametable */ -} sp_file_natives_t; - -/** - * @brief File-encoded format of the ".pubvars" section. - */ -typedef struct sp_file_pubvars_s -{ - uint32_t address; /**< Address relative to the DAT section */ - uint32_t name; /**< Index into nametable */ -} sp_file_pubvars_t; - -/** - * @brief File-encoded tag info. - */ -typedef struct sp_file_tag_s -{ - uint32_t tag_id; /**< Tag ID from compiler */ - uint32_t name; /**< Index into nametable */ -} sp_file_tag_t; - -/** - * @brief File-encoded debug information table. - */ -typedef struct sp_fdbg_info_s -{ - uint32_t num_files; /**< number of files */ - uint32_t num_lines; /**< number of lines */ - uint32_t num_syms; /**< number of symbols */ - uint32_t num_arrays; /**< number of symbols which are arrays */ -} sp_fdbg_info_t; - -/** - * @brief File-encoded debug file table. - */ -typedef struct sp_fdbg_file_s -{ - uint32_t addr; /**< Address into code */ - uint32_t name; /**< Offset into debug nametable */ -} sp_fdbg_file_t; - -/** - * @brief File-encoded debug line table. - */ -typedef struct sp_fdbg_line_s -{ - uint32_t addr; /**< Address into code */ - uint32_t line; /**< Line number */ -} sp_fdbg_line_t; - -#define SP_SYM_VARIABLE 1 /**< Cell that has an address and that can be fetched directly (lvalue) */ -#define SP_SYM_REFERENCE 2 /**< VARIABLE, but must be dereferenced */ -#define SP_SYM_ARRAY 3 /**< Symbol is an array */ -#define SP_SYM_REFARRAY 4 /**< An array passed by reference (i.e. a pointer) */ -#define SP_SYM_FUNCTION 9 /**< Symbol is a function */ -#define SP_SYM_VARARGS 11 /**< Variadic argument start. */ - -/** - * @brief File-encoded debug symbol information. - */ -typedef struct sp_fdbg_symbol_s -{ - int32_t addr; /**< Address rel to DAT or stack frame */ - int16_t tagid; /**< Tag id */ - uint32_t codestart; /**< Start scope validity in code */ - uint32_t codeend; /**< End scope validity in code */ - uint8_t ident; /**< Variable type */ - uint8_t vclass; /**< Scope class (local vs global) */ - uint16_t dimcount; /**< Dimension count (for arrays) */ - uint32_t name; /**< Offset into debug nametable */ -} sp_fdbg_symbol_t; - -/** - * @brief File-encoded debug symbol array dimension info. - */ -typedef struct sp_fdbg_arraydim_s -{ - int16_t tagid; /**< Tag id */ - uint32_t size; /**< Size of dimension */ -} sp_fdbg_arraydim_t; - -/** Typedef for .names table */ -typedef char * sp_file_nametab_t; - -/** - * @brief File encoding for the dbg.natives table. - * - * This header is followed by variable length entries of sp_fdbg_native. - */ -typedef struct sp_fdbg_ntvtab_s -{ - uint32_t num_entries; /**< Number of entries. */ -} sp_fdbg_ntvtab_t; - -#define SP_NTVDBG_VARARGS (1<<0) /**< Formal args are followed by '...' */ - -/** - * @brief File encoding of native debug info. - * - * Each entry is followed by an sp_fdbg_ntvarg_t for each narg. - */ -typedef struct sp_fdbg_native_s -{ - uint32_t index; /**< Native index in the plugin. */ - uint32_t name; /**< Offset into debug nametable. */ - int16_t tagid; /**< Return tag. */ - uint16_t nargs; /**< Number of formal arguments. */ -} sp_fdbg_native_t; - -/** - * @brief File encoding of native arguments. - * - * Each entry is followed by an sp_fdbg_arraydim_t for each dimcount. - */ -typedef struct fp_fdbg_ntvarg_s -{ - uint8_t ident; /**< Variable type */ - int16_t tagid; /**< Tag id */ - uint16_t dimcount; /**< Dimension count (for arrays) */ - uint32_t name; /**< Offset into debug nametable */ -} sp_fdbg_ntvarg_t; - -#if defined __GNUC__ -#pragma pack() /* reset default packing */ -#else -#pragma pack(pop) /* reset previous packing */ -#endif - -/** - * Okay, my mistake here. I apologize. I changed the packing by accident and there is no - * version bump aside from the presence of the native debug table. Cat's out of the bag - * for SourceMod and we have no choice but to shim compat for the old version. For people - * parsing plugins on their own, use the presence of the native debug table to decide this. - * If there are no natives (really, there is a very very low chance of this), heuristics - * might be necessary. I've bumped the version to 0x0102 but there may have been plugins - * in the 0x0101 window with no natives. - */ - -/** - * @brief Unpacked file-encoded debug symbol array dimension info. - */ -typedef struct sp_u_fdbg_arraydim_s -{ - int16_t tagid; /**< Tag id */ - uint32_t size; /**< Size of dimension */ -} sp_u_fdbg_arraydim_t; - -/** - * @brief Unpacked file-encoded debug symbol information. - */ -typedef struct sp_u_fdbg_symbol_s -{ - int32_t addr; /**< Address rel to DAT or stack frame */ - int16_t tagid; /**< Tag id */ - uint32_t codestart; /**< Start scope validity in code */ - uint32_t codeend; /**< End scope validity in code */ - uint8_t ident; /**< Variable type */ - uint8_t vclass; /**< Scope class (local vs global) */ - uint16_t dimcount; /**< Dimension count (for arrays) */ - uint32_t name; /**< Offset into debug nametable */ -} sp_u_fdbg_symbol_t; - - -#endif //_INCLUDE_SPFILE_HEADERS_H - diff --git a/sourcepawn/compiler/sp_symhash.cpp b/sourcepawn/compiler/sp_symhash.cpp index b843698c..1b50b0ae 100644 --- a/sourcepawn/compiler/sp_symhash.cpp +++ b/sourcepawn/compiler/sp_symhash.cpp @@ -2,7 +2,6 @@ #include #include #include -#include "sp_file_headers.h" #include "sc.h" #include "sp_symhash.h" #include diff --git a/sourcepawn/compiler/string-pool.h b/sourcepawn/compiler/string-pool.h new file mode 100644 index 00000000..94b21655 --- /dev/null +++ b/sourcepawn/compiler/string-pool.h @@ -0,0 +1,139 @@ +/* vim: set ts=2 sw=2 tw=99 et: + * + * Copyright (C) 2012-2014 AlliedModders LLC, David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_string_pool_h_ +#define _include_jitcraft_string_pool_h_ + +#include +#include +#include + +namespace ke { + +// Wrapper around AString, since we might want to remove charfs() in a GC-safe +// implementation. +class Atom +{ + friend class StringPool; + + private: + Atom(const char *str, size_t len) + : str_(str, len) + { + } + + public: + size_t length() const { + return str_.length(); + } + const char *chars() const { + return str_.chars(); + } + + private: + AString str_; +}; + +class CharsAndLength +{ + public: + CharsAndLength() + : str_(nullptr), + length_(0) + { + } + + CharsAndLength(const char *str, size_t length) + : str_(str), + length_(length) + { + } + + const char *str() const { + return str_; + } + size_t length() const { + return length_; + } + + private: + const char *str_; + size_t length_; +}; + +class StringPool +{ + public: + StringPool() + : table_(SystemAllocatorPolicy()) + { + init(); + } + + ~StringPool() + { + if (!table_.elements()) + return; + for (Table::iterator i(&table_); !i.empty(); i.next()) + delete *i; + } + + bool init() { + return table_.init(256); + } + + Atom *add(const char *str, size_t length) { + CharsAndLength chars(str, length); + Table::Insert p = table_.findForAdd(chars); + if (!p.found() && !table_.add(p, new Atom(str, length))) + return nullptr; + return *p; + } + + Atom *add(const char *str) { + return add(str, strlen(str)); + } + + private: + struct Policy { + typedef Atom *Payload; + + static uint32_t hash(const char *key) { + return HashCharSequence(key, strlen(key)); + } + + static uint32_t hash(const CharsAndLength &key) { + return HashCharSequence(key.str(), key.length()); + } + + static bool matches(const CharsAndLength &key, const Payload &e) { + if (key.length() != e->length()) + return false; + return strcmp(key.str(), e->chars()) == 0; + } + }; + + typedef HashTable Table; + + private: + Table table_; +}; + +} // namespace ke + +#endif // _include_jitcraft_string_pool_h_ diff --git a/sourcepawn/include/smx/smx-headers.h b/sourcepawn/include/smx/smx-headers.h index dc9d7dcf..325f16c8 100644 --- a/sourcepawn/include/smx/smx-headers.h +++ b/sourcepawn/include/smx/smx-headers.h @@ -39,9 +39,8 @@ struct SmxConsts static const uint32_t FILE_MAGIC = 0x53504646; // File format verison number. - // 0x0101 - Initial version used by spcomp1 and SourceMod 1.0. - // 0x0102 - Used by spcomp1 and SourceMod 1.1+. - // 0x0103 - Used by SourceMod 1.7+. + // 0x0101 - SourcePawn 1.0; initial version used by SourceMod 1.0. + // 0x0102 - SourcePawn 1.1; used by SourceMod 1.1+. // 0x0200 - Used by spcomp2. // // The major version bits (8-15) indicate a product number. Consumers should @@ -49,8 +48,10 @@ struct SmxConsts // // The minor version bits (0-7) indicate a compatibility revision. Any minor // version higher than the current version should be rejected. - static const uint16_t SP1_VERSION_MIN = 0x0101; - static const uint16_t SP1_VERSION_MAX = 0x0103; + static const uint16_t SP1_VERSION_1_0 = 0x0101; + static const uint16_t SP1_VERSION_1_1 = 0x0102; + static const uint16_t SP1_VERSION_MIN = SP1_VERSION_1_0; + static const uint16_t SP1_VERSION_MAX = SP1_VERSION_1_1; static const uint16_t SP2_VERSION_MIN = 0x0200; static const uint16_t SP2_VERSION_MAX = 0x0200; @@ -94,18 +95,28 @@ typedef struct sp_file_hdr_s uint32_t magic; uint16_t version; - // Compression algorithm. If the file is not compressed, then imagesize and - // disksize are the same value, and dataoffs is 0. + // disksize, imagesize, and dataoffs (at the end) describe a region of the + // file that may be compressed. The region must occur after the initial + // sp_file_hdr_t header. For SourceMod compatibility, the meanings are as + // follows. // - // The start of the compressed region is indicated by dataoffs. The length - // of the compressed region is (disksize - dataoffs). The amount of memory - // required to hold the decompressed bytes is (imagesize - dataoffs). The - // compressed region should be expanded in-place. That is, bytes before - // "dataoffs" should be retained, and the decompressed region should be - // appended. + // Without compression: + // imagesize is the amount of bytes that must be read into memory to parse + // the SMX container, starting from the first byte of the file. + // disksize is undefined. + // dataoffs is undefined. // - // |imagesize| is the amount of memory required to hold the entire container - // in memory. + // With compression: + // dataoffs is an offset to the start of the compression region. + // disksize is the length of the compressed region, in bytes, plus dataoffs. + // imagesize is the size of the entire SMX container after decompression. + // + // This means that at least |imagesize-dataoffs| must be allocated to + // decompress, and the compressed region's length is |datasize-dataoffs|. + // + // The compressed region should always be expanded "in-place". That is, to + // parse the container, the compressed bytes should be replaced with + // decompressed bytes. // // Note: This scheme may seem odd. It's a combination of historical debt and // previously unspecified behavior. The original .amx file format contained diff --git a/sourcepawn/jit/Makefile.shell b/sourcepawn/jit/Makefile.shell index ad2fd0ae..127f0aaa 100644 --- a/sourcepawn/jit/Makefile.shell +++ b/sourcepawn/jit/Makefile.shell @@ -53,7 +53,7 @@ LINK = -m32 -lm -lpthread -lrt INCLUDE = -I. -I.. -I$(SMSDK)/public -I$(SMSDK)/public/jit -I$(SMSDK)/public/jit/x86 \ -I$(SMSDK)/public/sourcepawn -I$(MMSOURCE17)/core/sourcehook -I$(SMSDK)/knight/shared -Ix86 \ - -I$(SMSDK)/public/amtl + -I$(SMSDK)/public/amtl -I$(SMSDK)/sourcepawn/include CFLAGS += -D_LINUX -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp \ -D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp -Wall -DHAVE_STDINT_H \ From 98ec07a419157ed3d7900583358df52e61b1c221 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sat, 23 Aug 2014 13:21:55 -0700 Subject: [PATCH 3/6] Add debug info tables to smxbuilder. --- sourcepawn/compiler/memory-buffer.h | 11 +- sourcepawn/compiler/sc6.cpp | 402 ++++++++++++---------------- sourcepawn/compiler/smx-builder.h | 54 +++- sourcepawn/compiler/string-pool.h | 11 +- 4 files changed, 230 insertions(+), 248 deletions(-) diff --git a/sourcepawn/compiler/memory-buffer.h b/sourcepawn/compiler/memory-buffer.h index 3800f921..0a92c285 100644 --- a/sourcepawn/compiler/memory-buffer.h +++ b/sourcepawn/compiler/memory-buffer.h @@ -22,7 +22,16 @@ #include #include "smx-builder.h" -class MemoryBuffer : public ke::ISmxBuffer +// Interface for SmxBuilder to blit bytes. +class ISmxBuffer +{ + public: + virtual bool write(const void *bytes, size_t len) = 0; + virtual size_t pos() const = 0; +}; + +// An in-memory buffer for SmxBuilder. +class MemoryBuffer : public ISmxBuffer { static const size_t kDefaultSize = 4096; diff --git a/sourcepawn/compiler/sc6.cpp b/sourcepawn/compiler/sc6.cpp index 88acfe30..0c2d6554 100644 --- a/sourcepawn/compiler/sc6.cpp +++ b/sourcepawn/compiler/sc6.cpp @@ -45,7 +45,7 @@ using namespace sp; using namespace ke; -static void append_dbginfo(void *fout); +static void append_debug_Tables(SmxBuilder *builder); typedef cell (*OPCODE_PROC)(Vector *buffer, char *params, cell opcode); @@ -615,10 +615,173 @@ static int sort_by_addr(const void *a1, const void *a2) return s1->addr - s2->addr; } +// Helper for parsing a debug string. Debug strings look like this: +// L:40 10 +class DebugString +{ + public: + DebugString() : kind_('\0'), str_(nullptr) + { } + DebugString(char *str) + : kind_(str[0]), + str_(str) + { + assert(str_[1] == ':'); + str_ += 2; + } + char kind() const { + return kind_; + } + ucell parse() { + return hex2long(str_, &str_); + } + char *skipspaces() { + str_ = ::skipwhitespace(str_); + return str_; + } + void expect(char c) { + assert(*str_ == c); + str_++; + } + char *skipto(char c) { + str_ = strchr(str_, c); + return str_; + } + char getc() { + return *str_++; + } + + private: + char kind_; + char *str_; +}; + +typedef SmxBlobSection SmxDebugInfoSection; +typedef SmxListSection SmxDebugLineSection; +typedef SmxListSection SmxDebugFileSection; +typedef SmxListSection SmxTagSection; +typedef SmxBlobSection SmxDebugSymbolsSection; + +static void append_debug_tables(SmxBuilder *builder, StringPool &pool) +{ + // We use a separate name table for historical reasons that are no longer + // necessary. In the future we should just alias this to ".names". + Ref names = new SmxNameTable(".dbg.strings"); + Ref info = new SmxDebugInfoSection(".dbg.info"); + Ref lines = new SmxDebugLineSection(".dbg.lines"); + Ref files = new SmxDebugFileSection(".dbg.files"); + Ref symbols = new SmxDebugSymbolsSection(".dbg.symbols"); + Ref tags = new SmxTagSection(".tags"); + + stringlist *dbgstrs = get_dbgstrings(); + + // State for tracking which file we're on. We replicate the original AMXDBG + // behavior here which excludes duplicate addresses. + ucell prev_file_addr = 0; + const char *prev_file_name = nullptr; + + // Add debug data. + for (stringlist *iter = dbgstrs; iter; iter = iter->next) { + if (iter->line[0] == '\0') + continue; + + DebugString str(iter->line); + switch (str.kind()) { + case 'F': + { + ucell codeidx = str.parse(); + if (codeidx != prev_file_addr) { + if (prev_file_name) { + sp_fdbg_file_t &entry = files->add(); + entry.addr = prev_file_addr; + entry.name = names->add(pool, prev_file_name); + } + prev_file_addr = codeidx; + } + prev_file_name = str.skipspaces(); + break; + } + + case 'L': + { + sp_fdbg_line_t &entry = lines->add(); + entry.addr = str.parse(); + entry.line = str.parse(); + break; + } + + case 'S': + { + sp_fdbg_symbol_t sym; + sp_fdbg_arraydim_t dims[sDIMEN_MAX]; + + sym.addr = str.parse(); + sym.tagid = str.parse(); + + str.skipspaces(); + str.expect(':'); + char *name = str.skipspaces(); + char *nameend = str.skipto(' '); + Atom *atom = pool.add(name, nameend - name); + + sym.codestart = str.parse(); + sym.codeend = str.parse(); + sym.ident = (char)str.parse(); + sym.vclass = (char)str.parse(); + sym.dimcount = 0; + sym.name = names->add(atom); + + info->header().num_syms++; + + str.skipspaces(); + if (str.getc() == '[') { + info->header().num_arrays++; + for (char *ptr = str.skipspaces(); *ptr != ']'; ptr = str.skipspaces()) { + dims[sym.dimcount].tagid = str.parse(); + str.skipspaces(); + str.expect(':'); + dims[sym.dimcount].size = str.parse(); + sym.dimcount++; + } + } + + symbols->add(&sym, sizeof(sym)); + symbols->add(dims, sizeof(dims[0]) * sym.dimcount); + break; + } + } + } + + // Add the last file. + if (prev_file_name) { + sp_fdbg_file_t &entry = files->add(); + entry.addr = prev_file_addr; + entry.name = names->add(pool, prev_file_name); + } + + // Build the tags table. + for (constvalue *constptr = tagname_tab.next; constptr; constptr = constptr->next) { + assert(strlen(constptr->name)>0); + + sp_file_tag_t &tag = tags->add(); + tag.tag_id = constptr->value; + tag.name = names->add(pool, constptr->name); + } + + info->header().num_files = files->count(); + info->header().num_lines = lines->count(); + + builder->add(files); + builder->add(symbols); + builder->add(lines); + builder->add(names); + builder->add(info); + builder->add(tags); +} + typedef SmxListSection SmxNativeSection; typedef SmxListSection SmxPublicSection; typedef SmxListSection SmxPubvarSection; -typedef SmxListSection SmxTagSection; typedef SmxBlobSection SmxDataSection; typedef SmxBlobSection SmxCodeSection; @@ -629,7 +792,6 @@ static void assemble_to_buffer(MemoryBuffer *buffer, void *fin) Ref natives = new SmxNativeSection(".natives"); Ref publics = new SmxPublicSection(".publics"); Ref pubvars = new SmxPubvarSection(".pubvars"); - Ref tags = new SmxTagSection(".tags"); Ref data = new SmxDataSection(".data"); Ref code = new SmxCodeSection(".code"); Ref names = new SmxNameTable(".names"); @@ -656,15 +818,6 @@ static void assemble_to_buffer(MemoryBuffer *buffer, void *fin) } } - // Build the tags table. - for (constvalue *constptr = tagname_tab.next; constptr; constptr = constptr->next) { - assert(strlen(constptr->name)>0); - - sp_file_tag_t &tag = tags->add(); - tag.tag_id = constptr->value; - tag.name = names->add(pool, constptr->name); - } - // Shuffle natives to be in address order. qsort(nativeList.buffer(), nativeList.length(), sizeof(symbol *), sort_by_addr); for (size_t i = 0; i < nativeList.length(); i++) { @@ -719,7 +872,8 @@ static void assemble_to_buffer(MemoryBuffer *buffer, void *fin) builder.add(pubvars); builder.add(natives); builder.add(names); - builder.add(tags); + append_debug_tables(&builder, pool); + builder.write(buffer); } @@ -775,225 +929,3 @@ void assemble(const char *binfname, void *fin) splat_to_binary(binfname, buffer.bytes(), buffer.size()); } - -int pc_writebin(void *handle,void *buffer,int size) { - assert(false); - return 1; -} - -static void append_dbginfo(void *fout) -{ - AMX_DBG_HDR dbghdr; - AMX_DBG_LINE dbgline; - AMX_DBG_SYMBOL dbgsym; - AMX_DBG_SYMDIM dbgidxtag[sDIMEN_MAX]; - int dim,dbgsymdim; - char *str,*prevstr,*name,*prevname; - ucell codeidx,previdx; - constvalue *constptr; - char symname[2*sNAMEMAX+16]; - int16_t id1,id2; - ucell address; - stringlist *dbgstrs = get_dbgstrings(); - stringlist *iter; - - /* header with general information */ - memset(&dbghdr, 0, sizeof dbghdr); - dbghdr.size=sizeof dbghdr; - dbghdr.magic=AMX_DBG_MAGIC; - dbghdr.file_version=CUR_FILE_VERSION; - dbghdr.amx_version=MIN_AMX_VERSION; - - dbgstrs=dbgstrs->next; - - /* first pass: collect the number of items in various tables */ - - /* file table */ - previdx=0; - prevstr=NULL; - prevname=NULL; - for (iter=dbgstrs; iter!=NULL; iter=iter->next) { - str = iter->line; - assert(str!=NULL); - assert(str[0]!='\0' && str[1]==':'); - if (str[0]=='F') { - codeidx=hex2long(str+2,&name); - if (codeidx!=previdx) { - if (prevstr!=NULL) { - assert(prevname!=NULL); - dbghdr.files++; - dbghdr.size+=sizeof(cell)+strlen(prevname)+1; - } /* if */ - previdx=codeidx; - } /* if */ - prevstr=str; - prevname=skipwhitespace(name); - } /* if */ - } /* for */ - if (prevstr!=NULL) { - assert(prevname!=NULL); - dbghdr.files++; - dbghdr.size+=sizeof(cell)+strlen(prevname)+1; - } /* if */ - - /* line number table */ - for (iter=dbgstrs; iter!=NULL; iter=iter->next) { - str = iter->line; - assert(str!=NULL); - assert(str[0]!='\0' && str[1]==':'); - if (str[0]=='L') { - dbghdr.lines++; - dbghdr.size+=sizeof(AMX_DBG_LINE); - } /* if */ - } /* for */ - - /* symbol table */ - for (iter=dbgstrs; iter!=NULL; iter=iter->next) { - str = iter->line; - assert(str!=NULL); - assert(str[0]!='\0' && str[1]==':'); - if (str[0]=='S') { - dbghdr.symbols++; - name=strchr(str+2,':'); - assert(name!=NULL); - dbghdr.size+=sizeof(AMX_DBG_SYMBOL)+strlen(skipwhitespace(name+1)); - if ((prevstr=strchr(name,'['))!=NULL) - while ((prevstr=strchr(prevstr+1,':'))!=NULL) - dbghdr.size+=sizeof(AMX_DBG_SYMDIM); - } /* if */ - } /* for */ - - /* tag table */ - for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { - assert(strlen(constptr->name)>0); - dbghdr.tags++; - dbghdr.size+=sizeof(AMX_DBG_TAG)+strlen(constptr->name); - } /* for */ - - /* automaton table */ - for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) { - assert((constptr->index==0 && strlen(constptr->name)==0) || strlen(constptr->name)>0); - dbghdr.automatons++; - dbghdr.size+=sizeof(AMX_DBG_MACHINE)+strlen(constptr->name); - } /* for */ - - /* state table */ - for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) { - assert(strlen(constptr->name)>0); - dbghdr.states++; - dbghdr.size+=sizeof(AMX_DBG_STATE)+strlen(constptr->name); - } /* for */ - - - /* pass 2: generate the tables */ - writeerror |= !pc_writebin(fout,&dbghdr,sizeof dbghdr); - - /* file table */ - previdx=0; - prevstr=NULL; - prevname=NULL; - for (iter=dbgstrs; iter!=NULL; iter=iter->next) { - str = iter->line; - assert(str[0]!='\0' && str[1]==':'); - if (str[0]=='F') { - codeidx=hex2long(str+2,&name); - if (codeidx!=previdx) { - if (prevstr!=NULL) { - assert(prevname!=NULL); - writeerror |= !pc_writebin(fout,&previdx,sizeof previdx); - writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1); - } /* if */ - previdx=codeidx; - } /* if */ - prevstr=str; - prevname=skipwhitespace(name); - } /* if */ - } /* for */ - if (prevstr!=NULL) { - assert(prevname!=NULL); - writeerror |= !pc_writebin(fout,&previdx,sizeof previdx); - writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1); - } /* if */ - - /* line number table */ - for (iter=dbgstrs; iter!=NULL; iter=iter->next) { - str = iter->line; - assert(str!=NULL); - assert(str[0]!='\0' && str[1]==':'); - if (str[0]=='L') { - dbgline.address=hex2long(str+2,&str); - dbgline.line=(int32_t)hex2long(str,NULL); - writeerror |= !pc_writebin(fout,&dbgline,sizeof dbgline); - } /* if */ - } /* for */ - - /* symbol table */ - for (iter=dbgstrs; iter!=NULL; iter=iter->next) { - str = iter->line; - assert(str!=NULL); - assert(str[0]!='\0' && str[1]==':'); - if (str[0]=='S') { - dbgsym.address=hex2long(str+2,&str); - dbgsym.tag=(int16_t)hex2long(str,&str); - str=skipwhitespace(str); - assert(*str==':'); - name=skipwhitespace(str+1); - str=strchr(name,' '); - assert(str!=NULL); - assert((int)(str-name)next) { - assert(strlen(constptr->name)>0); - id1=(int16_t)(constptr->value & TAGMASK); - writeerror |= !pc_writebin(fout,&id1,sizeof id1); - writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); - } /* for */ - - /* automaton table */ - for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) { - assert((constptr->index==0 && strlen(constptr->name)==0) || strlen(constptr->name)>0); - id1=(int16_t)constptr->index; - address=(ucell)constptr->value; - writeerror |= !pc_writebin(fout,&id1,sizeof id1); - writeerror |= !pc_writebin(fout,&address,sizeof address); - writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); - } /* for */ - - /* state table */ - for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) { - assert(strlen(constptr->name)>0); - id1=(int16_t)constptr->value; - id2=(int16_t)constptr->index; - address=(ucell)constptr->value; - writeerror |= !pc_writebin(fout,&id1,sizeof id1); - writeerror |= !pc_writebin(fout,&id2,sizeof id2); - writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); - } /* for */ - - delete_dbgstringtable(); -} diff --git a/sourcepawn/compiler/smx-builder.h b/sourcepawn/compiler/smx-builder.h index 1b165c52..57b1eb6e 100644 --- a/sourcepawn/compiler/smx-builder.h +++ b/sourcepawn/compiler/smx-builder.h @@ -24,16 +24,11 @@ #include #include #include "string-pool.h" +#include "memory-buffer.h" namespace ke { -class ISmxBuffer -{ - public: - virtual bool write(const void *bytes, size_t len) = 0; - virtual size_t pos() const = 0; -}; - +// An SmxSection is a named blob of data. class SmxSection : public Refcounted { public: @@ -53,13 +48,19 @@ class SmxSection : public Refcounted AString name_; }; +// An SmxBlobSection is a section that has some kind of header structure +// (specified as a template parameter), and then an arbitrary run of bytes +// immediately after. template class SmxBlobSection : public SmxSection { public: SmxBlobSection(const char *name) - : SmxSection(name) + : SmxSection(name), + extra_(nullptr), + extra_len_(0) { + memset(&t_, 0, sizeof(t_)); } T &header() { @@ -72,6 +73,8 @@ class SmxBlobSection : public SmxSection bool write(ISmxBuffer *buf) KE_OVERRIDE { if (!buf->write(&t_, sizeof(t_))) return false; + if (!extra_len_) + return true; return buf->write(extra_, extra_len_); } size_t length() const KE_OVERRIDE { @@ -84,6 +87,32 @@ class SmxBlobSection : public SmxSection size_t extra_len_; }; +// An SmxBlobSection without headers. +template <> +class SmxBlobSection : public SmxSection +{ + public: + SmxBlobSection(const char *name) + : SmxSection(name) + { + } + + void add(void *bytes, size_t len) { + buffer_.write(bytes, len); + } + bool write(ISmxBuffer *buf) KE_OVERRIDE { + return buf->write(buffer_.bytes(), buffer_.size()); + } + size_t length() const KE_OVERRIDE { + return buffer_.size(); + } + + private: + MemoryBuffer buffer_; +}; + +// An SmxListSection is a section that is a simple table of uniformly-sized +// structures. It has no header of its own. template class SmxListSection : public SmxSection { @@ -104,18 +133,23 @@ class SmxListSection : public SmxSection return buf->write(list_.buffer(), list_.length() * sizeof(T)); } size_t length() const KE_OVERRIDE { - return list_.length() * sizeof(T); + return count() * sizeof(T); + } + size_t count() const { + return list_.length(); } private: Vector list_; }; +// A name table is a blob of zero-terminated strings. Strings are entered +// into the table as atoms, so duplicate stings are not emitted. class SmxNameTable : public SmxSection { public: SmxNameTable(const char *name) - : SmxSection(".names"), + : SmxSection(name), buffer_size_(0) { name_table_.init(64); diff --git a/sourcepawn/compiler/string-pool.h b/sourcepawn/compiler/string-pool.h index 94b21655..63ef10e0 100644 --- a/sourcepawn/compiler/string-pool.h +++ b/sourcepawn/compiler/string-pool.h @@ -19,14 +19,19 @@ #ifndef _include_jitcraft_string_pool_h_ #define _include_jitcraft_string_pool_h_ +// string-pool is a collection of helpers to atomize/internalize strings. The +// StringPool class provides Atom objects which can be used for efficiently +// handling string sets or dictionaries. + #include #include #include namespace ke { -// Wrapper around AString, since we might want to remove charfs() in a GC-safe -// implementation. +// An Atom is a string that has a unique instance. That is, any Atom("a") is +// guaranteed to be pointer-equivalent to another Atom("a"), as long as they +// originated from the same StringPool. class Atom { friend class StringPool; @@ -49,6 +54,7 @@ class Atom AString str_; }; +// Helper class to use as a key for hash table lookups. class CharsAndLength { public: @@ -76,6 +82,7 @@ class CharsAndLength size_t length_; }; +// Atom dictionary. class StringPool { public: From 078ee764919ea6152d63db71961ae89bea8debc4 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sat, 23 Aug 2014 13:40:50 -0700 Subject: [PATCH 4/6] Re-add .dbg.natives. --- sourcepawn/compiler/sc6.cpp | 46 +++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/sourcepawn/compiler/sc6.cpp b/sourcepawn/compiler/sc6.cpp index 0c2d6554..782da306 100644 --- a/sourcepawn/compiler/sc6.cpp +++ b/sourcepawn/compiler/sc6.cpp @@ -45,8 +45,6 @@ using namespace sp; using namespace ke; -static void append_debug_Tables(SmxBuilder *builder); - typedef cell (*OPCODE_PROC)(Vector *buffer, char *params, cell opcode); typedef struct { @@ -661,8 +659,10 @@ typedef SmxListSection SmxDebugLineSection; typedef SmxListSection SmxDebugFileSection; typedef SmxListSection SmxTagSection; typedef SmxBlobSection SmxDebugSymbolsSection; +typedef SmxBlobSection SmxDebugNativesSection; +typedef Vector SymbolList; -static void append_debug_tables(SmxBuilder *builder, StringPool &pool) +static void append_debug_tables(SmxBuilder *builder, StringPool &pool, SymbolList &nativeList) { // We use a separate name table for historical reasons that are no longer // necessary. In the future we should just alias this to ".names". @@ -671,6 +671,7 @@ static void append_debug_tables(SmxBuilder *builder, StringPool &pool) Ref lines = new SmxDebugLineSection(".dbg.lines"); Ref files = new SmxDebugFileSection(".dbg.files"); Ref symbols = new SmxDebugSymbolsSection(".dbg.symbols"); + Ref natives = new SmxDebugNativesSection(".dbg.natives"); Ref tags = new SmxTagSection(".tags"); stringlist *dbgstrs = get_dbgstrings(); @@ -768,12 +769,49 @@ static void append_debug_tables(SmxBuilder *builder, StringPool &pool) tag.name = names->add(pool, constptr->name); } + // Finish up debug header statistics. info->header().num_files = files->count(); info->header().num_lines = lines->count(); + // Write natives. + sp_fdbg_ntvtab_t natives_header; + natives_header.num_entries = nativeList.length(); + natives->add(&natives_header, sizeof(natives_header)); + + for (size_t i = 0; i < nativeList.length(); i++) { + symbol *sym = nativeList[i]; + + sp_fdbg_native_t info; + info.index = i; + info.name = names->add(pool, sym->name); + info.tagid = sym->tag; + info.nargs = 0; + for (arginfo *arg = sym->dim.arglist; arg->ident; arg++) + info.nargs++; + natives->add(&info, sizeof(info)); + + for (arginfo *arg = sym->dim.arglist; arg->ident; arg++) { + sp_fdbg_ntvarg_t argout; + argout.ident = arg->ident; + argout.tagid = arg->tags[0]; + argout.dimcount = arg->numdim; + argout.name = names->add(pool, arg->name); + natives->add(&argout, sizeof(argout)); + + for (int j = 0; j < argout.dimcount; j++) { + sp_fdbg_arraydim_t dim; + dim.tagid = arg->idxtag[j]; + dim.size = arg->dim[j]; + natives->add(&dim, sizeof(dim)); + } + } + } + + // Add these in the same order SourceMod 1.6 added them. builder->add(files); builder->add(symbols); builder->add(lines); + builder->add(natives); builder->add(names); builder->add(info); builder->add(tags); @@ -872,7 +910,7 @@ static void assemble_to_buffer(MemoryBuffer *buffer, void *fin) builder.add(pubvars); builder.add(natives); builder.add(names); - append_debug_tables(&builder, pool); + append_debug_tables(&builder, pool, nativeList); builder.write(buffer); } From d230711e4dc3829b3f3d2c701f5b451b2077fb74 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sat, 23 Aug 2014 19:26:51 -0700 Subject: [PATCH 5/6] Bump SourcePawn version to 1.7 to match SourceMod. --- sourcepawn/compiler/sc6.cpp | 4 ++-- sourcepawn/include/smx/smx-headers.h | 14 ++++++++------ sourcepawn/include/smx/{smx-v2.h => smx-rtti.h} | 0 sourcepawn/include/smx/smx-v1.h | 4 ++++ sourcepawn/jit/BaseRuntime.cpp | 4 ++-- sourcepawn/jit/engine2.cpp | 2 +- 6 files changed, 17 insertions(+), 11 deletions(-) rename sourcepawn/include/smx/{smx-v2.h => smx-rtti.h} (100%) diff --git a/sourcepawn/compiler/sc6.cpp b/sourcepawn/compiler/sc6.cpp index 782da306..042aa8e3 100644 --- a/sourcepawn/compiler/sc6.cpp +++ b/sourcepawn/compiler/sc6.cpp @@ -882,8 +882,8 @@ static void assemble_to_buffer(MemoryBuffer *buffer, void *fin) // Set up the code section. code->header().codesize = code_buffer.length() * sizeof(cell); code->header().cellsize = sizeof(cell); - code->header().codeversion = SmxConsts::CODE_VERSION_JIT2; - code->header().flags = 0; // :TODO: CODEFLAG_DEBUG; + code->header().codeversion = SmxConsts::CODE_VERSION_JIT_1_1; + code->header().flags = CODEFLAG_DEBUG; code->header().main = 0; code->header().code = sizeof(sp_file_code_t); code->setBlob((uint8_t *)code_buffer.buffer(), code_buffer.length() * sizeof(cell)); diff --git a/sourcepawn/include/smx/smx-headers.h b/sourcepawn/include/smx/smx-headers.h index 325f16c8..2fb94235 100644 --- a/sourcepawn/include/smx/smx-headers.h +++ b/sourcepawn/include/smx/smx-headers.h @@ -50,8 +50,9 @@ struct SmxConsts // version higher than the current version should be rejected. static const uint16_t SP1_VERSION_1_0 = 0x0101; static const uint16_t SP1_VERSION_1_1 = 0x0102; + static const uint16_t SP1_VERSION_1_7 = 0x0107; static const uint16_t SP1_VERSION_MIN = SP1_VERSION_1_0; - static const uint16_t SP1_VERSION_MAX = SP1_VERSION_1_1; + static const uint16_t SP1_VERSION_MAX = SP1_VERSION_1_7; static const uint16_t SP2_VERSION_MIN = 0x0200; static const uint16_t SP2_VERSION_MAX = 0x0200; @@ -59,11 +60,12 @@ struct SmxConsts static const uint8_t FILE_COMPRESSION_NONE = 0; static const uint8_t FILE_COMPRESSION_GZ = 1; - // SourcePawn 1.0. - static const uint8_t CODE_VERSION_JIT1 = 9; - - // SourcePawn 1.1. - static const uint8_t CODE_VERSION_JIT2 = 10; + // SourcePawn 1. + static const uint8_t CODE_VERSION_JIT_1_0 = 9; + static const uint8_t CODE_VERSION_JIT_1_1 = 10; + static const uint8_t CODE_VERSION_JIT_1_7 = 11; + static const uint8_t CODE_VERSION_SP1_MIN = CODE_VERSION_JIT_1_0; + static const uint8_t CODE_VERSION_SP1_MAX = CODE_VERSION_JIT_1_1; // For SP1 consumers, the container version may not be checked, but usually // the code version is. This constant allows newer containers to be rejected diff --git a/sourcepawn/include/smx/smx-v2.h b/sourcepawn/include/smx/smx-rtti.h similarity index 100% rename from sourcepawn/include/smx/smx-v2.h rename to sourcepawn/include/smx/smx-rtti.h diff --git a/sourcepawn/include/smx/smx-v1.h b/sourcepawn/include/smx/smx-v1.h index 3ae95029..7ea2392e 100644 --- a/sourcepawn/include/smx/smx-v1.h +++ b/sourcepawn/include/smx/smx-v1.h @@ -161,6 +161,10 @@ typedef struct sp_fdbg_ntvarg_s uint32_t name; /**< Offset into debug nametable */ } sp_fdbg_ntvarg_t; +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// DO NOT DEFINE NEW STRUCTURES BELOW. +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + #if defined __GNUC__ # pragma pack() /* reset default packing */ #else diff --git a/sourcepawn/jit/BaseRuntime.cpp b/sourcepawn/jit/BaseRuntime.cpp index fcf7fbf7..2fade850 100644 --- a/sourcepawn/jit/BaseRuntime.cpp +++ b/sourcepawn/jit/BaseRuntime.cpp @@ -172,9 +172,9 @@ int BaseRuntime::CreateFromMemory(sp_file_hdr_t *hdr, uint8_t *base) if (!(m_plugin.pcode) && !strcmp(nameptr, ".code")) { sp_file_code_t *cod = (sp_file_code_t *)(base + secptr->dataoffs); - if (cod->codeversion < SmxConsts::CODE_VERSION_JIT1) + if (cod->codeversion < SmxConsts::CODE_VERSION_SP1_MIN) return SP_ERROR_CODE_TOO_OLD; - if (cod->codeversion > SmxConsts::CODE_VERSION_JIT2) + if (cod->codeversion > SmxConsts::CODE_VERSION_SP1_MAX) return SP_ERROR_CODE_TOO_NEW; m_plugin.pcode = base + secptr->dataoffs + cod->code; diff --git a/sourcepawn/jit/engine2.cpp b/sourcepawn/jit/engine2.cpp index e5a8b8c2..2c1b73f9 100644 --- a/sourcepawn/jit/engine2.cpp +++ b/sourcepawn/jit/engine2.cpp @@ -147,7 +147,7 @@ void SourcePawnEngine2::DestroyFakeNative(SPVM_NATIVE_FUNC func) const char *SourcePawnEngine2::GetEngineName() { - return "SourcePawn 1.3, jit-x86"; + return "SourcePawn 1.7, jit-x86"; } const char *SourcePawnEngine2::GetVersionString() From 5b85f41fec1db656ff15c05e289a3dcaaa2114b3 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sun, 7 Sep 2014 15:02:09 -0700 Subject: [PATCH 6/6] Use the correct string table for tag names. --- sourcepawn/compiler/sc6.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sourcepawn/compiler/sc6.cpp b/sourcepawn/compiler/sc6.cpp index 042aa8e3..1c6be5bb 100644 --- a/sourcepawn/compiler/sc6.cpp +++ b/sourcepawn/compiler/sc6.cpp @@ -662,11 +662,11 @@ typedef SmxBlobSection SmxDebugSymbolsSection; typedef SmxBlobSection SmxDebugNativesSection; typedef Vector SymbolList; -static void append_debug_tables(SmxBuilder *builder, StringPool &pool, SymbolList &nativeList) +static void append_debug_tables(SmxBuilder *builder, StringPool &pool, Ref names, SymbolList &nativeList) { // We use a separate name table for historical reasons that are no longer // necessary. In the future we should just alias this to ".names". - Ref names = new SmxNameTable(".dbg.strings"); + Ref dbgnames = new SmxNameTable(".dbg.strings"); Ref info = new SmxDebugInfoSection(".dbg.info"); Ref lines = new SmxDebugLineSection(".dbg.lines"); Ref files = new SmxDebugFileSection(".dbg.files"); @@ -695,7 +695,7 @@ static void append_debug_tables(SmxBuilder *builder, StringPool &pool, SymbolLis if (prev_file_name) { sp_fdbg_file_t &entry = files->add(); entry.addr = prev_file_addr; - entry.name = names->add(pool, prev_file_name); + entry.name = dbgnames->add(pool, prev_file_name); } prev_file_addr = codeidx; } @@ -730,7 +730,7 @@ static void append_debug_tables(SmxBuilder *builder, StringPool &pool, SymbolLis sym.ident = (char)str.parse(); sym.vclass = (char)str.parse(); sym.dimcount = 0; - sym.name = names->add(atom); + sym.name = dbgnames->add(atom); info->header().num_syms++; @@ -757,7 +757,7 @@ static void append_debug_tables(SmxBuilder *builder, StringPool &pool, SymbolLis if (prev_file_name) { sp_fdbg_file_t &entry = files->add(); entry.addr = prev_file_addr; - entry.name = names->add(pool, prev_file_name); + entry.name = dbgnames->add(pool, prev_file_name); } // Build the tags table. @@ -783,7 +783,7 @@ static void append_debug_tables(SmxBuilder *builder, StringPool &pool, SymbolLis sp_fdbg_native_t info; info.index = i; - info.name = names->add(pool, sym->name); + info.name = dbgnames->add(pool, sym->name); info.tagid = sym->tag; info.nargs = 0; for (arginfo *arg = sym->dim.arglist; arg->ident; arg++) @@ -795,7 +795,7 @@ static void append_debug_tables(SmxBuilder *builder, StringPool &pool, SymbolLis argout.ident = arg->ident; argout.tagid = arg->tags[0]; argout.dimcount = arg->numdim; - argout.name = names->add(pool, arg->name); + argout.name = dbgnames->add(pool, arg->name); natives->add(&argout, sizeof(argout)); for (int j = 0; j < argout.dimcount; j++) { @@ -812,7 +812,7 @@ static void append_debug_tables(SmxBuilder *builder, StringPool &pool, SymbolLis builder->add(symbols); builder->add(lines); builder->add(natives); - builder->add(names); + builder->add(dbgnames); builder->add(info); builder->add(tags); } @@ -910,7 +910,7 @@ static void assemble_to_buffer(MemoryBuffer *buffer, void *fin) builder.add(pubvars); builder.add(natives); builder.add(names); - append_debug_tables(&builder, pool, nativeList); + append_debug_tables(&builder, pool, names, nativeList); builder.write(buffer); }