Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions klee_stubs/klee/klee.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#ifndef KLEE_KLEE_H
#define KLEE_KLEE_H

#include <stddef.h>

/* Stub implementations of KLEE functions for compilation without KLEE */
static void klee_make_symbolic(void *addr, size_t nbytes, const char *name) {
(void)addr; (void)nbytes; (void)name;
}

static void klee_assert(int condition) {
(void)condition;
}

static int klee_range(int start, int end, const char *name) {
(void)name;
return start;
}

#endif /* KLEE_KLEE_H */
6 changes: 4 additions & 2 deletions src/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ def _preprocess(self):
orig_file = self.project_config.location_orig_file
project_temp_dir = self.project_config.location_temp_dir
if not os.path.exists(orig_file):
shutil.rmtree(project_temp_dir)
if os.path.exists(project_temp_dir):
shutil.rmtree(project_temp_dir)
err_msg = "File to analyze not found: %s" % orig_file
raise GameTimeError(err_msg)

Expand All @@ -148,7 +149,8 @@ def _preprocess(self):
for additional_file in additional_files:
project_temp_dir = self.project_config.location_temp_dir
if not os.path.exists(additional_file):
shutil.rmtree(project_temp_dir)
if os.path.exists(project_temp_dir):
shutil.rmtree(project_temp_dir)
err_msg = "External File to analyze not found: %s" % additional_file
raise GameTimeError(err_msg)

Expand Down
81 changes: 77 additions & 4 deletions src/backend/generate_executable.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
ArrayDecl,
Cast,
Typename,
Typedef,
)
import os
import sys
Expand Down Expand Up @@ -72,6 +73,24 @@ def __init__(self, ast, function_name, hexvalues):
self.new_main = None
self.arguments = []
self.hexvalues = hexvalues
self.typedef_map = self._build_typedef_map(ast)

def _build_typedef_map(self, ast):
"""Build a map from typedef names to their resolved type nodes."""
typedef_map = {}
for node in ast.ext:
if isinstance(node, Typedef):
typedef_map[node.name] = node.type
return typedef_map

def _resolve_typedef(self, type_node):
"""If type_node is a typedef'd IdentifierType, resolve it to the underlying type."""
if (isinstance(type_node, TypeDecl) and
isinstance(type_node.type, IdentifierType)):
type_name = " ".join(type_node.type.names)
if type_name in self.typedef_map:
return self.typedef_map[type_name]
return type_node

def visit_func(self, node):
"""
Expand Down Expand Up @@ -403,6 +422,35 @@ def generate_array_declaration(self, name, array_type_node, values):
values_str = ", ".join(values_chunk)
return f"{element_type_str} {name}[] = {{ {values_str} }};"

def generate_typedef_array_declaration(self, name, orig_type_node, resolved_array_node, value):
"""
Generates the variable declaration for a typedef'd array type.

Uses the typedef name for the declaration but generates a flat initializer
list based on the resolved array element type and size.
"""
# Get the typedef name from the original type node
typedef_name = " ".join(orig_type_node.type.names)
# Get the base element type from the resolved array
element_type_str = self.get_element_type_str(resolved_array_node)
elem_size = TYPE_SIZES.get(element_type_str, 4)
hex_digits_per_elem = elem_size * 2

# Strip 0x prefix and trailing whitespace
values_hex = value.strip()
if values_hex.startswith("0x"):
values_hex = values_hex[2:]

# Chunk by element size and reverse bytes within each element
values_chunk = []
for i in range(0, len(values_hex), hex_digits_per_elem):
chunk = values_hex[i : i + hex_digits_per_elem]
if len(chunk) == hex_digits_per_elem:
values_chunk.append("0x" + _mem_bytes_to_literal_hex(chunk))

values_str = ", ".join(values_chunk)
return f"{typedef_name} {name} = {{{values_str}}};"

def get_element_type_str(self, array_type_node):
"""
Get the base type of array type.
Expand Down Expand Up @@ -441,12 +489,17 @@ def gen_arguments(self, arg_types, arg_names, hex_values):
declarations = []

for type_node, name, value in zip(arg_types, arg_names, hex_values):
if self.is_primitive(type_node):
resolved = self._resolve_typedef(type_node)
if self.is_array(type_node):
decl = self.generate_array_declaration(name, type_node, value)
elif self.is_array(resolved):
decl = self.generate_typedef_array_declaration(
name, type_node, resolved, value
)
elif self.is_primitive(type_node):
decl = self.generate_primitive_declaration(name, type_node, value)
elif self.is_struct(type_node):
decl = self.generate_struct_declaration(name, type_node, value)
elif self.is_array(type_node):
decl = self.generate_array_declaration(name, type_node, value)
else:
raise NotImplementedError(
f"Type handling not implemented for {type(type_node)}"
Expand Down Expand Up @@ -511,11 +564,31 @@ def generate_executable(
with open(input_file, "r") as file:
original_c_content = file.read()

# Remove any existing main function definition to avoid conflicts
import re as _re
main_def_pat = r'(?:^[^\n\S]*(?:int|void)\s+main\s*\([^)]*\)\s*\{)'
main_match = _re.search(main_def_pat, original_c_content, flags=_re.MULTILINE)
if main_match:
brace_start = original_c_content.index('{', main_match.start())
depth = 0
i = brace_start
while i < len(original_c_content):
if original_c_content[i] == '{':
depth += 1
elif original_c_content[i] == '}':
depth -= 1
if depth == 0:
original_c_content = original_c_content[:main_match.start()] + original_c_content[i+1:]
break
i += 1
# Also remove forward declarations of main
original_c_content = _re.sub(r'^[^\n\S]*int\s+main\s*\([^)]*\)\s*;\s*\n?', '', original_c_content, flags=_re.MULTILINE)

# This must be included in we want to run flexpret backend (for printf)
if include_flexpret:
original_c_content = "#include <flexpret/flexpret.h> \n" + original_c_content
else:
original_c_content = "#include <time.h> \n" + original_c_content
original_c_content = "#include <stdio.h>\n#include <stdint.h>\n#include <time.h>\n" + original_c_content

# TODO: generate global variables, add the global timing function
original_c_content += timing_function_body
Expand Down
2 changes: 1 addition & 1 deletion src/pulp_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class Extremum(object):
# Default integer linear programming solver of the PuLP package.
"": ([pulp.LpSolverDefault.__class__] if pulp.LpSolverDefault is not None else []),
# CBC mixed integer linear programming solver.
"cbc": [pulp.COIN],
"cbc": [pulp.COIN_CMD],
# Version of CBC provided with the PuLP package.
"cbc-pulp": [pulp.PULP_CBC_CMD],
# IBM ILOG CPLEX Optimizer.
Expand Down
12 changes: 9 additions & 3 deletions src/smt_solver/smt.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,14 @@ def run_klee(klee_file):
klee_file : str
Path to the file modified for KLEE execution.
"""
run_klee_command = ["klee", klee_file]
subprocess.run(run_klee_command, check=True)
run_klee_command = ["klee", "--max-time=30", klee_file]
try:
subprocess.run(run_klee_command, check=True, timeout=60)
except subprocess.TimeoutExpired:
logger.warning("KLEE timed out after 60 seconds")
except subprocess.CalledProcessError:
# KLEE may exit non-zero when it times out via --max-time
pass


def extract_labels_from_file(filename):
Expand Down Expand Up @@ -193,7 +199,7 @@ def run_smt(
# Get the path to the smt_solver directory relative to this file
smt_solver_dir = os.path.dirname(os.path.abspath(__file__))
modify_bit_code_cpp_file = os.path.join(smt_solver_dir, "modify_bitcode.cpp")
modify_bit_code_exec_file = os.path.join(smt_solver_dir, "modify_bitcode")
modify_bit_code_exec_file = os.path.join(output_dir, "modify_bitcode")
modified_klee_file_bc = compile_and_run_cplusplus(
modify_bit_code_cpp_file,
modify_bit_code_exec_file,
Expand Down
37 changes: 34 additions & 3 deletions src/smt_solver/to_klee_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,15 @@ def format_for_klee(
c_code = c_code.lstrip("\n")

# Generate KLEE headers
klee_headers = (
"#include </opt/homebrew/include/klee/klee.h>\n#include <stdbool.h>\n"
)
klee_include_path = os.environ.get("KLEE_INCLUDE_PATH", "")
if klee_include_path:
klee_headers = (
f"#include <{klee_include_path}/klee/klee.h>\n#include <stdbool.h>\n"
)
else:
klee_headers = (
"#include <klee/klee.h>\n#include <stdbool.h>\n"
)

# Generate global boolean variables and initialize them to false/true
global_booleans = "\n"
Expand All @@ -32,6 +38,31 @@ def format_for_klee(
for i in range(total_number_of_labels - n):
global_booleans += f"bool conditional_var_{i + n} = true;\n"

# Remove any existing main function definition (with its body) from c_code
# This handles cases where the source file already has a main()
main_def_pattern = r'(?:^[^\n\S]*(?:int|void)\s+main\s*\([^)]*\)\s*\{)'
main_match = re.search(main_def_pattern, c_code, flags=re.MULTILINE)
if main_match and func_name != "main":
# Find the matching closing brace by counting braces
start = main_match.start()
brace_start = c_code.index('{', main_match.start())
depth = 0
i = brace_start
while i < len(c_code):
if c_code[i] == '{':
depth += 1
elif c_code[i] == '}':
depth -= 1
if depth == 0:
# Remove the main function (and any trailing newlines)
end = i + 1
c_code = c_code[:start] + c_code[end:]
break
i += 1

# Also remove forward declarations of main
c_code = re.sub(r'^[^\n\S]*int\s+main\s*\([^)]*\)\s*;\s*\n?', '', c_code, flags=re.MULTILINE)

# Generate main function
main_function = "int main() {\n"
function_pattern = rf"^[^\n\S]*\w+\s+{func_name}\s*\(([^)]*)\)\s*\{{"
Expand Down
2 changes: 1 addition & 1 deletion test/countnegative/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
gametime-project:
file:
location: countnegative.c
analysis-function: main
analysis-function: countnegative_sum
additional-files: []
start-label: null
end-label: null
Expand Down
10 changes: 5 additions & 5 deletions test/countnegative/countnegative.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ void countnegative_initialize( matrix Array )
{
register int OuterIndex, InnerIndex;

_Pragma( "loopbound min 20 max 20" )

for ( OuterIndex = 0; OuterIndex < MAXSIZE; OuterIndex++ )
_Pragma( "loopbound min 20 max 20" )

for ( InnerIndex = 0; InnerIndex < MAXSIZE; InnerIndex++ )
Array[ OuterIndex ][ InnerIndex ] = countnegative_randomInteger();
}
Expand Down Expand Up @@ -105,9 +105,9 @@ void countnegative_sum( matrix Array )
int Pcnt = 0;
int Ncnt = 0;

_Pragma( "loopbound min 20 max 20" )

for ( Outer = 0; Outer < MAXSIZE; Outer++ )
_Pragma( "loopbound min 20 max 20" )

for ( Inner = 0; Inner < MAXSIZE; Inner++ )
if ( Array[ Outer ][ Inner ] >= 0 ) {
Ptotal += Array[ Outer ][ Inner ];
Expand All @@ -126,7 +126,7 @@ void countnegative_sum( matrix Array )
/*
The main function
*/
void _Pragma( "entrypoint" ) countnegative_main ( void )
void countnegative_main ( void )
{
countnegative_sum( countnegative_array );
}
Expand Down
12 changes: 12 additions & 0 deletions test/ext_func/ext_func.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
int abs_val(int x) {
if (x < 0) return -x;
return x;
}

int test(int x){
if (abs_val(x) == 4) {
return 0;
} else {
return 1;
}
}
Loading