// -*- C++ -*- (c) 2012-2014 Petr Ročkai #include #include #include #include #include #include #include #include #include using namespace divine::llvm; using ::llvm::isa; using ::llvm::dyn_cast; using ::llvm::cast; bool ProgramInfo::isCodePointerConst( ::llvm::Value *val ) { return ( isa< ::llvm::Function >( val ) || isa< ::llvm::BlockAddress >( val ) || isa< ::llvm::BasicBlock >( val ) ); } bool ProgramInfo::isCodePointer( ::llvm::Value *val ) { if ( auto ty = dyn_cast< ::llvm::PointerType >( val->getType() ) ) return ty->getElementType()->isFunctionTy(); return isCodePointerConst( val ); } PC ProgramInfo::getCodePointer( ::llvm::Value *val ) { if ( auto B = dyn_cast< ::llvm::BasicBlock >( val ) ) { ASSERT( blockmap.count( B ) ); return blockmap[ B ]; } else if ( auto B = dyn_cast< ::llvm::BlockAddress >( val ) ) { ASSERT( blockmap.count( B->getBasicBlock() ) ); return blockmap[ B->getBasicBlock() ]; } else if ( auto F = dyn_cast< ::llvm::Function >( val ) ) { if ( !functionmap.count( F ) && builtin( F ) == NotBuiltin ) throw std::logic_error( "ProgramInfo::insert: " + std::string( "Unresolved symbol (function): " ) + F->getName().str() ); if ( builtin( F ) ) return PC( 0, 0 ); return PC( functionmap[ F ], 0 ); } return PC(); } namespace { int intrinsic_id( ::llvm::Value *v ) { auto insn = dyn_cast< ::llvm::Instruction >( v ); if ( !insn || insn->getOpcode() != ::llvm::Instruction::Call ) return ::llvm::Intrinsic::not_intrinsic; llvm::CallSite CS( insn ); auto f = CS.getCalledFunction(); if ( !f ) return ::llvm::Intrinsic::not_intrinsic; return f->getIntrinsicID(); } } void ProgramInfo::initValue( ::llvm::Value *val, ProgramInfo::Value &result ) { if ( val->getType()->isVoidTy() ) { result.width = 0; result.type = Value::Void; } else if ( isCodePointer( val ) ) { result.width = val->getType()->isPointerTy() ? TD.getTypeAllocSize( val->getType() ) : sizeof( PC ); result.type = Value::CodePointer; } else if ( val->getType()->isPointerTy() ) { result.width = TD.getTypeAllocSize( val->getType() ); result.type = Value::Pointer; } else { result.width = TD.getTypeAllocSize( val->getType() ); if ( val->getType()->isIntegerTy() ) result.type = Value::Integer; else if ( val->getType()->isFloatTy() || val->getType()->isDoubleTy() ) result.type = Value::Float; else result.type = Value::Aggregate; } if ( auto CDS = dyn_cast< ::llvm::ConstantDataSequential >( val ) ) { result.type = Value::Aggregate; result.width = CDS->getNumElements() * CDS->getElementByteSize(); } if ( isa< ::llvm::AllocaInst >( val ) || intrinsic_id( val ) == ::llvm::Intrinsic::stacksave ) result.type = ProgramInfo::Value::Alloca; } ProgramInfo::Value ProgramInfo::insert( int function, ::llvm::Value *val ) { if ( valuemap.find( val ) != valuemap.end() ) return valuemap.find( val )->second; makeFit( functions, function ); Value result; /* not seen yet, needs to be allocated */ initValue( val, result ); bool constglobal = true; auto infopair = std::make_pair( val->getType(), val->getValueName() ? val->getValueName()->getKey() : "" ); if ( result.width % framealign ) return Value(); /* ignore for now, later pass will assign this one */ if ( isCodePointerConst( val ) ) { makeConstant( result, getCodePointer( val ) ); doneInit.insert( val ); } else if ( auto GA = dyn_cast< ::llvm::GlobalAlias >( val ) ) result = insert( function, const_cast< ::llvm::GlobalValue * >( GA->resolveAliasedGlobal() ) ); else if ( auto C = dyn_cast< ::llvm::Constant >( val ) ) { result.global = true; if ( auto G = dyn_cast< ::llvm::GlobalVariable >( val ) ) { Value pointee; if ( !G->hasInitializer() ) throw std::logic_error( "ProgramInfo::insert: " + std::string( "Unresolved symbol (global variable): " ) + G->getValueName()->getKey().str() ); ASSERT( G->hasInitializer() ); initValue( G->getInitializer(), pointee ); if ( (pointee.constant = G->isConstant()) ) makeLLVMConstant( pointee, G->getInitializer() ); else { allocateValue( 0, pointee ); globalvars.emplace_back( globals.size(), pointee ); } globals.push_back( pointee ); Pointer p( false, globals.size() - 1, 0 ); makeConstant( result, p ); doneInit.insert( val ); valuemap.insert( std::make_pair( val, result ) ); if ( !G->isConstant() ) { storeConstant( pointee, G->getInitializer(), true ); constglobal = false; } } else makeLLVMConstant( result, C ); } else allocateValue( function, result ); if ( function && !result.global && !result.constant ) { if ( result.width ) this->functions[ function ].values.push_back( result ); } else { makeFit( constglobal ? constinfo : globalinfo, globals.size() ); (constglobal ? constinfo : globalinfo )[ globals.size() ] = infopair; globals.push_back( result ); } valuemap.insert( std::make_pair( val, result ) ); return result; } /* This is silly. */ ProgramInfo::Position ProgramInfo::lower( Position p ) { auto BB = p.I->getParent(); bool first = p.I == BB->begin(); auto insert = p.I; if ( !first ) --insert; IL->LowerIntrinsicCall( cast< ::llvm::CallInst >( p.I ) ); if ( first ) insert = BB->begin(); else insert ++; return Position( p.pc, insert ); } Builtin ProgramInfo::builtin( ::llvm::Function *f ) { std::string name = f->getName().str(); if ( name == "__divine_interrupt_mask" ) return BuiltinMask; if ( name == "__divine_interrupt_unmask" ) return BuiltinUnmask; if ( name == "__divine_interrupt" ) return BuiltinInterrupt; if ( name == "__divine_get_tid" ) return BuiltinGetTID; if ( name == "__divine_new_thread" ) return BuiltinNewThread; if ( name == "__divine_choice" ) return BuiltinChoice; if ( name == "__divine_assert" ) return BuiltinAssert; if ( name == "__divine_problem" ) return BuiltinProblem; if ( name == "__divine_ap" ) return BuiltinAp; if ( name == "__divine_malloc" ) return BuiltinMalloc; if ( name == "__divine_heap_object_size" ) return BuiltinHeapObjectSize; if ( name == "__divine_free" ) return BuiltinFree; if ( name == "__divine_va_start" ) return BuiltinVaStart; if ( name == "__divine_unwind" ) return BuiltinUnwind; if ( name == "__divine_landingpad" ) return BuiltinLandingPad; if ( name == "__divine_memcpy" ) return BuiltinMemcpy; if ( f->getIntrinsicID() != ::llvm::Intrinsic::not_intrinsic ) return BuiltinIntrinsic; /* not our builtin */ return NotBuiltin; } void ProgramInfo::builtin( Position p ) { ProgramInfo::Instruction &insn = instruction( p.pc ); ::llvm::CallSite CS( p.I ); ::llvm::Function *F = CS.getCalledFunction(); insn.builtin = builtin( F ); if ( insn.builtin == NotBuiltin ) throw std::logic_error( std::string( "ProgramInfo::builtin: " ) + "Can't call an undefined function: " + F->getName().str() ); } template< typename Insn > void ProgramInfo::insertIndices( Position p ) { Insn *I = cast< Insn >( p.I ); ProgramInfo::Instruction &insn = instruction( p.pc ); int shift = insn.values.size(); insn.values.resize( shift + I->getNumIndices() ); for ( unsigned i = 0; i < I->getNumIndices(); ++i ) { Value v; v.width = sizeof( unsigned ); makeConstant( v, I->getIndices()[ i ] ); insn.values[ shift + i ] = v; } } ProgramInfo::Position ProgramInfo::insert( Position p ) { makeFit( function( p.pc ).instructions, p.pc.instruction ); ProgramInfo::Instruction &insn = instruction( p.pc ); insn.opcode = p.I->getOpcode(); insn.op = &*p.I; if ( dyn_cast< ::llvm::CallInst >( p.I ) || dyn_cast< ::llvm::InvokeInst >( p.I ) ) { ::llvm::CallSite CS( p.I ); ::llvm::Function *F = CS.getCalledFunction(); if ( F ) { // you can actually invoke a label switch ( F->getIntrinsicID() ) { case ::llvm::Intrinsic::not_intrinsic: if ( builtin( F ) ) { builtin( p ); break; } case ::llvm::Intrinsic::eh_typeid_for: case ::llvm::Intrinsic::trap: case ::llvm::Intrinsic::vastart: case ::llvm::Intrinsic::vacopy: case ::llvm::Intrinsic::vaend: case ::llvm::Intrinsic::stacksave: case ::llvm::Intrinsic::stackrestore: case ::llvm::Intrinsic::umul_with_overflow: case ::llvm::Intrinsic::smul_with_overflow: case ::llvm::Intrinsic::uadd_with_overflow: case ::llvm::Intrinsic::sadd_with_overflow: case ::llvm::Intrinsic::usub_with_overflow: case ::llvm::Intrinsic::ssub_with_overflow: break; case ::llvm::Intrinsic::dbg_declare: case ::llvm::Intrinsic::dbg_value: p.I++; return p; default: return lower( p ); } } } if ( !codepointers ) { insn.values.resize( 1 + p.I->getNumOperands() ); for ( int i = 0; i < int( p.I->getNumOperands() ); ++i ) insn.values[ i + 1 ] = insert( p.pc.function, p.I->getOperand( i ) ); if ( isa< ::llvm::ExtractValueInst >( p.I ) ) insertIndices< ::llvm::ExtractValueInst >( p ); if ( isa< ::llvm::InsertValueInst >( p.I ) ) insertIndices< ::llvm::InsertValueInst >( p ); if ( auto LPI = dyn_cast< ::llvm::LandingPadInst >( p.I ) ) { for ( int i = 0; i < int( LPI->getNumClauses() ); ++i ) { if ( LPI->isFilter( i ) ) continue; Pointer ptr = constant< Pointer >( insn.operand( i + 1 ) ); if ( !function( p.pc ).typeID( ptr ) ) function( p.pc ).typeIDs.push_back( ptr ); ASSERT( function( p.pc ).typeID( ptr ) ); } } pcmap.insert( std::make_pair( p.I, p.pc ) ); insn.result() = insert( p.pc.function, &*p.I ); } ++ p.I; /* next please */ if ( ! ++ p.pc.instruction ) throw std::logic_error( "ProgramInfo::insert() in " + p.I->getParent()->getParent()->getName().str() + "\nToo many instructions in a basic block, capacity exceeded" ); return p; } void ProgramInfo::build() { /* null pointers are heap = 0, segment = 0, where segment is an index into * the "globals" vector */ Value nullpage; nullpage.offset = 0; nullpage.width = 0; nullpage.global = true; nullpage.constant = false; globals.push_back( nullpage ); for ( auto &f : *module ) if ( f.getName() == "memset" || f.getName() == "memmove" || f.getName() == "memcpy" ) f.setLinkage( ::llvm::GlobalValue::ExternalLinkage ); framealign = 1; codepointers = true; pass(); codepointers = false; for ( auto var = module->global_begin(); var != module->global_end(); ++ var ) insert( 0, &*var ); framealign = 4; pass(); framealign = 1; pass(); while ( !toInit.empty() ) { auto x = toInit.front(); toInit.pop_front(); storeConstant( std::get< 0 >( x ), std::get< 1 >( x ), std::get< 2 >( x ) ); } } void ProgramInfo::pass() { PC pc( 1, 0 ); int _framealign = framealign; for ( auto function = module->begin(); function != module->end(); ++ function ) { if ( function->isDeclaration() ) continue; /* skip */ auto name = function->getName(); if ( !++ pc.function ) throw std::logic_error( "ProgramInfo::build in " + name.str() + "\nToo many functions, capacity exceeded" ); if ( function->begin() == function->end() ) throw std::logic_error( "ProgramInfo::build in " + name.str() + "\nCan't deal with empty functions" ); makeFit( functions, pc.function ); functionmap[ function ] = pc.function; pc.instruction = 0; if ( !codepointers ) { framealign = 1; /* force all args to go in in the first pass */ auto &pi_function = this->functions[ pc.function ]; pi_function.argcount = 0; for ( auto arg = function->arg_begin(); arg != function->arg_end(); ++ arg ) { insert( pc.function, &*arg ); ++ pi_function.argcount; } if ( ( pi_function.vararg = function->isVarArg() ) ) { Value vaptr; vaptr.width = TD.getPointerSize(); vaptr.type = Value::Pointer; allocateValue( pc.function, vaptr ); pi_function.values.push_back( vaptr ); } framealign = _framealign; this->function( pc ).datasize = align( this->function( pc ).datasize, framealign ); } for ( auto block = function->begin(); block != function->end(); ++ block ) { blockmap[ &*block ] = pc; if ( block->begin() == block->end() ) throw std::logic_error( std::string( "ProgramInfo::build: " ) + "Can't deal with an empty BasicBlock in function " + name.str() ); ProgramInfo::Position p( pc, block->begin() ); while ( p.I != block->end() ) p = insert( p ); pc = p.pc; makeFit( this->function( p.pc ).instructions, p.pc.instruction ); } } globalsize = align( globalsize, 4 ); }