Add an option that encodes standard I/O functions as unknown fns
MODIFY
llvm_util/known_fns.cpp
llvm_util/known_fns.cpp
#include "llvm_util/utils.h"
#include "ir/function.h"
#include "ir/instr.h"
+#include "util/config.h"
#include "llvm/IR/Constants.h"
#include "llvm/Analysis/MemoryBuiltins.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
@@ -15,6 +16,7 @@ using namespace std;
#define RETURN_KNOWN(op) return { op, true }
#define RETURN_FAIL_KNOWN() return { nullptr, true }
+#define RETURN_FAIL_UNKNOWN() return { nullptr, false }
namespace llvm_util {
@@ -40,7 +42,33 @@ known_call(llvm::CallInst &i, const llvm::TargetLibraryInfo &TLI,
auto decl = i.getCalledFunction();
llvm::LibFunc libfn;
if (!decl || !TLI.getLibFunc(*decl, libfn) || !TLI.has(libfn))
- return { nullptr, false };
+ RETURN_FAIL_UNKNOWN();
+
+ if (util::config::io_nobuiltin) {
+ switch (libfn) {
+ case llvm::LibFunc_printf:
+ case llvm::LibFunc_putc:
+ case llvm::LibFunc_putchar:
+ case llvm::LibFunc_puts:
+ case llvm::LibFunc_scanf:
+ case llvm::LibFunc_fclose:
+ case llvm::LibFunc_ferror:
+ case llvm::LibFunc_fgetc:
+ case llvm::LibFunc_fprintf:
+ case llvm::LibFunc_fputc:
+ case llvm::LibFunc_fputs:
+ case llvm::LibFunc_fread:
+ case llvm::LibFunc_fscanf:
+ case llvm::LibFunc_fwrite:
+ case llvm::LibFunc_lstat:
+ case llvm::LibFunc_perror:
+ case llvm::LibFunc_read:
+ case llvm::LibFunc_write:
+ RETURN_FAIL_UNKNOWN();
+ default:
+ break;
+ }
+ }
switch (libfn) {
case llvm::LibFunc_memset: // void* memset(void *ptr, int val, size_t bytes)

MODIFY
scripts/opt-alive.sh
scripts/opt-alive.sh
@@ -10,15 +10,23 @@ set -e
# attributor, functionattrs: inter procedural pass that deduces and/or propagates attributes
# metarenamer: anonymizes function names
PASSES="argpromotion deadargelim globalopt hotcoldsplit inline ipconstprop ipsccp mergefunc partial-inliner tbaa loop-extract extract-blocks safe-stack place-safepoints attributor functionattrs metarenamer lowertypetests extract-blocks openmpopt prune-eh -Os -Oz -O1 -O2 -O3"
+PASSES_SIMPLIFYLIB="-instcombine"
TV="-tv"
-for p in $PASSES; do
+IO_NOBUILTIN="-tv-io-nobuiltin"
for arg in $@; do
+ for p in $PASSES; do
if [[ $arg == *"$p"* ]]; then
TV=""
break
fi
done
+ for p in $PASSES_SIMPLIFYLIB; do
+ if [[ $arg == "$p" ]]; then
+ IO_NOBUILTIN=""
+ break
+ fi
+ done
done
if [[ "$OSTYPE" == "darwin"* ]]; then
@@ -28,4 +36,4 @@ else
# Linux, Cygwin/Msys, or Win32?
TV_SHAREDLIB=tv.so
fi
-timeout 1000 $HOME/llvm/build/bin/opt -load=$HOME/alive2/build/tv/$TV_SHAREDLIB -tv-exit-on-error $TV $@ $TV -tv-smt-to=10000 -tv-report-dir=$HOME/alive2/build/logs -tv-smt-stats
+timeout 1000 $HOME/llvm/build/bin/opt -load=$HOME/alive2/build/tv/$TV_SHAREDLIB -tv-exit-on-error $TV $@ $TV -tv-smt-to=10000 -tv-report-dir=$HOME/alive2/build/logs -tv-smt-stats $IO_NOBUILTIN

ADD
tests/alive-tv/printf.srctgt.ll
+; TEST-ARGS: -io-nobuiltin
+@.str = private unnamed_addr constant [3 x i8] c"%d\00", align 1
+
+define i32 @src() {
+entry:
+ %r = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), i32 10)
+ ret i32 %r
+}
+
+define i32 @tgt() {
+entry:
+ %r = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), i32 10)
+ ret i32 %r
+}
+
+declare i32 @printf(i8* nocapture readonly, ...)

MODIFY
tools/alive-tv.cpp
tools/alive-tv.cpp
@@ -98,6 +98,11 @@ static llvm::cl::opt<unsigned> opt_omit_array_size(
"this number"),
llvm::cl::cat(opt_alive), llvm::cl::init(-1));
+static llvm::cl::opt<bool> opt_io_nobuiltin(
+ "io-nobuiltin",
+ llvm::cl::desc("Encode standard I/O functions as an unknown function"),
+ llvm::cl::cat(opt_alive), llvm::cl::init(false));
+
static llvm::cl::opt<unsigned> opt_max_mem(
"max-mem", llvm::cl::desc("Max memory (approx)"),
llvm::cl::cat(opt_alive), llvm::cl::init(1024), llvm::cl::value_desc("MB"));
@@ -393,6 +398,7 @@ convenient way to demonstrate an existing optimizer bug.
smt::set_query_timeout(to_string(opt_smt_to));
smt::set_memory_limit((uint64_t)opt_max_mem * 1024 * 1024);
config::skip_smt = opt_smt_skip;
+ config::io_nobuiltin = opt_io_nobuiltin;
config::symexec_print_each_value = opt_se_verbose;
config::disable_undef_input = opt_disable_undef;
config::disable_poison_input = opt_disable_poison;

MODIFY
tv/tv.cpp
tv/tv.cpp
@@ -105,6 +105,11 @@ llvm::cl::opt<unsigned> opt_omit_array_size(
"this number"),
llvm::cl::init(-1));
+llvm::cl::opt<bool> opt_io_nobuiltin(
+ "tv-io-nobuiltin",
+ llvm::cl::desc("Encode standard I/O functions as an unknown function"),
+ llvm::cl::init(false));
+
ostream *out;
ofstream out_file;
string report_filename;
@@ -248,6 +253,7 @@ struct TVPass final : public llvm::FunctionPass {
smt::set_query_timeout(to_string(opt_smt_to));
smt::set_memory_limit(opt_max_mem * 1024 * 1024);
config::skip_smt = opt_smt_skip;
+ config::io_nobuiltin = opt_io_nobuiltin;
config::symexec_print_each_value = opt_se_verbose;
config::disable_undef_input = opt_disable_undef_input;
config::disable_poison_input = opt_disable_poison_input;

MODIFY
util/config.cpp
util/config.cpp
@@ -12,6 +12,7 @@ namespace util::config {
bool symexec_print_each_value = false;
bool skip_smt = false;
+bool io_nobuiltin = false;
bool disable_poison_input = false;
bool disable_undef_input = false;
bool debug = false;

MODIFY
util/config.h
util/config.h
@@ -11,6 +11,8 @@ extern bool symexec_print_each_value;
extern bool skip_smt;
+extern bool io_nobuiltin;
+
extern bool disable_poison_input;
extern bool disable_undef_input;

Add an option that encodes standard I/O functions as unknown fns (#398)