// -*- C++ -*- (c) 2014 Vladimír Štill #if GEN_LLVM || GEN_LLVM_CSDR || GEN_LLVM_PROB || GEN_LLVM_PTST #include <initializer_list> #include <brick-llvm.h> #include <brick-fs.h> #include <divine/utility/strings.h> #include <divine/utility/die.h> #include <divine/compile/env.h> #include <divine/compile/snapshot.h> #ifndef TOOLS_COMPILE_LLVM_H #define TOOLS_COMPILE_LLVM_H namespace divine { extern stringtable pdclib_list[]; extern stringtable libm_list[]; extern stringtable libunwind_list[]; extern stringtable libcxxabi_list[]; extern stringtable libcxx_list[]; namespace compile { static constexpr auto precompiledRuntime = "libdivinert.bc"; struct CompileLLVM { CompileLLVM( Env &env ) : _env( env ) { } void compile() try { if ( !_env.usePrecompiled.empty() ) { auto pre = ::llvm::ParseIRFile( brick::fs::joinPath( _env.usePrecompiled, precompiledRuntime ), err, ctx ); if ( !pre ) die( std::string( "Could not load precompiled library: " ) + err.getMessage().data() ); linker.load( pre ); } // create temporary directory to compile in brick::fs::TempDir tmpdir( "_divine-compile.XXXXXX", brick::fs::AutoDelete( !_env.keepBuildDir ) ); brick::fs::ChangeCwd rootDir( tmpdir.path ); // copy content of library files from memory to the directory prepareIncludes( llvm_h_list ); brick::fs::mkFilePath( "bits/pthreadtypes.h" ); brick::fs::writeFile( "bits/pthreadtypes.h" , "#include <pthread.h>" ); brick::fs::writeFile( "assert.h", "#include <divine.h>\n" ); /* override PDClib's assert.h */ brick::fs::mkFilePath( "divine/problem.def" ); brick::fs::writeFile( "divine/problem.def", src_llvm::llvm_problem_def_str ); if ( !_env.dontLink ) prepareIncludes( llvm_list ); // compile libraries std::string flags = "-D__divine__ -D_POSIX_C_SOURCE=2008098L -D_LITTLE_ENDIAN=1234 -D_BYTE_ORDER=1234 -emit-llvm " "-Qunused-arguments -nobuiltininc -nostdlibinc -nostdinc -Xclang -nostdsysteminc -nostdinc++ -g "; compileLibrary( "libpdc", pdclib_list, flags + " -D_PDCLIB_BUILD -I.." ); compileLibrary( "libm", libm_list, flags + " -I../libpdc -I." ); prepareIncludes( "libcxx", libcxx_list ); // cyclic dependency cxxabi <-> cxx compileLibrary( "libcxxabi", libcxxabi_list, flags + " -I../libpdc -I../libm " "-I.. -Iinclude -I../libcxx/std -I../libcxx -std=c++11 -fstrict-aliasing" ); compileLibrary( "libcxx", libcxx_list, flags + " -I../libpdc -I../libm " "-I../libcxxabi/include -I.. -Istd -I. -fstrict-aliasing -std=c++11 -fstrict-aliasing" ); flags += " -Ilibcxxabi/include -Ilibpdc -Ilibcxx/std -Ilibcxx -Ilibm "; if ( _env.usePrecompiled.empty() && !_env.dontLink ) { auto files = { "glue", "stubs", "entry", "pthread" }; auto filesCpp11 = { "fs", "fs-snapshot", "fs-manager", "fs-memory", "cxa_exception_divine" }; { std::ofstream sn( "fs-snapshot.cpp" ); brick::fs::ChangeCwd _( rootDir.oldcwd ); Snapshot::writeFile( sn, _env.snapshot, _env.stdin ); } for ( std::string f : files ) { _env.compileFile( f + ".cpp", " -c -I. -Ilibcxxabi " + flags + " -o " + f + ".bc" ); linkFile( f + ".bc" ); } for ( std::string f : filesCpp11 ) { _env.compileFile( f + ".cpp", " -std=c++11 -c -I. -Ilibcxxabi " + flags + " -o " + f + ".bc" ); linkFile( f + ".bc" ); } } if ( _env.librariesOnly ) { brick::llvm::writeModule( linker.get(), brick::fs::joinPath( rootDir.oldcwd, precompiledRuntime ) ); return; } flags += _env.flags; if ( _env.input.size() >= 2 && _env.dontLink && !_env.output.empty() ) die( "Cannot specify both -o and --dont-link with multiple files." ); for ( auto file : _env.input ) { auto compilename = brick::fs::replaceExtension( brick::fs::splitFileName( file ).second, ".bc" ); if ( _env.output.empty() && !_env.dontLink ) { // If -o and --dont-link are not specified, then choose the name of the first file in the list // for the target name. _env.output = compilename; } if ( brick::fs::takeExtension( file ) != ".bc" ) { _env.compileFile( brick::fs::joinPath( "..", file ), " -c -I. " + flags + " -o " + compilename ); if ( _env.dontLink ) { brick::fs::renameIfExists( compilename, _env.output.empty() ? ( brick::fs::joinPath( rootDir.oldcwd, compilename ) ) : ( brick::fs::joinPath( rootDir.oldcwd, _env.output ) ) ); } else linkFile( compilename ); } else if ( !_env.dontLink ) { brick::fs::ChangeCwd _( rootDir.oldcwd ); linkFile( file ); } } if ( !_env.dontLink ) { // we must enter list of root, symbols not accessible from these will be pruned // note: mem* functions must be here since clang may emit llvm instrinsic // instead of them and this intrinsic needs to be later lowered to symbol linker.prune( { "_divine_start", "main", "memmove", "memset", "memcpy", "llvm.global_ctors" }, brick::llvm::Prune::UnusedModules ); // AllUnused ); brick::llvm::writeModule( linker.get(), brick::fs::joinPath( rootDir.oldcwd, _env.output ) ); } } catch ( brick::fs::SystemException &ex ) { die( std::string( "FATAL: " ) + ex.what() ); } catch ( brick::fs::Exception &ex ) { die( std::string( "FATAL: " ) + ex.what() ); } template< typename Src > void prepareIncludes( std::string name, Src src ) { brick::fs::mkdirIfMissing( name, 0755 ); brick::fs::ChangeCwd _( name ); prepareIncludes( src ); } template< typename Src > void prepareIncludes( Src src ) { while ( src->n ) { brick::fs::mkFilePath( src->n ); brick::fs::writeFile( src->n, src->c ); ++src; } } template< typename Src > void compileLibrary( std::string name, Src src, std::string flags ) { std::vector< std::string > files; auto src_ = src; prepareIncludes( name, src ); brick::fs::ChangeCwd _( name ); if ( !_env.usePrecompiled.empty() || _env.dontLink ) return; /* we only need the headers from above */ src = src_; auto queue = _env.getQueue(); while ( src->n ) { if ( brick::string::endsWith( src->n, ".cc" ) || brick::string::endsWith( src->n, ".c" ) || brick::string::endsWith( src->n, ".cpp" ) || brick::string::endsWith( src->n, ".cxx" ) ) { queue.pushCompilation( src->n, " -c " + flags + " -I. -o " + src->n + ".bc" ); files.emplace_back( std::string( src->n ) + ".bc" ); } ++src; } queue.run(); for ( auto f : files ) linkFile( f ); } void linkFile( std::string file ) { std::cout << "linking " << file << std::endl; auto mod = ::llvm::ParseIRFile( file, err, ctx ); if ( !mod ) die( std::string( "Linker error: " ) + err.getMessage().data() ); linker.link( mod ); } private: Env &_env; ::llvm::LLVMContext ctx; ::llvm::SMDiagnostic err; brick::llvm::Linker linker; }; } } #endif // TOOLS_COMPILE_LLVM_H #endif // llvm