From 66e745a94bee3c893e67d731dda333da6d82576a Mon Sep 17 00:00:00 2001 From: Alex Martin Date: Wed, 27 Sep 2017 18:21:09 +0200 Subject: [PATCH 01/16] improve write op performance --- source/compiler/sc7.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/source/compiler/sc7.c b/source/compiler/sc7.c index 5981efc..91759dc 100644 --- a/source/compiler/sc7.c +++ b/source/compiler/sc7.c @@ -1254,6 +1254,7 @@ static void stgopt(char *start,char *end,int (*outputfunc)(char *str)); static char *stgbuf=NULL; static int stgmax=0; /* current size of the staging buffer */ +static int stglen=0; /* current length of the staging buffer */ static char *stgpipe=NULL; static int pipemax=0; /* current size of the stage pipe, a second staging buffer */ @@ -1364,10 +1365,12 @@ static int filewrite(char *str) * Global references: stgidx (altered) * stgbuf (altered) * staging (referred to only) + * stglen (altered) */ SC_FUNC void stgwrite(const char *st) { int len; + int st_len; if (staging) { assert(stgidx==0 || stgbuf!=NULL); /* staging buffer must be valid if there is (apparently) something in it */ @@ -1380,13 +1383,16 @@ SC_FUNC void stgwrite(const char *st) CHECK_STGBUFFER(stgidx); stgbuf[stgidx++]='\0'; } else { - len=(stgbuf!=NULL) ? strlen(stgbuf) : 0; - CHECK_STGBUFFER(len+strlen(st)+1); - strcat(stgbuf,st); - len=strlen(stgbuf); + len=(stgbuf!=NULL) ? stglen : 0; + st_len=strlen(st); + CHECK_STGBUFFER(len+st_len+1); + memcpy(stgbuf + len, st, st_len + 1); //strcat(stgbuf,st); + len=len+st_len; + stglen=len; if (len>0 && stgbuf[len-1]=='\n') { filewrite(stgbuf); stgbuf[0]='\0'; + stglen=0; } /* if */ } /* if */ } From 01387266987f90933949cbb2da6f66b26f7dfa68 Mon Sep 17 00:00:00 2001 From: Alex Martin Date: Tue, 3 Oct 2017 21:14:58 +0200 Subject: [PATCH 02/16] add symbol lookup cache --- source/compiler/CMakeLists.txt | 2 + source/compiler/hashmap/LICENSE | 21 + source/compiler/hashmap/hashmap.c | 689 ++++++++++++++++++++++++++++++ source/compiler/hashmap/hashmap.h | 263 ++++++++++++ source/compiler/sc.h | 5 +- source/compiler/sc1.c | 5 +- source/compiler/sc2.c | 35 +- source/compiler/scvars.c | 1 + 8 files changed, 1003 insertions(+), 18 deletions(-) create mode 100644 source/compiler/hashmap/LICENSE create mode 100644 source/compiler/hashmap/hashmap.c create mode 100644 source/compiler/hashmap/hashmap.h diff --git a/source/compiler/CMakeLists.txt b/source/compiler/CMakeLists.txt index 9e6d4b5..cae954e 100644 --- a/source/compiler/CMakeLists.txt +++ b/source/compiler/CMakeLists.txt @@ -60,6 +60,8 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) # The Pawn compiler shared library set(PAWNC_SRCS + hashmap/hashmap.c + hashmap/hashmap.h libpawnc.c lstring.c lstring.h diff --git a/source/compiler/hashmap/LICENSE b/source/compiler/hashmap/LICENSE new file mode 100644 index 0000000..2d576cc --- /dev/null +++ b/source/compiler/hashmap/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 David Leeds + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/source/compiler/hashmap/hashmap.c b/source/compiler/hashmap/hashmap.c new file mode 100644 index 0000000..d169457 --- /dev/null +++ b/source/compiler/hashmap/hashmap.c @@ -0,0 +1,689 @@ +/* + * Copyright (c) 2016-2017 David Leeds + * + * Hashmap is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include +#include +#include + +#include "hashmap.h" + +#ifndef HASHMAP_NOASSERT +#include +#define HASHMAP_ASSERT(expr) assert(expr) +#else +#define HASHMAP_ASSERT(expr) +#endif + +/* Table sizes must be powers of 2 */ +#define HASHMAP_SIZE_MIN (1 << 5) /* 32 */ +#define HASHMAP_SIZE_DEFAULT (1 << 8) /* 256 */ +#define HASHMAP_SIZE_MOD(map, val) ((val) & ((map)->table_size - 1)) + +/* Limit for probing is 1/2 of table_size */ +#define HASHMAP_PROBE_LEN(map) ((map)->table_size >> 1) +/* Return the next linear probe index */ +#define HASHMAP_PROBE_NEXT(map, index) HASHMAP_SIZE_MOD(map, (index) + 1) + +/* Check if index b is less than or equal to index a */ +#define HASHMAP_INDEX_LE(map, a, b) \ + ((a) == (b) || (((b) - (a)) & ((map)->table_size >> 1)) != 0) + + +struct hashmap_entry { + void *key; + void *data; +#ifdef HASHMAP_METRICS + size_t num_collisions; +#endif +}; + + +/* + * Enforce a maximum 0.75 load factor. + */ +static inline size_t hashmap_table_min_size_calc(size_t num_entries) +{ + return num_entries + (num_entries / 3); +} + +/* + * Calculate the optimal table size, given the specified max number + * of elements. + */ +static size_t hashmap_table_size_calc(size_t num_entries) +{ + size_t table_size; + size_t min_size; + + table_size = hashmap_table_min_size_calc(num_entries); + + /* Table size is always a power of 2 */ + min_size = HASHMAP_SIZE_MIN; + while (min_size < table_size) { + min_size <<= 1; + } + return min_size; +} + +/* + * Get a valid hash table index from a key. + */ +static inline size_t hashmap_calc_index(const struct hashmap *map, + const void *key) +{ + return HASHMAP_SIZE_MOD(map, map->hash(key)); +} + +/* + * Return the next populated entry, starting with the specified one. + * Returns NULL if there are no more valid entries. + */ +static struct hashmap_entry *hashmap_entry_get_populated( + const struct hashmap *map, struct hashmap_entry *entry) +{ + for (; entry < &map->table[map->table_size]; ++entry) { + if (entry->key) { + return entry; + } + } + return NULL; +} + +/* + * Find the hashmap entry with the specified key, or an empty slot. + * Returns NULL if the entire table has been searched without finding a match. + */ +static struct hashmap_entry *hashmap_entry_find(const struct hashmap *map, + const void *key, bool find_empty) +{ + size_t i; + size_t index; + size_t probe_len = HASHMAP_PROBE_LEN(map); + struct hashmap_entry *entry; + + index = hashmap_calc_index(map, key); + + /* Linear probing */ + for (i = 0; i < probe_len; ++i) { + entry = &map->table[index]; + if (!entry->key) { + if (find_empty) { +#ifdef HASHMAP_METRICS + entry->num_collisions = i; +#endif + return entry; + } + return NULL; + } + if (map->key_compare(key, entry->key) == 0) { + return entry; + } + index = HASHMAP_PROBE_NEXT(map, index); + } + return NULL; +} + +/* + * Removes the specified entry and processes the proceeding entries to reduce + * the load factor and keep the chain continuous. This is a required + * step for hash maps using linear probing. + */ +static void hashmap_entry_remove(struct hashmap *map, + struct hashmap_entry *removed_entry) +{ + size_t i; +#ifdef HASHMAP_METRICS + size_t removed_i = 0; +#endif + size_t index; + size_t entry_index; + size_t removed_index = (removed_entry - map->table); + struct hashmap_entry *entry; + + /* Free the key */ + if (map->key_free) { + map->key_free(removed_entry->key); + } + --map->num_entries; + + /* Fill the free slot in the chain */ + index = HASHMAP_PROBE_NEXT(map, removed_index); + for (i = 1; i < map->table_size; ++i) { + entry = &map->table[index]; + if (!entry->key) { + /* Reached end of chain */ + break; + } + entry_index = hashmap_calc_index(map, entry->key); + /* Shift in entries with an index <= to the removed slot */ + if (HASHMAP_INDEX_LE(map, removed_index, entry_index)) { +#ifdef HASHMAP_METRICS + entry->num_collisions -= (i - removed_i); + removed_i = i; +#endif + memcpy(removed_entry, entry, sizeof(*removed_entry)); + removed_index = index; + removed_entry = entry; + } + index = HASHMAP_PROBE_NEXT(map, index); + } + /* Clear the last removed entry */ + memset(removed_entry, 0, sizeof(*removed_entry)); +} + +/* + * Reallocates the hash table to the new size and rehashes all entries. + * new_size MUST be a power of 2. + * Returns 0 on success and -1 on allocation or hash function failure. + */ +static int hashmap_rehash(struct hashmap *map, size_t new_size) +{ + size_t old_size; + struct hashmap_entry *old_table; + struct hashmap_entry *new_table; + struct hashmap_entry *entry; + struct hashmap_entry *new_entry; + + HASHMAP_ASSERT(new_size >= HASHMAP_SIZE_MIN); + HASHMAP_ASSERT((new_size & (new_size - 1)) == 0); + + new_table = (struct hashmap_entry *)calloc(new_size, + sizeof(struct hashmap_entry)); + if (!new_table) { + return -1; + } + /* Backup old elements in case of rehash failure */ + old_size = map->table_size; + old_table = map->table; + map->table_size = new_size; + map->table = new_table; + /* Rehash */ + for (entry = old_table; entry < &old_table[old_size]; ++entry) { + if (!entry->data) { + /* Only copy entries with data */ + continue; + } + new_entry = hashmap_entry_find(map, entry->key, true); + if (!new_entry) { + /* + * The load factor is still too high with the new table + * size, or a poor hash function was used. + */ + goto revert; + } + /* Shallow copy (intentionally omits num_collisions) */ + new_entry->key = entry->key; + new_entry->data = entry->data; + } + free(old_table); + return 0; +revert: + map->table_size = old_size; + map->table = old_table; + free(new_table); + return -1; +} + +/* + * Iterate through all entries and free all keys. + */ +static void hashmap_free_keys(struct hashmap *map) +{ + struct hashmap_iter *iter; + + if (!map->key_free) { + return; + } + for (iter = hashmap_iter(map); iter; + iter = hashmap_iter_next(map, iter)) { + map->key_free((void *)hashmap_iter_get_key(iter)); + } +} + +/* + * Initialize an empty hashmap. A hash function and a key comparator are + * required. + * + * hash_func should return an even distribution of numbers between 0 + * and SIZE_MAX varying on the key provided. + * + * key_compare_func should return 0 if the keys match, and non-zero otherwise. + * + * initial_size is optional, and may be set to the max number of entries + * expected to be put in the hash table. This is used as a hint to + * pre-allocate the hash table to the minimum size needed to avoid + * gratuitous rehashes. If initial_size 0, a default size will be used. + */ +int hashmap_init(struct hashmap *map, size_t (*hash_func)(const void *), + int (*key_compare_func)(const void *, const void *), + size_t initial_size) +{ + HASHMAP_ASSERT(map != NULL); + HASHMAP_ASSERT(hash_func != NULL); + HASHMAP_ASSERT(key_compare_func != NULL); + + if (!initial_size) { + initial_size = HASHMAP_SIZE_DEFAULT; + } else { + /* Convert init size to valid table size */ + initial_size = hashmap_table_size_calc(initial_size); + } + map->table_size_init = initial_size; + map->table_size = initial_size; + map->num_entries = 0; + map->table = (struct hashmap_entry *)calloc(initial_size, + sizeof(struct hashmap_entry)); + if (!map->table) { + return -1; + } + map->hash = hash_func; + map->key_compare = key_compare_func; + map->key_alloc = NULL; + map->key_free = NULL; + return 0; +} + +/* + * Free the hashmap and all associated memory. + */ +void hashmap_destroy(struct hashmap *map) +{ + if (!map) { + return; + } + hashmap_free_keys(map); + free(map->table); + memset(map, 0, sizeof(*map)); +} + +/* + * Enable internal memory management of hash keys. + */ +void hashmap_set_key_alloc_funcs(struct hashmap *map, + void *(*key_alloc_func)(const void *), + void (*key_free_func)(void *)) +{ + HASHMAP_ASSERT(map != NULL); + + map->key_alloc = key_alloc_func; + map->key_free = key_free_func; +} + +/* + * Add an entry to the hashmap. If an entry with a matching key already + * exists and has a data pointer associated with it, the existing data + * pointer is returned, instead of assigning the new value. Compare + * the return value with the data passed in to determine if a new entry was + * created. Returns NULL if memory allocation failed. + */ +void *hashmap_put(struct hashmap *map, const void *key, void *data) +{ + struct hashmap_entry *entry; + + HASHMAP_ASSERT(map != NULL); + HASHMAP_ASSERT(key != NULL); + + /* Rehash with 2x capacity if load factor is approaching 0.75 */ + if (map->table_size <= hashmap_table_min_size_calc(map->num_entries)) { + hashmap_rehash(map, map->table_size << 1); + } + entry = hashmap_entry_find(map, key, true); + if (!entry) { + /* + * Cannot find an empty slot. Either out of memory, or using + * a poor hash function. Attempt to rehash once to reduce + * chain length. + */ + if (hashmap_rehash(map, map->table_size << 1) < 0) { + return NULL; + } + entry = hashmap_entry_find(map, key, true); + if (!entry) { + return NULL; + } + } + if (!entry->key) { + /* Allocate copy of key to simplify memory management */ + if (map->key_alloc) { + entry->key = map->key_alloc(key); + if (!entry->key) { + return NULL; + } + } else { + entry->key = (void *)key; + } + ++map->num_entries; + } else if (entry->data) { + /* Do not overwrite existing data */ + return entry->data; + } + entry->data = data; + return data; +} + +/* + * Return the data pointer, or NULL if no entry exists. + */ +void *hashmap_get(const struct hashmap *map, const void *key) +{ + struct hashmap_entry *entry; + + HASHMAP_ASSERT(map != NULL); + HASHMAP_ASSERT(key != NULL); + + entry = hashmap_entry_find(map, key, false); + if (!entry) { + return NULL; + } + return entry->data; +} + +/* + * Remove an entry with the specified key from the map. + * Returns the data pointer, or NULL, if no entry was found. + */ +void *hashmap_remove(struct hashmap *map, const void *key) +{ + struct hashmap_entry *entry; + void *data; + + HASHMAP_ASSERT(map != NULL); + HASHMAP_ASSERT(key != NULL); + + entry = hashmap_entry_find(map, key, false); + if (!entry) { + return NULL; + } + data = entry->data; + /* Clear the entry and make the chain contiguous */ + hashmap_entry_remove(map, entry); + return data; +} + +/* + * Remove all entries. + */ +void hashmap_clear(struct hashmap *map) +{ + HASHMAP_ASSERT(map != NULL); + + hashmap_free_keys(map); + map->num_entries = 0; + memset(map->table, 0, sizeof(struct hashmap_entry) * map->table_size); +} + +/* + * Remove all entries and reset the hash table to its initial size. + */ +void hashmap_reset(struct hashmap *map) +{ + struct hashmap_entry *new_table; + + HASHMAP_ASSERT(map != NULL); + + hashmap_clear(map); + if (map->table_size == map->table_size_init) { + return; + } + new_table = (struct hashmap_entry *)realloc(map->table, + sizeof(struct hashmap_entry) * map->table_size_init); + if (!new_table) { + return; + } + map->table = new_table; + map->table_size = map->table_size_init; +} + +/* + * Return the number of entries in the hash map. + */ +size_t hashmap_size(const struct hashmap *map) +{ + HASHMAP_ASSERT(map != NULL); + + return map->num_entries; +} + +/* + * Get a new hashmap iterator. The iterator is an opaque + * pointer that may be used with hashmap_iter_*() functions. + * Hashmap iterators are INVALID after a put or remove operation is performed. + * hashmap_iter_remove() allows safe removal during iteration. + */ +struct hashmap_iter *hashmap_iter(const struct hashmap *map) +{ + HASHMAP_ASSERT(map != NULL); + + if (!map->num_entries) { + return NULL; + } + return (struct hashmap_iter *)hashmap_entry_get_populated(map, + map->table); +} + +/* + * Return an iterator to the next hashmap entry. Returns NULL if there are + * no more entries. + */ +struct hashmap_iter *hashmap_iter_next(const struct hashmap *map, + const struct hashmap_iter *iter) +{ + struct hashmap_entry *entry = (struct hashmap_entry *)iter; + + HASHMAP_ASSERT(map != NULL); + + if (!iter) { + return NULL; + } + return (struct hashmap_iter *)hashmap_entry_get_populated(map, + entry + 1); +} + +/* + * Remove the hashmap entry pointed to by this iterator and return an + * iterator to the next entry. Returns NULL if there are no more entries. + */ +struct hashmap_iter *hashmap_iter_remove(struct hashmap *map, + const struct hashmap_iter *iter) +{ + struct hashmap_entry *entry = (struct hashmap_entry *)iter; + + HASHMAP_ASSERT(map != NULL); + + if (!iter) { + return NULL; + } + if (!entry->key) { + /* Iterator is invalid, so just return the next valid entry */ + return hashmap_iter_next(map, iter); + } + hashmap_entry_remove(map, entry); + return (struct hashmap_iter *)hashmap_entry_get_populated(map, entry); +} + +/* + * Return the key of the entry pointed to by the iterator. + */ +const void *hashmap_iter_get_key(const struct hashmap_iter *iter) +{ + if (!iter) { + return NULL; + } + return (const void *)((struct hashmap_entry *)iter)->key; +} + +/* + * Return the data of the entry pointed to by the iterator. + */ +void *hashmap_iter_get_data(const struct hashmap_iter *iter) +{ + if (!iter) { + return NULL; + } + return ((struct hashmap_entry *)iter)->data; +} + +/* + * Set the data pointer of the entry pointed to by the iterator. + */ +void hashmap_iter_set_data(const struct hashmap_iter *iter, void *data) +{ + if (!iter) { + return; + } + ((struct hashmap_entry *)iter)->data = data; +} + +/* + * Invoke func for each entry in the hashmap. Unlike the hashmap_iter_*() + * interface, this function supports calls to hashmap_remove() during iteration. + * However, it is an error to put or remove an entry other than the current one, + * and doing so will immediately halt iteration and return an error. + * Iteration is stopped if func returns non-zero. Returns func's return + * value if it is < 0, otherwise, 0. + */ +int hashmap_foreach(const struct hashmap *map, + int (*func)(const void *, void *, void *), void *arg) +{ + struct hashmap_entry *entry; + size_t num_entries; + const void *key; + int rc; + + HASHMAP_ASSERT(map != NULL); + HASHMAP_ASSERT(func != NULL); + + entry = map->table; + for (entry = map->table; entry < &map->table[map->table_size]; + ++entry) { + if (!entry->key) { + continue; + } + num_entries = map->num_entries; + key = entry->key; + rc = func(entry->key, entry->data, arg); + if (rc < 0) { + return rc; + } + if (rc > 0) { + return 0; + } + /* Run this entry again if func() deleted it */ + if (entry->key != key) { + --entry; + } else if (num_entries != map->num_entries) { + /* Stop immediately if func put/removed another entry */ + return -1; + } + } + return 0; +} + +/* + * Default hash function for string keys. + * This is an implementation of the well-documented Jenkins one-at-a-time + * hash function. + */ +size_t hashmap_hash_string(const void *key) +{ + const char *key_str = (const char *)key; + size_t hash = 0; + + for (; *key_str; ++key_str) { + hash += *key_str; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; +} + +/* + * Default key comparator function for string keys. + */ +int hashmap_compare_string(const void *a, const void *b) +{ + return strcmp((const char *)a, (const char *)b); +} + +/* + * Default key allocation function for string keys. Use free() for the + * key_free_func. + */ +void *hashmap_alloc_key_string(const void *key) +{ + return (void *)strdup((const char *)key); +} + +#ifdef HASHMAP_METRICS +/* + * Return the load factor. + */ +double hashmap_load_factor(const struct hashmap *map) +{ + HASHMAP_ASSERT(map != NULL); + + if (!map->table_size) { + return 0; + } + return (double)map->num_entries / map->table_size; +} + +/* + * Return the average number of collisions per entry. + */ +double hashmap_collisions_mean(const struct hashmap *map) +{ + struct hashmap_entry *entry; + size_t total_collisions = 0; + + HASHMAP_ASSERT(map != NULL); + + if (!map->num_entries) { + return 0; + } + for (entry = map->table; entry < &map->table[map->table_size]; + ++entry) { + if (!entry->key) { + continue; + } + total_collisions += entry->num_collisions; + } + return (double)total_collisions / map->num_entries; +} + +/* + * Return the variance between entry collisions. The higher the variance, + * the more likely the hash function is poor and is resulting in clustering. + */ +double hashmap_collisions_variance(const struct hashmap *map) +{ + struct hashmap_entry *entry; + double mean_collisions; + double variance; + double total_variance = 0; + + HASHMAP_ASSERT(map != NULL); + + if (!map->num_entries) { + return 0; + } + mean_collisions = hashmap_collisions_mean(map); + for (entry = map->table; entry < &map->table[map->table_size]; + ++entry) { + if (!entry->key) { + continue; + } + variance = (double)entry->num_collisions - mean_collisions; + total_variance += variance * variance; + } + return total_variance / map->num_entries; +} +#endif diff --git a/source/compiler/hashmap/hashmap.h b/source/compiler/hashmap/hashmap.h new file mode 100644 index 0000000..b4ce16e --- /dev/null +++ b/source/compiler/hashmap/hashmap.h @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2016-2017 David Leeds + * + * Hashmap is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef __HASHMAP_H__ +#define __HASHMAP_H__ + +/* + * Define HASHMAP_METRICS to compile in performance analysis + * functions for use in assessing hash function performance. + */ +/* #define HASHMAP_METRICS */ + +/* + * Define HASHMAP_NOASSERT to compile out all assertions used internally. + */ +/* #define HASHMAP_NOASSERT */ + +/* + * Macros to declare type-specific versions of hashmap_*() functions to + * allow compile-time type checking and avoid the need for type casting. + */ +#define HASHMAP_FUNCS_DECLARE(name, key_type, data_type) \ + data_type *name##_hashmap_put(struct hashmap *map, key_type *key, \ + data_type *data); \ + data_type *name##_hashmap_get(const struct hashmap *map, \ + key_type *key); \ + data_type *name##_hashmap_remove(struct hashmap *map, \ + key_type *key); \ + key_type *name##_hashmap_iter_get_key( \ + const struct hashmap_iter *iter); \ + data_type *name##_hashmap_iter_get_data( \ + const struct hashmap_iter *iter); \ + void name##_hashmap_iter_set_data(const struct hashmap_iter *iter, \ + data_type *data); \ + int name##_hashmap_foreach(const struct hashmap *map, \ + int (*func)(key_type *, data_type *, void *), void *arg); + +#define HASHMAP_FUNCS_CREATE(name, key_type, data_type) \ + data_type *name##_hashmap_put(struct hashmap *map, key_type *key, \ + data_type *data) \ + { \ + return (data_type *)hashmap_put(map, (const void *)key, \ + (void *)data); \ + } \ + data_type *name##_hashmap_get(const struct hashmap *map, \ + key_type *key) \ + { \ + return (data_type *)hashmap_get(map, (const void *)key); \ + } \ + data_type *name##_hashmap_remove(struct hashmap *map, \ + key_type *key) \ + { \ + return (data_type *)hashmap_remove(map, (const void *)key); \ + } \ + key_type *name##_hashmap_iter_get_key( \ + const struct hashmap_iter *iter) \ + { \ + return (key_type *)hashmap_iter_get_key(iter); \ + } \ + data_type *name##_hashmap_iter_get_data( \ + const struct hashmap_iter *iter) \ + { \ + return (data_type *)hashmap_iter_get_data(iter); \ + } \ + void name##_hashmap_iter_set_data(const struct hashmap_iter *iter, \ + data_type *data) \ + { \ + hashmap_iter_set_data(iter, (void *)data); \ + } \ + struct __##name##_hashmap_foreach_state { \ + int (*func)(key_type *, data_type *, void *); \ + void *arg; \ + }; \ + static inline int __##name##_hashmap_foreach_callback(const void *key, \ + void *data, void *arg) \ + { \ + struct __##name##_hashmap_foreach_state *s = \ + (struct __##name##_hashmap_foreach_state *)arg; \ + return s->func((key_type *)key, (data_type *)data, s->arg); \ + } \ + int name##_hashmap_foreach(const struct hashmap *map, \ + int (*func)(key_type *, data_type *, void *), void *arg) \ + { \ + struct __##name##_hashmap_foreach_state s = { func, arg }; \ + return hashmap_foreach(map, \ + __##name##_hashmap_foreach_callback, &s); \ + } + + +struct hashmap_iter; +struct hashmap_entry; + +/* + * The hashmap state structure. + */ +struct hashmap { + size_t table_size_init; + size_t table_size; + size_t num_entries; + struct hashmap_entry *table; + size_t (*hash)(const void *); + int (*key_compare)(const void *, const void *); + void *(*key_alloc)(const void *); + void (*key_free)(void *); +}; + +/* + * Initialize an empty hashmap. A hash function and a key comparator are + * required. + * + * hash_func should return an even distribution of numbers between 0 + * and SIZE_MAX varying on the key provided. + * + * key_compare_func should return 0 if the keys match, and non-zero otherwise. + * + * initial_size is optional, and may be set to the max number of entries + * expected to be put in the hash table. This is used as a hint to + * pre-allocate the hash table to the minimum size to avoid gratuitous rehashes. + * If initial_size 0, a default size will be used. + */ +int hashmap_init(struct hashmap *map, size_t (*hash_func)(const void *), + int (*key_compare_func)(const void *, const void *), + size_t initial_size); + +/* + * Free the hashmap and all associated memory. + */ +void hashmap_destroy(struct hashmap *map); + +/* + * Enable internal memory allocation and management of hash keys. + */ +void hashmap_set_key_alloc_funcs(struct hashmap *map, + void *(*key_alloc_func)(const void *), + void (*key_free_func)(void *)); + +/* + * Add an entry to the hashmap. If an entry with a matching key already + * exists and has a data pointer associated with it, the existing data + * pointer is returned, instead of assigning the new value. Compare + * the return value with the data passed in to determine if a new entry was + * created. Returns NULL if memory allocation failed. + */ +void *hashmap_put(struct hashmap *map, const void *key, void *data); + +/* + * Return the data pointer, or NULL if no entry exists. + */ +void *hashmap_get(const struct hashmap *map, const void *key); + +/* + * Remove an entry with the specified key from the map. + * Returns the data pointer, or NULL, if no entry was found. + */ +void *hashmap_remove(struct hashmap *map, const void *key); + +/* + * Remove all entries. + */ +void hashmap_clear(struct hashmap *map); + +/* + * Remove all entries and reset the hash table to its initial size. + */ +void hashmap_reset(struct hashmap *map); + +/* + * Return the number of entries in the hash map. + */ +size_t hashmap_size(const struct hashmap *map); + +/* + * Get a new hashmap iterator. The iterator is an opaque + * pointer that may be used with hashmap_iter_*() functions. + * Hashmap iterators are INVALID after a put or remove operation is performed. + * hashmap_iter_remove() allows safe removal during iteration. + */ +struct hashmap_iter *hashmap_iter(const struct hashmap *map); + +/* + * Return an iterator to the next hashmap entry. Returns NULL if there are + * no more entries. + */ +struct hashmap_iter *hashmap_iter_next(const struct hashmap *map, + const struct hashmap_iter *iter); + +/* + * Remove the hashmap entry pointed to by this iterator and returns an + * iterator to the next entry. Returns NULL if there are no more entries. + */ +struct hashmap_iter *hashmap_iter_remove(struct hashmap *map, + const struct hashmap_iter *iter); + +/* + * Return the key of the entry pointed to by the iterator. + */ +const void *hashmap_iter_get_key(const struct hashmap_iter *iter); + +/* + * Return the data of the entry pointed to by the iterator. + */ +void *hashmap_iter_get_data(const struct hashmap_iter *iter); + +/* + * Set the data pointer of the entry pointed to by the iterator. + */ +void hashmap_iter_set_data(const struct hashmap_iter *iter, void *data); + +/* + * Invoke func for each entry in the hashmap. Unlike the hashmap_iter_*() + * interface, this function supports calls to hashmap_remove() during iteration. + * However, it is an error to put or remove an entry other than the current one, + * and doing so will immediately halt iteration and return an error. + * Iteration is stopped if func returns non-zero. Returns func's return + * value if it is < 0, otherwise, 0. + */ +int hashmap_foreach(const struct hashmap *map, + int (*func)(const void *, void *, void *), void *arg); + +/* + * Default hash function for string keys. + * This is an implementation of the well-documented Jenkins one-at-a-time + * hash function. + */ +size_t hashmap_hash_string(const void *key); + +/* + * Default key comparator function for string keys. + */ +int hashmap_compare_string(const void *a, const void *b); + +/* + * Default key allocation function for string keys. Use free() for the + * key_free_func. + */ +void *hashmap_alloc_key_string(const void *key); + + +#ifdef HASHMAP_METRICS +/* + * Return the load factor. + */ +double hashmap_load_factor(const struct hashmap *map); + +/* + * Return the average number of collisions per entry. + */ +double hashmap_collisions_mean(const struct hashmap *map); + +/* + * Return the variance between entry collisions. The higher the variance, + * the more likely the hash function is poor and is resulting in clustering. + */ +double hashmap_collisions_variance(const struct hashmap *map); +#endif + + +#endif /* __HASHMAP_H__ */ + diff --git a/source/compiler/sc.h b/source/compiler/sc.h index a26b9d2..cec8ca6 100644 --- a/source/compiler/sc.h +++ b/source/compiler/sc.h @@ -41,6 +41,7 @@ #else #include #endif +#include "hashmap/hashmap.h" #include "../amx/osdefs.h" #include "../amx/amx.h" @@ -131,7 +132,6 @@ typedef struct s_symbol { struct s_symbol *next; struct s_symbol *parent; /* hierarchical types (multi-dimensional arrays) */ char name[sNAMEMAX+1]; - uint32_t hash; /* value derived from name, for quicker searching */ cell addr; /* address or offset (or value for constant, index for native function) */ cell codeaddr; /* address (in the code segment) where the symbol declaration starts */ char vclass; /* sLOCAL if "addr" refers to a local symbol */ @@ -581,7 +581,7 @@ SC_FUNC void delete_symbol(symbol *root,symbol *sym); SC_FUNC void delete_symbols(symbol *root,int level,int del_labels,int delete_functions); SC_FUNC int refer_symbol(symbol *entry,symbol *bywhom); SC_FUNC void markusage(symbol *sym,int usage); -SC_FUNC uint32_t namehash(const char *name); +SC_FUNC void rename_symbol(symbol *sym, const char *newname); SC_FUNC symbol *findglb(const char *name,int filter); SC_FUNC symbol *findloc(const char *name); SC_FUNC symbol *findconst(const char *name,int *matchtag); @@ -786,6 +786,7 @@ SC_FUNC int state_conflict_id(int listid1,int listid2); #if !defined SC_SKIP_VDECL SC_VDECL symbol loctab; /* local symbol table */ SC_VDECL symbol glbtab; /* global symbol table */ +SC_VDECL struct hashmap symbol_cache_map; SC_VDECL cell *litq; /* the literal queue */ SC_VDECL unsigned char pline[]; /* the line read from the input file */ SC_VDECL const unsigned char *lptr;/* points to the current position in "pline" */ diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index cff8561..c7a8be4 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -761,6 +761,7 @@ cleanup: delete_symbols(&loctab,0,TRUE,TRUE); /* delete local variables if not yet * done (i.e. on a fatal error) */ delete_symbols(&glbtab,0,TRUE,TRUE); + hashmap_destroy(&symbol_cache_map); delete_consttable(&tagname_tab); delete_consttable(&libname_tab); delete_consttable(&sc_automaton_tab); @@ -927,6 +928,7 @@ static void initglobals(void) litq=NULL; /* the literal queue */ glbtab.next=NULL; /* clear global variables/constants table */ loctab.next=NULL; /* " local " / " " */ + hashmap_init(&symbol_cache_map, hashmap_hash_string, hashmap_compare_string, 100000); // TODO: make sure this is big enough tagname_tab.next=NULL;/* tagname table */ libname_tab.next=NULL;/* library table (#pragma library "..." syntax) */ @@ -3261,8 +3263,7 @@ static int operatoradjust(int opertok,symbol *sym,char *opername,int resulttag) refer_symbol(sym,oldsym->refer[i]); delete_symbol(&glbtab,oldsym); } /* if */ - strcpy(sym->name,tmpname); - sym->hash=namehash(sym->name);/* calculate new hash */ + rename_symbol(sym, tmpname); /* operators should return a value, except the '~' operator */ if (opertok!='~') diff --git a/source/compiler/sc2.c b/source/compiler/sc2.c index 2c9fd1a..0307695 100644 --- a/source/compiler/sc2.c +++ b/source/compiler/sc2.c @@ -2644,6 +2644,9 @@ static symbol *add_symbol(symbol *root,symbol *entry,int sort) memcpy(newsym,entry,sizeof(symbol)); newsym->next=root->next; root->next=newsym; + if (newsym->vclass == sGLOBAL) { + hashmap_put(&symbol_cache_map, newsym->name, newsym); + } return newsym; } @@ -2688,6 +2691,8 @@ static void free_symbol(symbol *sym) free(sym->refer); if (sym->documentation!=NULL) free(sym->documentation); + if (sym->vclass == sGLOBAL) + hashmap_remove(&symbol_cache_map, sym->name); free(sym); } @@ -2790,18 +2795,15 @@ SC_FUNC void delete_symbols(symbol *root,int level,int delete_labels,int delete_ } /* while */ } -/* The purpose of the hash is to reduce the frequency of a "name" - * comparison (which is costly). There is little interest in avoiding - * clusters in similar names, which is why this function is plain simple. - */ -SC_FUNC uint32_t namehash(const char *name) +SC_FUNC void rename_symbol(symbol *sym, const char *newname) { - const unsigned char *ptr=(const unsigned char *)name; - int len=strlen(name); - if (len==0) - return 0L; - assert(len<256); - return (len<<24Lu) + (ptr[0]<<16Lu) + (ptr[len-1]<<8Lu) + (ptr[len>>1Lu]); + char is_global = (sym->vclass == sGLOBAL); + + if (is_global) + hashmap_remove(&symbol_cache_map, sym->name); + strcpy(sym->name, newname); + if (is_global) + hashmap_put(&symbol_cache_map, sym->name, sym); } static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int automaton,int *cmptag) @@ -2809,9 +2811,13 @@ static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int a symbol *firstmatch=NULL; symbol *sym=root->next; int count=0; - unsigned long hash=namehash(name); + char is_global = (root == &glbtab); + + if (is_global) + sym = hashmap_get(&symbol_cache_map, name); + while (sym!=NULL) { - if (hash==sym->hash && strcmp(name,sym->name)==0 /* check name */ + if ( (is_global || strcmp(name,sym->name)==0) /* check name */ && (sym->parent==NULL || sym->ident==iCONSTEXPR) /* sub-types (hierarchical types) are skipped, except for enum fields */ && (sym->fnumber<0 || sym->fnumber==fnumber)) /* check file number for scope */ { @@ -2834,6 +2840,8 @@ static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int a } /* if */ } /* if */ } /* */ + if (is_global) + break; sym=sym->next; } /* while */ if (cmptag!=NULL && firstmatch!=NULL) { @@ -3010,7 +3018,6 @@ SC_FUNC symbol *addsym(const char *name,cell addr,int ident,int vclass,int tag,i /* first fill in the entry */ memset(&entry,0,sizeof entry); strcpy(entry.name,name); - entry.hash=namehash(name); entry.addr=addr; entry.codeaddr=code_idx; entry.vclass=(char)vclass; diff --git a/source/compiler/scvars.c b/source/compiler/scvars.c index d9f77d0..a96df7c 100644 --- a/source/compiler/scvars.c +++ b/source/compiler/scvars.c @@ -33,6 +33,7 @@ */ SC_VDEFINE symbol loctab; /* local symbol table */ SC_VDEFINE symbol glbtab; /* global symbol table */ +SC_VDEFINE struct hashmap symbol_cache_map; SC_VDEFINE cell *litq; /* the literal queue */ SC_VDEFINE unsigned char pline[sLINEMAX+1]; /* the line read from the input file */ SC_VDEFINE const unsigned char *lptr; /* points to the current position in "pline" */ From 2d8791a0f299570b696ee66d7f1e2e635efe88a5 Mon Sep 17 00:00:00 2001 From: Alex Martin Date: Tue, 3 Oct 2017 22:03:53 +0200 Subject: [PATCH 03/16] minor improvement --- source/compiler/sc2.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/source/compiler/sc2.c b/source/compiler/sc2.c index 0307695..ddabd11 100644 --- a/source/compiler/sc2.c +++ b/source/compiler/sc2.c @@ -3095,7 +3095,8 @@ SC_FUNC int getlabel(void) */ SC_FUNC char *itoh(ucell val) { -static char itohstr[30]; + const char *hex = "0123456789abcdef"; + static char itohstr[30]; char *ptr; int i,nibble[16]; /* a 64-bit hexadecimal cell has 16 nibbles */ int max; @@ -3118,10 +3119,7 @@ static char itohstr[30]; while (nibble[i]==0 && i>0) /* search for highest non-zero nibble */ i-=1; while (i>=0){ - if (nibble[i]>=10) - *ptr++=(char)('a'+(nibble[i]-10)); - else - *ptr++=(char)('0'+nibble[i]); + *ptr++ = hex[nibble[i]]; i-=1; } /* while */ *ptr='\0'; /* and a zero-terminator */ From 73d4934d61d0825c32faa461e9e9e7bbc99cbf38 Mon Sep 17 00:00:00 2001 From: Alex Martin Date: Sun, 15 Oct 2017 12:55:51 +0200 Subject: [PATCH 04/16] store symbol children for faster lookup --- source/compiler/sc.h | 1 + source/compiler/sc1.c | 6 ++++++ source/compiler/sc2.c | 10 ++++------ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/source/compiler/sc.h b/source/compiler/sc.h index cec8ca6..f3c6ea7 100644 --- a/source/compiler/sc.h +++ b/source/compiler/sc.h @@ -131,6 +131,7 @@ typedef struct s_constvalue { typedef struct s_symbol { struct s_symbol *next; struct s_symbol *parent; /* hierarchical types (multi-dimensional arrays) */ + struct s_symbol *child; char name[sNAMEMAX+1]; cell addr; /* address or offset (or value for constant, index for native function) */ cell codeaddr; /* address (in the code segment) where the symbol declaration starts */ diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index c7a8be4..592fba8 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -2929,6 +2929,8 @@ static void decl_enum(int vclass,int fstatic) sym->dim.array.length=size; sym->dim.array.level=0; sym->parent=enumsym; + if (enumsym) + enumsym->child=sym; if (fstatic) sym->fnumber=filenum; @@ -3522,6 +3524,8 @@ static void funcstub(int fnative) assert(sym!=NULL); sub=addvariable(symbolname,0,iARRAY,sGLOBAL,tag,dim,numdim,idxtag,0); sub->parent=sym; + if (sym) + sym->child=sub; } /* if */ litidx=0; /* clear the literal pool */ @@ -6513,6 +6517,8 @@ static void doreturn(void) sub=addvariable(curfunc->name,(argcount+3)*sizeof(cell),iREFARRAY,sGLOBAL, curfunc->tag,dim,numdim,idxtag,0); sub->parent=curfunc; + if (curfunc) + curfunc->child=sub; } /* if */ /* get the hidden parameter, copy the array (the array is on the heap; * it stays on the heap for the moment, and it is removed -usually- at diff --git a/source/compiler/sc2.c b/source/compiler/sc2.c index ddabd11..9a0fb90 100644 --- a/source/compiler/sc2.c +++ b/source/compiler/sc2.c @@ -2855,12 +2855,8 @@ static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int a static symbol *find_symbol_child(const symbol *root,const symbol *sym) { - symbol *ptr=root->next; - while (ptr!=NULL) { - if (ptr->parent==sym) - return ptr; - ptr=ptr->next; - } /* while */ + if (sym->child && sym->child->parent == sym) + return sym->child; return NULL; } @@ -3062,6 +3058,8 @@ SC_FUNC symbol *addvariable(const char *name,cell addr,int ident,int vclass,int top->dim.array.level=(short)(numdim-level-1); top->x.tags.index=idxtag[level]; top->parent=parent; + if (parent) + parent->child=top; if (vclass==sLOCAL || vclass==sSTATIC) { top->compound=compound; /* for multiple declaration/shadowing check */ } /* if */ From 3c5f91dd0b165a184476c6c7dd8f5bb595ca40e2 Mon Sep 17 00:00:00 2001 From: Alex Martin Date: Sun, 15 Oct 2017 13:44:29 +0200 Subject: [PATCH 05/16] fix symbol lookup for symbols with same name --- source/compiler/sc.h | 6 ++++ source/compiler/sc2.c | 76 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/source/compiler/sc.h b/source/compiler/sc.h index f3c6ea7..b6f381b 100644 --- a/source/compiler/sc.h +++ b/source/compiler/sc.h @@ -166,6 +166,12 @@ typedef struct s_symbol { char *documentation; /* optional documentation string */ } symbol; +/* new symbol struct for cached global symbols with the same names*/ +typedef struct s_symbol2 { + struct s_symbol *symbol; + struct s_symbol2 *next; +} symbol2; + /* Possible entries for "ident". These are used in the "symbol", "value" * and arginfo structures. Not every constant is valid for every use. diff --git a/source/compiler/sc2.c b/source/compiler/sc2.c index 9a0fb90..b41864d 100644 --- a/source/compiler/sc2.c +++ b/source/compiler/sc2.c @@ -2632,6 +2632,8 @@ SC_FUNC int ishex(char c) static symbol *add_symbol(symbol *root,symbol *entry,int sort) { symbol *newsym; + symbol2 *cache_sym; + symbol2 *new_cache_sym; if (sort) while (root->next!=NULL && strcmp(entry->name,root->next->name)>0) @@ -2645,7 +2647,23 @@ static symbol *add_symbol(symbol *root,symbol *entry,int sort) newsym->next=root->next; root->next=newsym; if (newsym->vclass == sGLOBAL) { - hashmap_put(&symbol_cache_map, newsym->name, newsym); + if ((cache_sym=hashmap_get(&symbol_cache_map, newsym->name))==NULL) { + if ((cache_sym=(symbol2 *)malloc(sizeof(symbol2)))==NULL) { + error(103); + return NULL; + } /* if */ + cache_sym->symbol=newsym; + cache_sym->next=NULL; + hashmap_put(&symbol_cache_map, newsym->name, cache_sym); + } else { + if ((new_cache_sym=(symbol2 *)malloc(sizeof(symbol2)))==NULL) { + error(103); + return NULL; + } /* if */ + new_cache_sym->symbol=newsym; + new_cache_sym->next=NULL; + cache_sym->next=new_cache_sym; + } } return newsym; } @@ -2653,6 +2671,8 @@ static symbol *add_symbol(symbol *root,symbol *entry,int sort) static void free_symbol(symbol *sym) { arginfo *arg; + symbol2 *cache_sym; + symbol2 *parent_cache_sym=NULL; /* free all sub-symbol allocated memory blocks, depending on the * kind of the symbol @@ -2691,8 +2711,24 @@ static void free_symbol(symbol *sym) free(sym->refer); if (sym->documentation!=NULL) free(sym->documentation); - if (sym->vclass == sGLOBAL) - hashmap_remove(&symbol_cache_map, sym->name); + if (sym->vclass == sGLOBAL) { + cache_sym=hashmap_get(&symbol_cache_map, sym->name); + while (cache_sym!=NULL) { + if (cache_sym->symbol!=sym) { + parent_cache_sym=cache_sym; + cache_sym=cache_sym->next; + continue; + } + + if (parent_cache_sym!=NULL) { + parent_cache_sym->next=cache_sym->next; + free(cache_sym); + } else { + hashmap_remove(&symbol_cache_map, sym->name); + } + break; + } + } free(sym); } @@ -2798,23 +2834,33 @@ SC_FUNC void delete_symbols(symbol *root,int level,int delete_labels,int delete_ SC_FUNC void rename_symbol(symbol *sym, const char *newname) { char is_global = (sym->vclass == sGLOBAL); + symbol2 *cache_sym; - if (is_global) - hashmap_remove(&symbol_cache_map, sym->name); + if (is_global) { + cache_sym=hashmap_get(&symbol_cache_map, sym->name); + if (cache_sym!=NULL) + hashmap_remove(&symbol_cache_map, sym->name); // TODO what if there are multiple symbols with same name and rename happens? + } strcpy(sym->name, newname); - if (is_global) - hashmap_put(&symbol_cache_map, sym->name, sym); + if (is_global && cache_sym != NULL) + hashmap_put(&symbol_cache_map, sym->name, cache_sym); } static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int automaton,int *cmptag) { symbol *firstmatch=NULL; symbol *sym=root->next; + symbol2 *cache_sym=NULL; int count=0; char is_global = (root == &glbtab); - if (is_global) - sym = hashmap_get(&symbol_cache_map, name); + if (is_global) { + cache_sym=hashmap_get(&symbol_cache_map, name); + if (cache_sym) + sym = cache_sym->symbol; + else + sym = NULL; + } while (sym!=NULL) { if ( (is_global || strcmp(name,sym->name)==0) /* check name */ @@ -2840,9 +2886,15 @@ static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int a } /* if */ } /* if */ } /* */ - if (is_global) - break; - sym=sym->next; + if (is_global) { + cache_sym=cache_sym->next; + if (cache_sym) + sym = cache_sym->symbol; + else + sym = NULL; + } else { + sym=sym->next; + } } /* while */ if (cmptag!=NULL && firstmatch!=NULL) { if (*cmptag==0) From b312efa57bd21ccc3d48b9e313209d9dac1db3ef Mon Sep 17 00:00:00 2001 From: Alex Martin Date: Sun, 15 Oct 2017 17:30:49 +0200 Subject: [PATCH 06/16] bug-fix: append new symbol to last one --- source/compiler/sc2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/compiler/sc2.c b/source/compiler/sc2.c index b41864d..9aad325 100644 --- a/source/compiler/sc2.c +++ b/source/compiler/sc2.c @@ -2662,6 +2662,8 @@ static symbol *add_symbol(symbol *root,symbol *entry,int sort) } /* if */ new_cache_sym->symbol=newsym; new_cache_sym->next=NULL; + while(cache_sym->next!=NULL) + cache_sym=cache_sym->next; cache_sym->next=new_cache_sym; } } From 9efca3a7b42f95e1168a5103ecbf8b66a39ece79 Mon Sep 17 00:00:00 2001 From: Alex Martin Date: Mon, 16 Oct 2017 21:23:15 +0200 Subject: [PATCH 07/16] increase hashmap size --- source/compiler/sc1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index 592fba8..dfb647f 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -928,7 +928,7 @@ static void initglobals(void) litq=NULL; /* the literal queue */ glbtab.next=NULL; /* clear global variables/constants table */ loctab.next=NULL; /* " local " / " " */ - hashmap_init(&symbol_cache_map, hashmap_hash_string, hashmap_compare_string, 100000); // TODO: make sure this is big enough + hashmap_init(&symbol_cache_map, hashmap_hash_string, hashmap_compare_string, 10000000); // TODO: make sure this is big enough tagname_tab.next=NULL;/* tagname table */ libname_tab.next=NULL;/* library table (#pragma library "..." syntax) */ From cbf7e661d23ef822bba5f65a4d753fae661e933d Mon Sep 17 00:00:00 2001 From: Alex Martin Date: Mon, 16 Oct 2017 21:24:48 +0200 Subject: [PATCH 08/16] fix previous stage buffer performance opt --- source/compiler/sc7.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/source/compiler/sc7.c b/source/compiler/sc7.c index 91759dc..bbdb0c1 100644 --- a/source/compiler/sc7.c +++ b/source/compiler/sc7.c @@ -1291,6 +1291,7 @@ SC_FUNC void stgbuffer_cleanup(void) if (stgbuf!=NULL) { free(stgbuf); stgbuf=NULL; + stglen=0; stgmax=0; } /* if */ if (stgpipe!=NULL) { @@ -1324,6 +1325,7 @@ SC_FUNC void stgmark(char mark) if (staging) { CHECK_STGBUFFER(stgidx); stgbuf[stgidx++]=mark; + stglen++; } /* if */ } @@ -1379,14 +1381,15 @@ SC_FUNC void stgwrite(const char *st) while (*st!='\0') { /* copy to staging buffer */ CHECK_STGBUFFER(stgidx); stgbuf[stgidx++]=*st++; + stglen++; } /* while */ CHECK_STGBUFFER(stgidx); stgbuf[stgidx++]='\0'; } else { len=(stgbuf!=NULL) ? stglen : 0; - st_len=strlen(st); + st_len = strlen(st); CHECK_STGBUFFER(len+st_len+1); - memcpy(stgbuf + len, st, st_len + 1); //strcat(stgbuf,st); + memcpy(stgbuf + len, st, st_len + 1); //strcat(stgbuf,st); len=len+st_len; stglen=len; if (len>0 && stgbuf[len-1]=='\n') { @@ -1419,6 +1422,7 @@ SC_FUNC void stgout(int index) /* first pass: sub-expressions */ if (sc_status==statWRITE) reordered=stgstring(&stgbuf[index],&stgbuf[stgidx]); + stglen=stgidx-index; stgidx=index; /* second pass: optimize the buffer created in the first pass */ @@ -1568,10 +1572,11 @@ SC_FUNC void stgset(int onoff) /* write any contents that may be put in the buffer by stgwrite() * when "staging" was 0 */ - if (strlen(stgbuf)>0) + if (stglen>0) filewrite(stgbuf); } /* if */ stgbuf[0]='\0'; + stglen=0; } #define MAX_OPT_VARS 5 From 8665f8b03cba38d5653c6d4be730c4b5bf2798e1 Mon Sep 17 00:00:00 2001 From: Daniel_Cortez Date: Tue, 17 Oct 2017 14:26:34 +0700 Subject: [PATCH 09/16] Code cleanup, style fixes --- source/compiler/sc.h | 2 +- source/compiler/sc1.c | 4 +- source/compiler/sc2.c | 129 ++++++++++++++++++++++-------------------- source/compiler/sc7.c | 4 +- 4 files changed, 74 insertions(+), 65 deletions(-) diff --git a/source/compiler/sc.h b/source/compiler/sc.h index b6f381b..72fe01d 100644 --- a/source/compiler/sc.h +++ b/source/compiler/sc.h @@ -588,7 +588,7 @@ SC_FUNC void delete_symbol(symbol *root,symbol *sym); SC_FUNC void delete_symbols(symbol *root,int level,int del_labels,int delete_functions); SC_FUNC int refer_symbol(symbol *entry,symbol *bywhom); SC_FUNC void markusage(symbol *sym,int usage); -SC_FUNC void rename_symbol(symbol *sym, const char *newname); +SC_FUNC void rename_symbol(symbol *sym,const char *newname); SC_FUNC symbol *findglb(const char *name,int filter); SC_FUNC symbol *findloc(const char *name); SC_FUNC symbol *findconst(const char *name,int *matchtag); diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index dfb647f..85cf255 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -928,7 +928,7 @@ static void initglobals(void) litq=NULL; /* the literal queue */ glbtab.next=NULL; /* clear global variables/constants table */ loctab.next=NULL; /* " local " / " " */ - hashmap_init(&symbol_cache_map, hashmap_hash_string, hashmap_compare_string, 10000000); // TODO: make sure this is big enough + hashmap_init(&symbol_cache_map,hashmap_hash_string,hashmap_compare_string,10000000); // TODO: make sure this is big enough tagname_tab.next=NULL;/* tagname table */ libname_tab.next=NULL;/* library table (#pragma library "..." syntax) */ @@ -3265,7 +3265,7 @@ static int operatoradjust(int opertok,symbol *sym,char *opername,int resulttag) refer_symbol(sym,oldsym->refer[i]); delete_symbol(&glbtab,oldsym); } /* if */ - rename_symbol(sym, tmpname); + rename_symbol(sym,tmpname); /* operators should return a value, except the '~' operator */ if (opertok!='~') diff --git a/source/compiler/sc2.c b/source/compiler/sc2.c index 9aad325..2f4befc 100644 --- a/source/compiler/sc2.c +++ b/source/compiler/sc2.c @@ -2623,6 +2623,57 @@ SC_FUNC int ishex(char c) return (c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F'); } +static void symbol_cache_add(symbol *sym,symbol2 *new_cache_sym) +{ + symbol2 *cache_sym; + + if (new_cache_sym==NULL) { + new_cache_sym=(symbol2 *)malloc(sizeof(symbol2)); + if (new_cache_sym==NULL) + error(103); /* insufficient memory */ + new_cache_sym->symbol=sym; + } + new_cache_sym->next=NULL; + + cache_sym=hashmap_get(&symbol_cache_map,sym->name); + if (cache_sym==NULL) { + if (hashmap_put(&symbol_cache_map,sym->name,new_cache_sym)==NULL) + error(103); /* insufficient memory */ + } else { + while(cache_sym->next!=NULL) + cache_sym=cache_sym->next; + cache_sym->next=new_cache_sym; + } +} + +static symbol2 *symbol_cache_remove(symbol *sym,int free_cache_sym) +{ + symbol2 *cache_sym; + symbol2 *parent_cache_sym=NULL; + + cache_sym=hashmap_get(&symbol_cache_map,sym->name); + if (cache_sym==NULL) + return NULL; + while (cache_sym->symbol!=sym) { + parent_cache_sym=cache_sym; + cache_sym=cache_sym->next; + } + + if (parent_cache_sym!=NULL) { + parent_cache_sym->next=cache_sym->next; + } else { + hashmap_remove(&symbol_cache_map,sym->name); + if (cache_sym->next!=NULL) + if (hashmap_put(&symbol_cache_map,sym->name,cache_sym->next)) + error(103); /* insufficient memory */ + } + if (free_cache_sym) { + free(cache_sym); + return NULL; + } + return cache_sym; +} + /* The local variable table must be searched backwards, so that the deepest * nesting of local variables is searched first. The simplest way to do * this is to insert all new items at the head of the list. @@ -2632,8 +2683,6 @@ SC_FUNC int ishex(char c) static symbol *add_symbol(symbol *root,symbol *entry,int sort) { symbol *newsym; - symbol2 *cache_sym; - symbol2 *new_cache_sym; if (sort) while (root->next!=NULL && strcmp(entry->name,root->next->name)>0) @@ -2646,35 +2695,14 @@ static symbol *add_symbol(symbol *root,symbol *entry,int sort) memcpy(newsym,entry,sizeof(symbol)); newsym->next=root->next; root->next=newsym; - if (newsym->vclass == sGLOBAL) { - if ((cache_sym=hashmap_get(&symbol_cache_map, newsym->name))==NULL) { - if ((cache_sym=(symbol2 *)malloc(sizeof(symbol2)))==NULL) { - error(103); - return NULL; - } /* if */ - cache_sym->symbol=newsym; - cache_sym->next=NULL; - hashmap_put(&symbol_cache_map, newsym->name, cache_sym); - } else { - if ((new_cache_sym=(symbol2 *)malloc(sizeof(symbol2)))==NULL) { - error(103); - return NULL; - } /* if */ - new_cache_sym->symbol=newsym; - new_cache_sym->next=NULL; - while(cache_sym->next!=NULL) - cache_sym=cache_sym->next; - cache_sym->next=new_cache_sym; - } - } + if (newsym->vclass==sGLOBAL) + symbol_cache_add(newsym,NULL); return newsym; } static void free_symbol(symbol *sym) { arginfo *arg; - symbol2 *cache_sym; - symbol2 *parent_cache_sym=NULL; /* free all sub-symbol allocated memory blocks, depending on the * kind of the symbol @@ -2713,24 +2741,8 @@ static void free_symbol(symbol *sym) free(sym->refer); if (sym->documentation!=NULL) free(sym->documentation); - if (sym->vclass == sGLOBAL) { - cache_sym=hashmap_get(&symbol_cache_map, sym->name); - while (cache_sym!=NULL) { - if (cache_sym->symbol!=sym) { - parent_cache_sym=cache_sym; - cache_sym=cache_sym->next; - continue; - } - - if (parent_cache_sym!=NULL) { - parent_cache_sym->next=cache_sym->next; - free(cache_sym); - } else { - hashmap_remove(&symbol_cache_map, sym->name); - } - break; - } - } + if (sym->vclass==sGLOBAL) + symbol_cache_remove(sym,1); free(sym); } @@ -2833,19 +2845,16 @@ SC_FUNC void delete_symbols(symbol *root,int level,int delete_labels,int delete_ } /* while */ } -SC_FUNC void rename_symbol(symbol *sym, const char *newname) +SC_FUNC void rename_symbol(symbol *sym,const char *newname) { - char is_global = (sym->vclass == sGLOBAL); + int is_global=(sym->vclass==sGLOBAL); symbol2 *cache_sym; - if (is_global) { - cache_sym=hashmap_get(&symbol_cache_map, sym->name); - if (cache_sym!=NULL) - hashmap_remove(&symbol_cache_map, sym->name); // TODO what if there are multiple symbols with same name and rename happens? - } - strcpy(sym->name, newname); - if (is_global && cache_sym != NULL) - hashmap_put(&symbol_cache_map, sym->name, cache_sym); + if (is_global) + cache_sym=symbol_cache_remove(sym,0); + strcpy(sym->name,newname); + if (is_global && cache_sym!=NULL) + symbol_cache_add(sym,cache_sym); } static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int automaton,int *cmptag) @@ -2854,14 +2863,14 @@ static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int a symbol *sym=root->next; symbol2 *cache_sym=NULL; int count=0; - char is_global = (root == &glbtab); + int is_global=(root==&glbtab); if (is_global) { - cache_sym=hashmap_get(&symbol_cache_map, name); + cache_sym=hashmap_get(&symbol_cache_map,name); if (cache_sym) - sym = cache_sym->symbol; + sym=cache_sym->symbol; else - sym = NULL; + sym=NULL; } while (sym!=NULL) { @@ -2891,9 +2900,9 @@ static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int a if (is_global) { cache_sym=cache_sym->next; if (cache_sym) - sym = cache_sym->symbol; + sym=cache_sym->symbol; else - sym = NULL; + sym=NULL; } else { sym=sym->next; } @@ -2909,7 +2918,7 @@ static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int a static symbol *find_symbol_child(const symbol *root,const symbol *sym) { - if (sym->child && sym->child->parent == sym) + if (sym->child && sym->child->parent==sym) return sym->child; return NULL; } diff --git a/source/compiler/sc7.c b/source/compiler/sc7.c index bbdb0c1..c28ef87 100644 --- a/source/compiler/sc7.c +++ b/source/compiler/sc7.c @@ -1387,9 +1387,9 @@ SC_FUNC void stgwrite(const char *st) stgbuf[stgidx++]='\0'; } else { len=(stgbuf!=NULL) ? stglen : 0; - st_len = strlen(st); + st_len=strlen(st); CHECK_STGBUFFER(len+st_len+1); - memcpy(stgbuf + len, st, st_len + 1); //strcat(stgbuf,st); + memcpy(stgbuf+len,st,st_len+1); //strcat(stgbuf,st); len=len+st_len; stglen=len; if (len>0 && stgbuf[len-1]=='\n') { From f5ba1b40779d74afac9822b7f4d81742b552e8c9 Mon Sep 17 00:00:00 2001 From: Daniel_Cortez Date: Tue, 17 Oct 2017 14:54:38 +0700 Subject: [PATCH 10/16] Improved itoh() implementation --- source/compiler/sc2.c | 40 ++++++++++++---------------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/source/compiler/sc2.c b/source/compiler/sc2.c index 2f4befc..2a13c15 100644 --- a/source/compiler/sc2.c +++ b/source/compiler/sc2.c @@ -3156,33 +3156,17 @@ SC_FUNC int getlabel(void) */ SC_FUNC char *itoh(ucell val) { - const char *hex = "0123456789abcdef"; - static char itohstr[30]; - char *ptr; - int i,nibble[16]; /* a 64-bit hexadecimal cell has 16 nibbles */ - int max; + static const char hex[16]= + { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + static char itohstr[17]= + { '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0' }; + char *ptr=&itohstr[15]; - #if PAWN_CELL_SIZE==16 - max=4; - #elif PAWN_CELL_SIZE==32 - max=8; - #elif PAWN_CELL_SIZE==64 - max=16; - #else - #error Unsupported cell size - #endif - ptr=itohstr; - for (i=0; i>=4; - } /* endfor */ - i=max-1; - while (nibble[i]==0 && i>0) /* search for highest non-zero nibble */ - i-=1; - while (i>=0){ - *ptr++ = hex[nibble[i]]; - i-=1; - } /* while */ - *ptr='\0'; /* and a zero-terminator */ - return itohstr; +#if PAWN_CELL_SIZE>64 + #error Unsupported cell size +#endif + do { + *ptr-- = hex[val&(ucell)0x0f]; + } while ((val>>=4)!=0); + return ptr+1; } From 03aaa4f2cd092ca1bc069dc41a76940b5302ad31 Mon Sep 17 00:00:00 2001 From: Daniel_Cortez Date: Wed, 18 Oct 2017 22:08:08 +0700 Subject: [PATCH 11/16] Minor adjustments --- source/compiler/sc2.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/source/compiler/sc2.c b/source/compiler/sc2.c index 2a13c15..7330e73 100644 --- a/source/compiler/sc2.c +++ b/source/compiler/sc2.c @@ -2630,7 +2630,7 @@ static void symbol_cache_add(symbol *sym,symbol2 *new_cache_sym) if (new_cache_sym==NULL) { new_cache_sym=(symbol2 *)malloc(sizeof(symbol2)); if (new_cache_sym==NULL) - error(103); /* insufficient memory */ + error(103); /* insufficient memory */ new_cache_sym->symbol=sym; } new_cache_sym->next=NULL; @@ -2652,9 +2652,11 @@ static symbol2 *symbol_cache_remove(symbol *sym,int free_cache_sym) symbol2 *parent_cache_sym=NULL; cache_sym=hashmap_get(&symbol_cache_map,sym->name); - if (cache_sym==NULL) - return NULL; - while (cache_sym->symbol!=sym) { + for ( ;; ) { + if (cache_sym==NULL) + return NULL; + if (cache_sym->symbol==sym) + break; parent_cache_sym=cache_sym; cache_sym=cache_sym->next; } @@ -2664,7 +2666,7 @@ static symbol2 *symbol_cache_remove(symbol *sym,int free_cache_sym) } else { hashmap_remove(&symbol_cache_map,sym->name); if (cache_sym->next!=NULL) - if (hashmap_put(&symbol_cache_map,sym->name,cache_sym->next)) + if (hashmap_put(&symbol_cache_map,sym->name,cache_sym->next)==NULL) error(103); /* insufficient memory */ } if (free_cache_sym) { @@ -3157,14 +3159,23 @@ SC_FUNC int getlabel(void) SC_FUNC char *itoh(ucell val) { static const char hex[16]= - { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; +#if PAWN_CELL_SIZE==16 + static char itohstr[5]= + {'\0','\0','\0','\0','\0'}; + char *ptr=&itohstr[3]; +#elif PAWN_CELL_SIZE==32 + static char itohstr[9]= + {'\0','\0','\0','\0','\0','\0','\0','\0','\0'}; + char *ptr=&itohstr[7]; +#elif PAWN_CELL_SIZE==64 static char itohstr[17]= - { '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0' }; + {'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'}; char *ptr=&itohstr[15]; - -#if PAWN_CELL_SIZE>64 +#else #error Unsupported cell size #endif + do { *ptr-- = hex[val&(ucell)0x0f]; } while ((val>>=4)!=0); From dc21aec45d6b5cac46f1ee9f2a7fc38a39ecce19 Mon Sep 17 00:00:00 2001 From: Daniel_Cortez Date: Thu, 19 Oct 2017 16:14:32 +0700 Subject: [PATCH 12/16] Set cache_sym->next to NULL in symbol_cache_remove() --- source/compiler/sc2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/compiler/sc2.c b/source/compiler/sc2.c index 7330e73..9f4f0b4 100644 --- a/source/compiler/sc2.c +++ b/source/compiler/sc2.c @@ -2632,8 +2632,8 @@ static void symbol_cache_add(symbol *sym,symbol2 *new_cache_sym) if (new_cache_sym==NULL) error(103); /* insufficient memory */ new_cache_sym->symbol=sym; + new_cache_sym->next=NULL; } - new_cache_sym->next=NULL; cache_sym=hashmap_get(&symbol_cache_map,sym->name); if (cache_sym==NULL) { @@ -2673,6 +2673,7 @@ static symbol2 *symbol_cache_remove(symbol *sym,int free_cache_sym) free(cache_sym); return NULL; } + cache_sym->next=NULL; return cache_sym; } From 7363feb6126270cc413fae434a97076f909a15d8 Mon Sep 17 00:00:00 2001 From: Alex Martin Date: Thu, 19 Oct 2017 21:05:50 +0200 Subject: [PATCH 13/16] change hashmap size --- source/compiler/sc1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index 85cf255..a63de8e 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -928,7 +928,7 @@ static void initglobals(void) litq=NULL; /* the literal queue */ glbtab.next=NULL; /* clear global variables/constants table */ loctab.next=NULL; /* " local " / " " */ - hashmap_init(&symbol_cache_map,hashmap_hash_string,hashmap_compare_string,10000000); // TODO: make sure this is big enough + hashmap_init(&symbol_cache_map,hashmap_hash_string,hashmap_compare_string,8388608); /* 2^23 */ tagname_tab.next=NULL;/* tagname table */ libname_tab.next=NULL;/* library table (#pragma library "..." syntax) */ From a4dbe023304f5b5b1358e36c125feb92051d426e Mon Sep 17 00:00:00 2001 From: Alex Martin Date: Thu, 19 Oct 2017 21:06:03 +0200 Subject: [PATCH 14/16] minor cleanup --- source/compiler/sc7.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/compiler/sc7.c b/source/compiler/sc7.c index c28ef87..f8d2c9d 100644 --- a/source/compiler/sc7.c +++ b/source/compiler/sc7.c @@ -1389,7 +1389,7 @@ SC_FUNC void stgwrite(const char *st) len=(stgbuf!=NULL) ? stglen : 0; st_len=strlen(st); CHECK_STGBUFFER(len+st_len+1); - memcpy(stgbuf+len,st,st_len+1); //strcat(stgbuf,st); + memcpy(stgbuf+len,st,st_len+1); len=len+st_len; stglen=len; if (len>0 && stgbuf[len-1]=='\n') { From aa9f589bf52bb8d6741cdde745f54ffbf2ea3951 Mon Sep 17 00:00:00 2001 From: Daniel_Cortez Date: Fri, 20 Oct 2017 23:02:34 +0700 Subject: [PATCH 15/16] Memoize the address of the '__line' symbol instead of searching for it in loctab/glbtab every new line --- source/compiler/sc.h | 2 +- source/compiler/sc1.c | 3 ++- source/compiler/sc2.c | 3 ++- source/compiler/sc4.c | 9 --------- source/compiler/scvars.c | 1 + 5 files changed, 6 insertions(+), 12 deletions(-) diff --git a/source/compiler/sc.h b/source/compiler/sc.h index 72fe01d..84d33cf 100644 --- a/source/compiler/sc.h +++ b/source/compiler/sc.h @@ -617,7 +617,6 @@ SC_FUNC void setline(int chkbounds); SC_FUNC void setfiledirect(char *name); SC_FUNC void setfileconst(char *name); SC_FUNC void setlinedirect(int line); -SC_FUNC void setlineconst(int line); SC_FUNC void setlabel(int index); SC_FUNC void markexpr(optmark type,const char *name,cell offset); SC_FUNC void startfunc(char *fname); @@ -794,6 +793,7 @@ SC_FUNC int state_conflict_id(int listid1,int listid2); SC_VDECL symbol loctab; /* local symbol table */ SC_VDECL symbol glbtab; /* global symbol table */ SC_VDECL struct hashmap symbol_cache_map; +SC_VDECL symbol *line_sym; SC_VDECL cell *litq; /* the literal queue */ SC_VDECL unsigned char pline[]; /* the line read from the input file */ SC_VDECL const unsigned char *lptr;/* points to the current position in "pline" */ diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index a63de8e..80a3234 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -761,6 +761,7 @@ cleanup: delete_symbols(&loctab,0,TRUE,TRUE); /* delete local variables if not yet * done (i.e. on a fatal error) */ delete_symbols(&glbtab,0,TRUE,TRUE); + line_sym=NULL; hashmap_destroy(&symbol_cache_map); delete_consttable(&tagname_tab); delete_consttable(&libname_tab); @@ -1512,7 +1513,7 @@ static void setconstants(void) add_builtin_constant("__Pawn",VERSION_INT,sGLOBAL,0); add_builtin_constant("__PawnBuild",VERSION_BUILD,sGLOBAL,0); - add_builtin_constant("__line",0,sGLOBAL,0); + line_sym=add_builtin_constant("__line",0,sGLOBAL,0); add_builtin_constant("__compat",pc_compat,sGLOBAL,0); debug=0; diff --git a/source/compiler/sc2.c b/source/compiler/sc2.c index 9f4f0b4..233a40a 100644 --- a/source/compiler/sc2.c +++ b/source/compiler/sc2.c @@ -431,7 +431,8 @@ static void readline(unsigned char *line) line+=strlen((char*)line); } /* if */ fline+=1; - setlineconst(fline); + assert(line_sym!=NULL); + line_sym->addr=fline; } while (num>=0 && cont); } diff --git a/source/compiler/sc4.c b/source/compiler/sc4.c index 4124883..df22516 100644 --- a/source/compiler/sc4.c +++ b/source/compiler/sc4.c @@ -287,15 +287,6 @@ SC_FUNC void setlinedirect(int line) } /* if */ } -SC_FUNC void setlineconst(int line) -{ - symbol *sym; - - sym=findconst("__line",NULL); - assert(sym!=NULL); - sym->addr=fline; -} - /* setlabel * * Post a code label (specified as a number), on a new line. diff --git a/source/compiler/scvars.c b/source/compiler/scvars.c index a96df7c..1ca6054 100644 --- a/source/compiler/scvars.c +++ b/source/compiler/scvars.c @@ -34,6 +34,7 @@ SC_VDEFINE symbol loctab; /* local symbol table */ SC_VDEFINE symbol glbtab; /* global symbol table */ SC_VDEFINE struct hashmap symbol_cache_map; +SC_VDEFINE symbol *line_sym=NULL; SC_VDEFINE cell *litq; /* the literal queue */ SC_VDEFINE unsigned char pline[sLINEMAX+1]; /* the line read from the input file */ SC_VDEFINE const unsigned char *lptr; /* points to the current position in "pline" */ From 08f760fe283ce368d69f96294de0dfe694dd6809 Mon Sep 17 00:00:00 2001 From: Alex Martin Date: Sun, 22 Oct 2017 10:57:15 +0200 Subject: [PATCH 16/16] use more recent version of Visual Studio --- appveyor.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 4b13ef6..f5753ee 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,10 +1,11 @@ version: '{build}' +image: Visual Studio 2015 configuration: - RelWithDebInfo before_build: - - cmake -G "Visual Studio 10 2010" source/compiler -DCPACK_GENERATOR=ZIP + - cmake -G "Visual Studio 14 2015" source/compiler -DCPACK_GENERATOR=ZIP build_script: - cmake --build . --config %CONFIGURATION%