Commit 4af99ad0 authored by Cédric VALENSI's avatar Cédric VALENSI

Sync

parent ea99fa9f
...@@ -441,7 +441,7 @@ INSTALL( PROGRAMS maqao ...@@ -441,7 +441,7 @@ INSTALL( PROGRAMS maqao
SET(server_path "${CMAKE_CURRENT_SOURCE_DIR}/src/plugins/built_in") SET(server_path "${CMAKE_CURRENT_SOURCE_DIR}/src/plugins/built_in")
ADD_CUSTOM_TARGET(distclean DEPENDS ADD_CUSTOM_TARGET(distclean DEPENDS
# MAQAO core # MAQAO core
distclean_maqao distclean_madras distclean_madrasAPI distclean_libdwarf distclean_maqao distclean_madras distclean_libtroll distclean_libdwarf
# MAQAO Lua API # MAQAO Lua API
${DISTCLEAN_LUA} distclean_builtin distclean_abstractobjects distclean_crosscompil ${DISTCLEAN_LUA} distclean_builtin distclean_abstractobjects distclean_crosscompil
# MAQAO doc # MAQAO doc
......
...@@ -601,7 +601,7 @@ WARN_LOGFILE = @CMAKE_SOURCE_DIR@/doc/internals/doxygen.errors ...@@ -601,7 +601,7 @@ WARN_LOGFILE = @CMAKE_SOURCE_DIR@/doc/internals/doxygen.errors
# directories like "/usr/src/myproject". Separate the files or directories # directories like "/usr/src/myproject". Separate the files or directories
# with spaces. # with spaces.
INPUT = @CMAKE_SOURCE_DIR@/doc/internals/CoreDevGuide @CMAKE_SOURCE_DIR@/src/common/libmcommon.h @CMAKE_SOURCE_DIR@/src/common/libmasm.h @CMAKE_SOURCE_DIR@/src/analyze/libmcore.h @CMAKE_SOURCE_DIR@/src/madras/libmtroll.h @CMAKE_SOURCE_DIR@/src/madras/libmdisass.h @CMAKE_SOURCE_DIR@/src/madras/libmpatch.h @CMAKE_SOURCE_DIR@/src/madras/fsmutils.h @CMAKE_SOURCE_DIR@/src/madrasAPI/libmadras.h @CMAKE_SOURCE_DIR@/src/maqao/libmmaqao.h INPUT = @CMAKE_SOURCE_DIR@/doc/internals/CoreDevGuide @CMAKE_SOURCE_DIR@/src/common/libmcommon.h @CMAKE_SOURCE_DIR@/src/asm/libmasm.h @CMAKE_SOURCE_DIR@/src/analyze/libmcore.h @CMAKE_SOURCE_DIR@/src/madras/libtroll/libmtroll.h @CMAKE_SOURCE_DIR@/src/madras/disass/libmdisass.h @CMAKE_SOURCE_DIR@/src/madras/patch/libmpatch.h @CMAKE_SOURCE_DIR@/src/madras/disass/fsmutils.h @CMAKE_SOURCE_DIR@/src/madras/libmadras.h @CMAKE_SOURCE_DIR@/src/maqao/libmmaqao.h
# This tag can be used to specify the character encoding of the source files # This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
......
...@@ -34,8 +34,8 @@ FILE(GLOB ...@@ -34,8 +34,8 @@ FILE(GLOB
# ---- Specify paths to sub-directories # ---- Specify paths to sub-directories
ADD_SUBDIRECTORY(${LUA_DIR}) ADD_SUBDIRECTORY(${LUA_DIR})
ADD_SUBDIRECTORY(common) ADD_SUBDIRECTORY(common)
ADD_SUBDIRECTORY(asm)
ADD_SUBDIRECTORY(madras) ADD_SUBDIRECTORY(madras)
ADD_SUBDIRECTORY(madrasAPI)
ADD_SUBDIRECTORY(analyze) ADD_SUBDIRECTORY(analyze)
ADD_SUBDIRECTORY(plugins) ADD_SUBDIRECTORY(plugins)
ADD_SUBDIRECTORY(maqao) ADD_SUBDIRECTORY(maqao)
...@@ -135,6 +135,10 @@ typedef struct cntxt_s { ...@@ -135,6 +135,10 @@ typedef struct cntxt_s {
queue_t* current_CC; //queue of block. Each block is an entry of current CC queue_t* current_CC; //queue of block. Each block is an entry of current CC
queue_t** bflags; //array of queue, one per block queue_t** bflags; //array of queue, one per block
queue_t* CC_to_remove; //array of queue to remove, one per CC queue_t* CC_to_remove; //array of queue to remove, one per CC
// Index on CCs to speedup lookup
hashtable_t* current_CC_ht; //hashtable of block. Each block is an entry of current CC
hashtable_t** bflags_ht; //array of hashtable, one per block
} cntxt_t; } cntxt_t;
/** /**
...@@ -196,13 +200,16 @@ static void _DFS_func(graph_t* node, void* context) ...@@ -196,13 +200,16 @@ static void _DFS_func(graph_t* node, void* context)
if (cntxt->bflags[b->id] == NULL) { if (cntxt->bflags[b->id] == NULL) {
cntxt->bflags[b->id] = cntxt->current_CC; cntxt->bflags[b->id] = cntxt->current_CC;
cntxt->bflags_ht[b->id] = cntxt->current_CC_ht;
} else { } else {
block_t *CC_entry = queue_peek_head(cntxt->current_CC);
//Means current block has been traversed twice //Means current block has been traversed twice
//Add current CC entry into the CC saved in cntxt->bflags[b->id] //Add current CC entry into the CC saved in cntxt->bflags[b->id]
if (queue_lookup(cntxt->bflags[b->id], &direct_equal, if (hashtable_lookup(cntxt->bflags_ht[b->id], CC_entry) == NULL) {
queue_peek_head(cntxt->current_CC)) == NULL) queue_add_tail(cntxt->bflags[b->id], CC_entry);
queue_add_tail(cntxt->bflags[b->id], hashtable_insert (cntxt->bflags_ht[b->id], CC_entry, CC_entry);
queue_peek_head(cntxt->current_CC)); }
if (queue_lookup(cntxt->CC_to_remove, &direct_equal, if (queue_lookup(cntxt->CC_to_remove, &direct_equal,
cntxt->current_CC) == NULL) cntxt->current_CC) == NULL)
queue_add_head(cntxt->CC_to_remove, cntxt->current_CC); queue_add_head(cntxt->CC_to_remove, cntxt->current_CC);
...@@ -220,6 +227,7 @@ static void _DFS_main(graph_t* node, void* context) ...@@ -220,6 +227,7 @@ static void _DFS_main(graph_t* node, void* context)
cntxt_t* cntxt = (cntxt_t*) context; cntxt_t* cntxt = (cntxt_t*) context;
block_t* b = node->data; block_t* b = node->data;
cntxt->bflags[b->id] = cntxt->current_CC; cntxt->bflags[b->id] = cntxt->current_CC;
cntxt->bflags_ht[b->id] = cntxt->current_CC_ht;
} }
/** /**
...@@ -276,6 +284,9 @@ void lcore_analyze_connected_components(asmfile_t *asmfile) ...@@ -276,6 +284,9 @@ void lcore_analyze_connected_components(asmfile_t *asmfile)
// and add each traversed block in the CC. If a block belongs to several CC, // and add each traversed block in the CC. If a block belongs to several CC,
// these CC are merged into a single one with multiple entries // these CC are merged into a single one with multiple entries
// Recycled structures
hashtable_t* marks = hashtable_new(&direct_hash, &direct_equal);
// Iterate over asmfile functions // Iterate over asmfile functions
FOREACH_INQUEUE(asmfile->functions, it_func) { FOREACH_INQUEUE(asmfile->functions, it_func) {
fct_t* func = GET_DATA_T(fct_t*, it_func); fct_t* func = GET_DATA_T(fct_t*, it_func);
...@@ -306,26 +317,33 @@ void lcore_analyze_connected_components(asmfile_t *asmfile) ...@@ -306,26 +317,33 @@ void lcore_analyze_connected_components(asmfile_t *asmfile)
cntxt_t cntxt; cntxt_t cntxt;
cntxt.f = func; cntxt.f = func;
cntxt.current_CC = NULL; cntxt.current_CC = NULL;
cntxt.current_CC_ht = NULL;
cntxt.bflags = lc_malloc0( cntxt.bflags = lc_malloc0(
queue_length(func->blocks) * sizeof(queue_t*)); queue_length(func->blocks) * sizeof(queue_t*));
cntxt.bflags_ht = lc_malloc0(
queue_length(func->blocks) * sizeof(hashtable_t*));
cntxt.CC_to_remove = queue_new(); cntxt.CC_to_remove = queue_new();
queue_t *CC_ht_to_remove = queue_new();
FOREACH_INQUEUE(func->components, it_cc) { FOREACH_INQUEUE(func->components, it_cc) {
queue_t* cc = GET_DATA_T(queue_t*, it_cc); queue_t* cc = GET_DATA_T(queue_t*, it_cc);
block_t* b = queue_peek_head(cc); block_t* b = queue_peek_head(cc);
cntxt.current_CC = cc; cntxt.current_CC = cc;
cntxt.current_CC_ht = hashtable_new (&direct_hash, &direct_equal);
queue_add_tail (CC_ht_to_remove, cntxt.current_CC_ht);
FOREACH_INQUEUE(cc, it_bl) {
block_t *block = GET_DATA_T(block_t*, it_bl);
hashtable_insert (cntxt.current_CC_ht, block, block);
}
if (pos == 0) { // If first CC (the main one), just marks blocks if (pos == 0) { // If first CC (the main one), just marks blocks
hashtable_t* marks = hashtable_new(&direct_hash, &direct_equal);
__traverse_CFG(b->cfg_node, &cntxt, marks, &_DFS_main); __traverse_CFG(b->cfg_node, &cntxt, marks, &_DFS_main);
hashtable_free(marks, NULL, NULL);
} else { // Else, check for if traversed block if it has already been traversed } else { // Else, check for if traversed block if it has already been traversed
// (during another CC analysis) // (during another CC analysis)
// If yes, merge current CC with the CC containing the block // If yes, merge current CC with the CC containing the block
hashtable_t* marks = hashtable_new(&direct_hash, &direct_equal);
__traverse_CFG(b->cfg_node, &cntxt, marks, &_DFS_func); __traverse_CFG(b->cfg_node, &cntxt, marks, &_DFS_func);
hashtable_free(marks, NULL, NULL);
} }
hashtable_flush (marks, NULL, NULL);
pos++; pos++;
} }
...@@ -336,8 +354,20 @@ void lcore_analyze_connected_components(asmfile_t *asmfile) ...@@ -336,8 +354,20 @@ void lcore_analyze_connected_components(asmfile_t *asmfile)
queue_free(cc, NULL); queue_free(cc, NULL);
} }
queue_free(cntxt.CC_to_remove, NULL); queue_free(cntxt.CC_to_remove, NULL);
FOREACH_INQUEUE(CC_ht_to_remove, it_cc2) {
hashtable_t* cc_ht = GET_DATA_T(hashtable_t*, it_cc2);
hashtable_free(cc_ht, NULL, NULL);
}
queue_free(CC_ht_to_remove, NULL);
lc_free(cntxt.bflags); lc_free(cntxt.bflags);
lc_free(cntxt.bflags_ht);
} }
} }
// recycled structures
hashtable_free (marks, NULL, NULL);
asmfile->analyze_flag |= COM_ANALYZE; asmfile->analyze_flag |= COM_ANALYZE;
} }
...@@ -246,7 +246,7 @@ static void connect_nodes(graph_t *src, graph_t *dst, char *kind, int distance, ...@@ -246,7 +246,7 @@ static void connect_nodes(graph_t *src, graph_t *dst, char *kind, int distance,
/* Fills data_dep fields */ /* Fills data_dep fields */
data_dep->distance = distance; data_dep->distance = distance;
insnext_t *ext = insn_get_ext(src->data); intel_ooo_t *ext = insn_get_ext(src->data);
if (ext != NULL) { if (ext != NULL) {
data_dep->latency = min_latency ? ext->latency.min : ext->latency.max; data_dep->latency = min_latency ? ext->latency.min : ext->latency.max;
} else } else
......
...@@ -433,6 +433,67 @@ static void lcore_loop_find_orphan_CC(asmfile_t *asmfile) ...@@ -433,6 +433,67 @@ static void lcore_loop_find_orphan_CC(asmfile_t *asmfile)
} }
} }
int _fix_loop_entries (asmfile_t *asmfile)
{
int nb_added_entries = 0;
DBGMSG0("Fixing loops entries\n");
// Analyze all functions
FOREACH_INQUEUE(asmfile->functions, funciter)
{
fct_t* f = GET_DATA_T(fct_t*, funciter);
// Analyse all loops
FOREACH_INQUEUE(f->loops, loopiter)
{
loop_t* l = GET_DATA_T(loop_t*, loopiter);
// Analyze all blocks in the loop
FOREACH_INQUEUE(l->blocks, blockiter)
{
block_t* b = GET_DATA_T (block_t*, blockiter);
graph_t* cfg_b = block_get_CFG_node (b);
// For each predecessor in the CFG, check if it is in the loop
FOREACH_INLIST (cfg_b->in, initer)
{
edge_t* edge = GET_DATA_T (edge_t*, initer);
graph_t* cfg_pred = edge->from;
block_t* pred = cfg_pred->data;
char is_in_loop = FALSE;
FOREACH_INQUEUE(l->blocks, blockiter2)
{
block_t* b = GET_DATA_T (block_t*, blockiter2);
if (block_get_id (pred) == block_get_id (b))
{
is_in_loop = TRUE;
break;
}
}
// If yes, continue
if (is_in_loop == TRUE)
continue;
// Check if it is in the entry list
if (list_lookup(loop_get_entries (l), b) == NULL)
{
// If not, add it in the entry list
l->entries = list_add_before(l->entries, b);
nb_added_entries = nb_added_entries + 1;
}
}
}
}
}
return (nb_added_entries);
}
/* /*
* Launches the loop detection analysis for all functions * Launches the loop detection analysis for all functions
* \param asmfile a valid asmfile * \param asmfile a valid asmfile
...@@ -459,4 +520,10 @@ void lcore_analyze_loops(asmfile_t *asmfile) ...@@ -459,4 +520,10 @@ void lcore_analyze_loops(asmfile_t *asmfile)
asmfile->analyze_flag |= LOO_ANALYZE; asmfile->analyze_flag |= LOO_ANALYZE;
//Special case where an independent loop is not recognized as a CC //Special case where an independent loop is not recognized as a CC
lcore_loop_find_orphan_CC(asmfile); lcore_loop_find_orphan_CC(asmfile);
// fix loop entries
// In some cases, some loop entries are not detected.
// As some of our analysis require loops have only one entry, it is
// necessary than loop entries are corrects
_fix_loop_entries (asmfile);
} }
...@@ -18,32 +18,31 @@ ...@@ -18,32 +18,31 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
## ##
CONFIGURE_FILE(madras_help.c.in ${CMAKE_CURRENT_SOURCE_DIR}/madras_help.c @ONLY) FILE(GLOB_RECURSE
files_asm
la*.c
la*.cpp
)
foreach(ARCH ${ARCHS}) foreach(ARCH ${ARCHS})
ADD_SUBDIRECTORY(${ARCH}) add_subdirectory(${ARCH})
endforeach(ARCH) endforeach(ARCH)
FILE(GLOB_RECURSE ### --- Create the masm objects --- ###
file_libmdrs # Create the masm object for STATIC libraries/binaries #
libmadras.c ADD_LIBRARY(masm-obj-static OBJECT ${files_asm})
) SET_TARGET_PROPERTIES(masm-obj-static PROPERTIES COMPILE_FLAGS "${C_STATIC_FLAGS}")
### --- Create the madras objects --- ###
# Create the madras object for static libraries/binaries #
ADD_LIBRARY(madras-obj-static OBJECT ${file_libmdrs})
SET_TARGET_PROPERTIES(madras-obj-static PROPERTIES COMPILE_FLAGS "${C_STATIC_FLAGS}")
# Create the madras object for dynamic libraries/binaries # # Create the masm object for DYNAMIC libraries/binaries #
ADD_LIBRARY(madras-obj-dyn OBJECT ${file_libmdrs}) ADD_LIBRARY(masm-obj-dyn OBJECT ${files_asm})
SET_TARGET_PROPERTIES(madras-obj-dyn PROPERTIES COMPILE_FLAGS "${C_DYNAMIC_FLAGS}") SET_TARGET_PROPERTIES(masm-obj-dyn PROPERTIES COMPILE_FLAGS "${C_DYNAMIC_FLAGS}")
IF (IS_STDCXX)
SET_TARGET_PROPERTIES (masm-obj-static PROPERTIES COMPILE_DEFINITIONS "IS_STDCXX")
SET_TARGET_PROPERTIES (masm-obj-dyn PROPERTIES COMPILE_DEFINITIONS "IS_STDCXX")
MESSAGE ("-- stdc++ available")
ENDIF (IS_STDCXX)
### --- Install headers --- ### ### --- Install the headers --- ###
FILE(COPY libmadras.h DESTINATION ${INCLUDE_OUTPUT_PATH}) FILE(COPY libmasm.h DESTINATION ${INCLUDE_OUTPUT_PATH})
### --- For cleaning files generated by cmake --- ###
ADD_CUSTOM_TARGET(distclean_madrasAPI
COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_CURRENT_SOURCE_DIR}/madras_help.c
)
##
# Copyright (C) 2004 - 2016 Université de Versailles Saint-Quentin-en-Yvelines (UVSQ)
#
# This file is part of MAQAO.
#
# MAQAO is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
##
SET (SELECTED_ARCH k1om)
### ===========================================================================
### --- Create the march objects --- ###
# List the source files #
SET (file_arch ${CMAKE_CURRENT_SOURCE_DIR}/${SELECTED_ARCH}_arch.c
${CMAKE_CURRENT_SOURCE_DIR}/${SELECTED_ARCH}_implicit_reg.c)
# Create the march object for STATIC libraries/binaries #
ADD_LIBRARY(march${SELECTED_ARCH}-obj-static OBJECT ${file_arch})
SET_TARGET_PROPERTIES(march${SELECTED_ARCH}-obj-static PROPERTIES COMPILE_FLAGS "${C_STATIC_FLAGS}")
IF (is_WINDOWS)
# Create the march object for DYNAMIC libraries/binaries #
ADD_LIBRARY(march${SELECTED_ARCH}-obj-dyn OBJECT ${file_arch})
SET_TARGET_PROPERTIES(march${SELECTED_ARCH}-obj-dyn PROPERTIES COMPILE_FLAGS "${C_DYNAMIC_FLAGS}")
ENDIF ()
### ===========================================================================
### --- Create the mext objects --- ###
# List the source files #
SET (file_ext ${CMAKE_CURRENT_SOURCE_DIR}/${SELECTED_ARCH}_ext.c)
# Create the mext object for STATIC libraries/binaries #
ADD_LIBRARY(mext${SELECTED_ARCH}-obj-static OBJECT ${file_ext})
SET_TARGET_PROPERTIES(mext${SELECTED_ARCH}-obj-static PROPERTIES COMPILE_FLAGS "${C_STATIC_FLAGS}")
IF (is_WINDOWS)
# Create the mext object for DYNAMIC libraries/binaries #
ADD_LIBRARY(mext${SELECTED_ARCH}-obj-dyn OBJECT ${file_ext})
SET_TARGET_PROPERTIES(mext${SELECTED_ARCH}-obj-dyn PROPERTIES COMPILE_FLAGS "${C_DYNAMIC_FLAGS}")
ENDIF ()
### --- Install the headers --- ###
FILE(COPY ${SELECTED_ARCH}_arch.h DESTINATION ${INCLUDE_OUTPUT_PATH})
...@@ -455,7 +455,27 @@ extern insn_t* k1om_insn_parse(char* strinsn); ...@@ -455,7 +455,27 @@ extern insn_t* k1om_insn_parse(char* strinsn);
* */ * */
extern queue_t* k1om_insnlist_parse(char* insn_list); extern queue_t* k1om_insnlist_parse(char* insn_list);
enum k1om_flag { F_OF, F_CF, F_PF, F_SF, F_ZF, F_AF}; ///////////////////////////////////////////////////////////////////////////////
// Operand //
///////////////////////////////////////////////////////////////////////////////
/*
* Parses an operand from a string representation of an instruction, based on a given architecture
* \param strinsn The string containing the representation of the instruction.
* \param pos Pointer to the index into the string at which the parsing starts. Will be updated to contain the
* index at which the operand ends (operand separator or end of string)
* \return The parsed operand, or NULL if the parsing failed
* */
extern oprnd_t* k1om_oprnd_parse(char* strinsn, int *pos);
enum k1om_flag {
F_OF,
F_CF,
F_PF,
F_SF,
F_ZF,
F_AF
};
int k1om_insn_altered_flags(insn_t *in, uint8_t *read, uint8_t *set, uint8_t *cleared, uint8_t * def, uint8_t *undef); int k1om_insn_altered_flags(insn_t *in, uint8_t *read, uint8_t *set, uint8_t *cleared, uint8_t * def, uint8_t *undef);
......
...@@ -220,6 +220,46 @@ graph_t* block_get_CFG_node(block_t* b) ...@@ -220,6 +220,46 @@ graph_t* block_get_CFG_node(block_t* b)
return (b != NULL) ? b->cfg_node : PTR_ERROR; return (b != NULL) ? b->cfg_node : PTR_ERROR;
} }
/*
* Returns predecessors (in the CFG) of a block
* \param b a block
* \return queue of blocks or PTR_ERROR if no CFG node
*/
queue_t * block_get_predecessors(block_t* b)
{
graph_t *CFG_node = block_get_CFG_node (b);
if (CFG_node == NULL) return PTR_ERROR;
queue_t *new = queue_new();
FOREACH_INLIST(CFG_node->in, iter) {
block_t *block = GET_DATA_T (edge_t *, iter)->from->data;
queue_add_tail (new, block);
}
return new;
}
/*
* Returns successors (in the CFG) of a block
* \param b a block
* \return queue of blocks or PTR_ERROR if no CFG node
*/
queue_t * block_get_successors(block_t* b)
{
graph_t *CFG_node = block_get_CFG_node (b);
if (CFG_node == NULL) return PTR_ERROR;
queue_t *new = queue_new();
FOREACH_INLIST(CFG_node->out, iter) {
block_t *block = GET_DATA_T (edge_t *, iter)->to->data;
queue_add_tail (new, block);
}
return new;
}
/* /*
* Retrieves the block domination node * Retrieves the block domination node
* \param b a block * \param b a block
...@@ -638,6 +678,75 @@ void block_get_src_lines (block_t *block, unsigned *min, unsigned *max) ...@@ -638,6 +678,75 @@ void block_get_src_lines (block_t *block, unsigned *min, unsigned *max)
} }
} }
/*
* Returns source regions for a set of blocks as a queue of strings.
* Source regions are formatted as "<file>: <start_line>-<end_line>"
* \param blocks queue of blocks
* \return queue of strings
*/
queue_t *blocks_get_src_regions (queue_t *blocks) {
queue_t *ret = queue_new();
hashtable_t *index = hashtable_new (&str_hash, &str_equal);
FOREACH_INQUEUE(blocks, it_b) {
block_t *block = GET_DATA_T(block_t *, it_b);
FOREACH_INSN_INBLOCK(block, iti) {
insn_t *insn = GET_DATA_T(insn_t*, iti);
char *file_path = insn_get_src_file (insn);
if (file_path == NULL) continue;
unsigned src_line = insn_get_src_line (insn);
if (src_line == 0) continue;
list_t *src_lines = hashtable_lookup (index, file_path);
if (src_lines == NULL) {
src_lines = list_new ((void *) (unsigned long) src_line);
hashtable_insert (index, file_path, src_lines);
} else {
src_lines = list_add_after (src_lines, (void *) (unsigned long) src_line);
}
}
}
FOREACH_INHASHTABLE(index,it_hn) {
char *src_file = GET_KEY(char *, it_hn);
list_t *src_lines = GET_DATA_T(list_t *, it_hn);
int min = -1, max = -1;
FOREACH_INLIST (src_lines, it_line) {
unsigned line = GET_DATA_T(unsigned long, it_line);
if (min == -1 || min > line) min = line;
if (max == -1 || max < line) max = line;
}
list_free (src_lines, NULL);
char *str = malloc (strlen (src_file)+20);
lc_sprintf (str, strlen (src_file)+20, "%s: %u-%u", src_file, min, max);
queue_add_tail (ret, str);
}
hashtable_free (index, NULL, NULL);
return ret;
}
/*
* Returns source regions for a block