Instructions Bytecode::parser(const Tokens& tokens) const { Tokens tokens_group; // Abstract syntax tree. Instructions ast; int funcs = 0, params = 0, specs = 0; for (int i = 0; i < tokens.size(); i++) { if (tokens[i].type == DELIMITER) { checkFunctions(tokens_group[0], funcs); checkById(tokens_group[0].function_id, params, specs); Instruction inst = makeInstruction(params, specs, tokens_group); ast.push_back(inst); funcs = 0, params = 0, specs = 0; tokens_group.clear(); } else { if (tokens[i].type == FUNCTION) { funcs++; } else if (tokens[i].type == PARAMETER) { params++; } else { specs++; } tokens_group.push_back(tokens[i]); } } return ast; }
void constructBlocks(Blocks &blocks, Instructions &instructions) { /* Create the first block containing the very first instruction in this script. * Then follow the complete code flow from this instruction onwards. */ assert(blocks.empty()); if (instructions.empty()) return; blocks.push_back(Block(instructions.front().address)); constructBlocks(blocks, blocks.back(), instructions.front()); }
void ConstantInsertExtractElementIndex::fixOutOfRangeConstantIndices( BasicBlock &BB, const Instructions &Instrs) const { for (Instructions::const_iterator IB = Instrs.begin(), IE = Instrs.end(); IB != IE; ++IB) { Instruction *I = *IB; const APInt &Idx = cast<ConstantInt>(getInsertExtractElementIdx(I))->getValue(); APInt NumElements = APInt(Idx.getBitWidth(), vectorNumElements(I)); APInt NewIdx = Idx.urem(NumElements); setInsertExtractElementIdx(I, ConstantInt::get(M->getContext(), NewIdx)); } }
void ConstantInsertExtractElementIndex::findNonConstantInsertExtractElements( const BasicBlock &BB, Instructions &OutOfRangeConstantIndices, Instructions &NonConstantVectorIndices) const { for (BasicBlock::const_iterator BBI = BB.begin(), BBE = BB.end(); BBI != BBE; ++BBI) { const Instruction *I = &*BBI; if (Value *Idx = getInsertExtractElementIdx(I)) { if (ConstantInt *CI = dyn_cast<ConstantInt>(Idx)) { if (!CI->getValue().ult(vectorNumElements(I))) OutOfRangeConstantIndices.push_back(const_cast<Instruction *>(I)); } else NonConstantVectorIndices.push_back(const_cast<Instruction *>(I)); } } }
bool ConstantInsertExtractElementIndex::runOnBasicBlock(BasicBlock &BB) { bool Changed = false; if (!DL) DL = &getAnalysis<DataLayoutPass>().getDataLayout(); Instructions OutOfRangeConstantIndices; Instructions NonConstantVectorIndices; findNonConstantInsertExtractElements(BB, OutOfRangeConstantIndices, NonConstantVectorIndices); if (!OutOfRangeConstantIndices.empty()) { Changed = true; fixOutOfRangeConstantIndices(BB, OutOfRangeConstantIndices); } if (!NonConstantVectorIndices.empty()) { Changed = true; fixNonConstantVectorIndices(BB, NonConstantVectorIndices); } return Changed; }
void Bytecode::compile(const Instructions& instructions) { for (int i = 0; i < instructions.size(); i++) { int func_id = instructions[i].function.function_id; int p1 = instructions[i].p1.parameter; int p2 = instructions[i].p2.parameter; bool spec = instructions[i].spec.spec; PackedInstruction packed_inst(func_id, p1, p2, spec); bytecode_.push_back(packed_inst); } }
ClobFile::ClobFile(std::string _path, Instructions _instructions, strings_t _data) { path = _path; instructions = _instructions; header = {0, _instructions.size(), _data.size()}; for (auto _d : _data) { DataEntry_t entry; entry.string = _d; entry.length = entry.string.size(); data.push_back(entry); } }
void ConstantInsertExtractElementIndex::fixNonConstantVectorIndices( BasicBlock &BB, const Instructions &Instrs) const { for (Instructions::const_iterator IB = Instrs.begin(), IE = Instrs.end(); IB != IE; ++IB) { Instruction *I = *IB; Value *Vec = I->getOperand(0); Value *Idx = getInsertExtractElementIdx(I); VectorType *VecTy = cast<VectorType>(Vec->getType()); Type *ElemTy = VecTy->getElementType(); unsigned ElemAlign = DL->getPrefTypeAlignment(ElemTy); unsigned VecAlign = std::max(ElemAlign, DL->getPrefTypeAlignment(VecTy)); IRBuilder<> IRB(I); AllocaInst *Alloca = IRB.CreateAlloca( ElemTy, ConstantInt::get(Type::getInt32Ty(M->getContext()), vectorNumElements(I))); Alloca->setAlignment(VecAlign); Value *AllocaAsVec = IRB.CreateBitCast(Alloca, VecTy->getPointerTo()); IRB.CreateAlignedStore(Vec, AllocaAsVec, Alloca->getAlignment()); Value *GEP = IRB.CreateGEP(Alloca, Idx); Value *Res; switch (I->getOpcode()) { default: llvm_unreachable("expected InsertElement or ExtractElement"); case Instruction::InsertElement: IRB.CreateAlignedStore(I->getOperand(1), GEP, ElemAlign); Res = IRB.CreateAlignedLoad(AllocaAsVec, Alloca->getAlignment()); break; case Instruction::ExtractElement: Res = IRB.CreateAlignedLoad(GEP, ElemAlign); break; } I->replaceAllUsesWith(Res); I->eraseFromParent(); } }
void AArch64AddressTypePromotion::mergeSExts(ValueToInsts &ValToSExtendedUses, SetOfInstructions &ToRemove) { DominatorTree &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree(); for (auto &Entry : ValToSExtendedUses) { Instructions &Insts = Entry.second; Instructions CurPts; for (Instruction *Inst : Insts) { if (ToRemove.count(Inst)) continue; bool inserted = false; for (auto &Pt : CurPts) { if (DT.dominates(Inst, Pt)) { DEBUG(dbgs() << "Replace all uses of:\n" << *Pt << "\nwith:\n" << *Inst << '\n'); Pt->replaceAllUsesWith(Inst); ToRemove.insert(Pt); Pt = Inst; inserted = true; break; } if (!DT.dominates(Pt, Inst)) // Give up if we need to merge in a common dominator as the // expermients show it is not profitable. continue; DEBUG(dbgs() << "Replace all uses of:\n" << *Inst << "\nwith:\n" << *Pt << '\n'); Inst->replaceAllUsesWith(Pt); ToRemove.insert(Inst); inserted = true; break; } if (!inserted) CurPts.push_back(Inst); } } }
/** @brief opens instructions window after pushing button */ void MainWindow::help() { //open new window Instructions* instruct = new Instructions(this); instruct->show(); }
void AArch64AddressTypePromotion::analyzeSExtension(Instructions &SExtInsts) { DEBUG(dbgs() << "*** Analyze Sign Extensions ***\n"); DenseMap<Value *, Instruction *> SeenChains; for (auto &BB : *Func) { for (auto &II : BB) { Instruction *SExt = &II; // Collect all sext operation per type. if (!isa<SExtInst>(SExt) || !shouldConsiderSExt(SExt)) continue; DEBUG(dbgs() << "Found:\n" << (*SExt) << '\n'); // Cases where we actually perform the optimization: // 1. SExt is used in a getelementptr with more than 2 operand => // likely we can merge some computation if they are done on 64 bits. // 2. The beginning of the SExt chain is SExt several time. => // code sharing is possible. bool insert = false; // #1. for (const User *U : SExt->users()) { const Instruction *Inst = dyn_cast<GetElementPtrInst>(U); if (Inst && Inst->getNumOperands() > 2) { DEBUG(dbgs() << "Interesting use in GetElementPtrInst\n" << *Inst << '\n'); insert = true; break; } } // #2. // Check the head of the chain. Instruction *Inst = SExt; Value *Last; do { int OpdIdx = 0; const BinaryOperator *BinOp = dyn_cast<BinaryOperator>(Inst); if (BinOp && isa<ConstantInt>(BinOp->getOperand(0))) OpdIdx = 1; Last = Inst->getOperand(OpdIdx); Inst = dyn_cast<Instruction>(Last); } while (Inst && canGetThrough(Inst) && shouldGetThrough(Inst)); DEBUG(dbgs() << "Head of the chain:\n" << *Last << '\n'); DenseMap<Value *, Instruction *>::iterator AlreadySeen = SeenChains.find(Last); if (insert || AlreadySeen != SeenChains.end()) { DEBUG(dbgs() << "Insert\n"); SExtInsts.push_back(SExt); if (AlreadySeen != SeenChains.end() && AlreadySeen->second != nullptr) { DEBUG(dbgs() << "Insert chain member\n"); SExtInsts.push_back(AlreadySeen->second); SeenChains[Last] = nullptr; } } else { DEBUG(dbgs() << "Record its chain membership\n"); SeenChains[Last] = SExt; } } } }
// Input: // - SExtInsts contains all the sext instructions that are used directly in // GetElementPtrInst, i.e., access to memory. // Algorithm: // - For each sext operation in SExtInsts: // Let var be the operand of sext. // while it is profitable (see shouldGetThrough), legal, and safe // (see canGetThrough) to move sext through var's definition: // * promote the type of var's definition. // * fold var into sext uses. // * move sext above var's definition. // * update sext operand to use the operand of var that should be sign // extended (by construction there is only one). // // E.g., // a = ... i32 c, 3 // b = sext i32 a to i64 <- is it legal/safe/profitable to get through 'a' // ... // = b // => Yes, update the code // b = sext i32 c to i64 // a = ... i64 b, 3 // ... // = a // Iterate on 'c'. bool AArch64AddressTypePromotion::propagateSignExtension(Instructions &SExtInsts) { DEBUG(dbgs() << "*** Propagate Sign Extension ***\n"); bool LocalChange = false; SetOfInstructions ToRemove; ValueToInsts ValToSExtendedUses; while (!SExtInsts.empty()) { // Get through simple chain. Instruction *SExt = SExtInsts.pop_back_val(); DEBUG(dbgs() << "Consider:\n" << *SExt << '\n'); // If this SExt has already been merged continue. if (SExt->use_empty() && ToRemove.count(SExt)) { DEBUG(dbgs() << "No uses => marked as delete\n"); continue; } // Now try to get through the chain of definitions. while (auto *Inst = dyn_cast<Instruction>(SExt->getOperand(0))) { DEBUG(dbgs() << "Try to get through:\n" << *Inst << '\n'); if (!canGetThrough(Inst) || !shouldGetThrough(Inst)) { // We cannot get through something that is not an Instruction // or not safe to SExt. DEBUG(dbgs() << "Cannot get through\n"); break; } LocalChange = true; // If this is a sign extend, it becomes useless. if (isa<SExtInst>(Inst) || isa<TruncInst>(Inst)) { DEBUG(dbgs() << "SExt or trunc, mark it as to remove\n"); // We cannot use replaceAllUsesWith here because we may trigger some // assertion on the type as all involved sext operation may have not // been moved yet. while (!Inst->use_empty()) { Use &U = *Inst->use_begin(); Instruction *User = dyn_cast<Instruction>(U.getUser()); assert(User && "User of sext is not an Instruction!"); User->setOperand(U.getOperandNo(), SExt); } ToRemove.insert(Inst); SExt->setOperand(0, Inst->getOperand(0)); SExt->moveBefore(Inst); continue; } // Get through the Instruction: // 1. Update its type. // 2. Replace the uses of SExt by Inst. // 3. Sign extend each operand that needs to be sign extended. // Step #1. Inst->mutateType(SExt->getType()); // Step #2. SExt->replaceAllUsesWith(Inst); // Step #3. Instruction *SExtForOpnd = SExt; DEBUG(dbgs() << "Propagate SExt to operands\n"); for (int OpIdx = 0, EndOpIdx = Inst->getNumOperands(); OpIdx != EndOpIdx; ++OpIdx) { DEBUG(dbgs() << "Operand:\n" << *(Inst->getOperand(OpIdx)) << '\n'); if (Inst->getOperand(OpIdx)->getType() == SExt->getType() || !shouldSExtOperand(Inst, OpIdx)) { DEBUG(dbgs() << "No need to propagate\n"); continue; } // Check if we can statically sign extend the operand. Value *Opnd = Inst->getOperand(OpIdx); if (const ConstantInt *Cst = dyn_cast<ConstantInt>(Opnd)) { DEBUG(dbgs() << "Statically sign extend\n"); Inst->setOperand(OpIdx, ConstantInt::getSigned(SExt->getType(), Cst->getSExtValue())); continue; } // UndefValue are typed, so we have to statically sign extend them. if (isa<UndefValue>(Opnd)) { DEBUG(dbgs() << "Statically sign extend\n"); Inst->setOperand(OpIdx, UndefValue::get(SExt->getType())); continue; } // Otherwise we have to explicity sign extend it. assert(SExtForOpnd && "Only one operand should have been sign extended"); SExtForOpnd->setOperand(0, Opnd); DEBUG(dbgs() << "Move before:\n" << *Inst << "\nSign extend\n"); // Move the sign extension before the insertion point. SExtForOpnd->moveBefore(Inst); Inst->setOperand(OpIdx, SExtForOpnd); // If more sext are required, new instructions will have to be created. SExtForOpnd = nullptr; } if (SExtForOpnd == SExt) { DEBUG(dbgs() << "Sign extension is useless now\n"); ToRemove.insert(SExt); break; } } // If the use is already of the right type, connect its uses to its argument // and delete it. // This can happen for an Instruction all uses of which are sign extended. if (!ToRemove.count(SExt) && SExt->getType() == SExt->getOperand(0)->getType()) { DEBUG(dbgs() << "Sign extension is useless, attach its use to " "its argument\n"); SExt->replaceAllUsesWith(SExt->getOperand(0)); ToRemove.insert(SExt); } else ValToSExtendedUses[SExt->getOperand(0)].push_back(SExt); } if (EnableMerge) mergeSExts(ValToSExtendedUses, ToRemove); // Remove all instructions marked as ToRemove. for (Instruction *I: ToRemove) I->eraseFromParent(); return LocalChange; }
// Pre-Processamento: // Remove os comentarios e linhas em branco // E coloca o codigo numa estrutura do tipo CodeLines // Tambem verifica os labels e equs e ifs void readAndPreProcess (const char* fileName) { ifstream infile(fileName); string line; int textMemAddr = textStartAddress; int dataMemAddr = 0; int BSSMemAddr = 0; stringstream tempSS; CodeSection codeSection = NONE; // Le linha a linha for (int lineCount = 1; getline(infile, line); ++lineCount) { // Troca virgulas por espaco strReplace(line, ",", " "); // Ignora linhas em branco if (line.empty()) continue; // Pega palavra a palavra de acordo com os espacos istringstream iss(line); string tempStr; while (iss >> tempStr) { if ("SECTION" == tempStr) { string tempStr2; iss >> tempStr2; if ("TEXT" == tempStr2) codeSection = TEXT; else if ("DATA" == tempStr2) codeSection = DATA; codeLines[lineCount].push_back(tempStr); codeLines[lineCount].push_back(tempStr2); continue; } // Ignora comentarios if (";" == tempStr.substr(0,1)) break; // Desconsidera o caso (maiusculas/minusculas) transform(tempStr.begin(), tempStr.end(), tempStr.begin(), ::toupper); // Ve se eh um label / define if (":" == tempStr.substr(tempStr.length() - 1, 1)) { // Ve se ainda restam tokens na linha if (iss.rdbuf()->in_avail() != 0) { // Remove o ':' tempStr = tempStr.substr(0, tempStr.length() - 1); string tempStr2; iss >> tempStr2; // Ve se o proximo token eh EQU if ("EQU" == tempStr2) { string tempStr3; iss >> tempStr3; // Se define já existe if (defines.find(tempStr3) != defines.end()){ tempSS << lineCount; errors.push("ERRO NA LINHA " + tempSS.str() + ": EQU ja declarado."); tempSS.str(""); } else { // Coloca o valor do EQU na tabela de defines defines[tempStr] = tempStr3; } // Se nao eh so um label // Com algo a mais na mesma linha } else { if ( (labels.find(tempStr) != labels.end()) || (dataLabels.find(tempStr) != dataLabels.end()) ){ tempSS << lineCount; errors.push("ERRO NA LINHA " + tempSS.str() + ": Label ja declarado."); tempSS.str(""); } else { // Adiciona na tabela de labels if(codeSection == TEXT){ labels[tempStr] = textMemAddr; } else if (codeSection == DATA) { dataLabels[tempStr] = dataMemAddr; dataMemAddr += 4; } } // Adiciona endereco de memoria if (instructions.find(tempStr2) != instructions.end()) textMemAddr += get<3>(instructions[tempStr2]); // Adiciona os tokens ao vetor codeLines[lineCount].push_back(tempStr+":"); codeLines[lineCount].push_back(tempStr2); } // Se nao eh um label "sozinho" // Adiciona no vetor } else {
// Syntax of instruction // // mnemonic {op} '|' ( prefix )* opcode args // // mnemonic = Intel instruction lexium // // op = sr %es, ..., %gs // xb %al, .., %bh // xd %ax, .., %dil // xw %eax, ..., %edi // xq %rax, ..., %rdi // rb %rNb // rd %rNd // rw %rNw // rq %rN // st %stN // mm %mmN // xmm %xmmN // ymm %ymmN // zmm %zmmN // cr %crN // dr %drN // k %kN // addr.disp.rq, 10(%r10) // addr.disp.xq, 20(%rdx) // addr.disp.rq.rq, 30(%r10,%r11,2) // addr.disp.rq.xq, 40(%r10,%rdx,2) // addr.disp.xq.rq, 50(%rdx,%r11,2) // addr.disp.xq.xq, 60(%rdx,%rax,2) // addr.disp.rd, 70(%r10d) // addr.disp.xd, 80(%dx) // addr.disp.rd.rd, 90(%r10d,%r11d,2) // addr.disp.rd.xd, 10(%r10d,%dx,2) // addr.disp.xd.rd, 11(%dx,%r11d,2) // addr.disp.xd.xd, 12(%dx,%ax,2) // addr.disp.rw, 13(%r10w) // addr.disp.xw, 14(%edx) // addr.disp.rw.rw, 15(%r10w,%r11w,1) // addr.disp.rw.xw, 16(%r10w,%edx,2) // addr.disp.xw.rw, 17(%edx,%r11w,4) // addr.disp.xw.xw, 18(%edx,%eax,8) // addr.rq, (%r10) // addr.xq, (%rdx) // addr.rq.rq, (%r10,%r11,2) // addr.rq.xq, (%r10,%rdx,2) // addr.xq.rq, (%rdx,%r11,2) // addr.xq.xq, (%rdx,%rax,2) // addr.rd, (%r10d) // addr.xd, (%dx) // addr.rd.rd, (%r10d,%r11d,2) // addr.rd.xd, (%r10d,%dx,2) // addr.xd.rd, (%dx,%r11d,2) // addr.xd.xd, (%dx,%ax,2) // addr.rw, (%r10w) // addr.xw, (%edx) // addr.rw.rw, (%r10w,%r11w,1) // addr.rw.xw, (%r10w,%edx,2) // addr.xw.rw, (%edx,%r11w,4) // addr.xw.xw, (%edx,%eax,8) // // imm8 Immediate value // imm32 Immediate value // disp Memory addressing displayment as the exponent of power of 2 // // prefix = rex:BRWX // vex2:R:vvvv:L:pp // vex3:RXB:mmmm:W:vvvv:L:pp // evex:RXB:R':mm:W:vvvv:pp:z:L':L:b:V':aaa // es // cs // ss // ds // os // as // rep // ne // lock // bhnt // bnht // // opcode = one or more bytes // args = reg:<len>:<n> | imm:<len>:<n> | scale:<len>:<n> // bool loadDef( FILE * fh, Instructions & insts ) { const int BUF_SIZE = 256; char buff[BUF_SIZE]; enum Pstate { start, nmemonicSeen, operandSeen, sepSeen, opcodeSeen, argsSeen, }; static const char * opTypes[] = { "addr.disp32.rd", "addr.disp32.rd.rd", "addr.disp32.rd.rd.scale", "addr.disp32.rd.xd", "addr.disp32.rd.xd.scale", "addr.disp32.rq", "addr.disp32.rq.rq", "addr.disp32.rq.rq.scale", "addr.disp32.rq.xq", "addr.disp32.rq.xq.scale", "addr.disp32.xd", "addr.disp32.xd.rd", "addr.disp32.xd.rd.scale", "addr.disp32.xd.xd", "addr.disp32.xd.xd.scale", "addr.disp32.xq", "addr.disp32.xq.rq", "addr.disp32.xq.rq.scale", "addr.disp32.xq.xq", "addr.disp32.xq.xq.scale", "addr.disp8.rd", "addr.disp8.rd.rd", "addr.disp8.rd.rd.scale", "addr.disp8.rd.xd", "addr.disp8.rd.xd.scale", "addr.disp8.rq", "addr.disp8.rq.rq", "addr.disp8.rq.rq.scale", "addr.disp8.rq.xq", "addr.disp8.rq.xq.scale", "addr.disp8.xd", "addr.disp8.xd.rd", "addr.disp8.xd.rd.scale", "addr.disp8.xd.xd", "addr.disp8.xd.xd.scale", "addr.disp8.xq", "addr.disp8.xq.rq", "addr.disp8.xq.rq.scale", "addr.disp8.xq.xq", "addr.disp8.xq.xq.scale", "addr.rd", "addr.rd.rd", "addr.rd.rd.scale", "addr.rd.xd", "addr.rd.xd.scale", "addr.rq", "addr.rq.rq", "addr.rq.rq.scale", "addr.rq.xq", "addr.rq.xq.scale", "addr.xd", "addr.xd.rd", "addr.xd.rd.scale", "addr.xd.xd", "addr.xd.xd.scale", "addr.xq", "addr.xq.rq", "addr.xq.rq.scale", "addr.xq.xq", "addr.xq.xq.scale", "cr", "dr", "imm32", "imm8", "k", "mm", "rb", "rd", "rq", "rw", "sr", "st", "xb", "xb64", "xd", "xmm", "xq", "xw", "ymm", "zmm", }; while( fgets( buff, BUF_SIZE, fh ) ) { vector<string> tokens = split(buff); if( tokens.size() > 0 ) { if( tokens.size() > 1 ) { try { Pstate state = start; Nmemonic nm; vector<Otype> operands; vector<unique_ptr<Prefix>> prefixes; vector<uint8_t> opcode; set<MC_Comp,Instruction::Comp>args; int offset = 0; for( auto & token:tokens) { switch(state) { case start: nm = Instruction::nmemonic(token); state = nmemonicSeen; break; case nmemonicSeen: { static auto end = opTypes + sizeof(opTypes)/sizeof(char *); auto p = equal_range( opTypes, end, token.c_str(), []( const char * a, const char * b){ return strcmp(a,b) < 0; } ); if(p.first < end && token == opTypes[p.first-opTypes]) operands.push_back(static_cast<Otype>(p.first-opTypes)); else if( token == "|" ) state = sepSeen; else throw "Invalid instruction: Expected operand or separator"; break; } case sepSeen: { unique_ptr<Prefix> prefix; if( createPrefix( prefix, token, offset )) prefixes.push_back(move(prefix)); else { offset += 8; opcode.push_back(strtol( token.c_str(), nullptr, 16 )); state = opcodeSeen; } break; } case opcodeSeen: if(isArgOrConst(token)) { args.insert(MC_Comp(token, offset )); state = argsSeen; } else if(isOpcodeByte(token)) { opcode.push_back(strtol( token.c_str(), nullptr, 16 )); offset += 8; } else throw "Invalid instruction: Expected opcode or separator "; break; case argsSeen: if(isArgOrConst(token)) { args.insert(MC_Comp(token, offset )); } else throw "Invalid instruction"; break; } } if( opcode.size() == 0 ) fprintf( stderr, "%s missing opcode. Ignored\n", Instruction::lexium(nm) ); else insts.insert( Instruction(nm,move(operands),move(prefixes),move(opcode), move(args))); } catch( ... ) { fprintf( stderr, "Failed to parse instruction on %s\n", buff ); return false; } } } } return true; }
/** * Compiles a brainfreeze program. It converts the program's * textual representation into a program containing only the * brainfreeze vm instructions. * * Additionally, it performs several compile time optimizations in order to * speed up execution time. These optimizations include pre-calculating * jump offsets and fusing sequential identical instructions. * * \return Returns true if the program was succesfully compiled, otherwise * it will return false to indicate the presence of errors */ bool BFProgram::compile() { Instructions temp; Instruction last = Instruction( OP_EOF, 0 ); std::stack<int> jumps; // record positions for [ // // Scan the code string. Replace each BF instruction // with its symbolic equivilant. // int icount = 0; for( std::string::iterator itr = m_codestr.begin(); itr != m_codestr.end(); ++itr ) { if( BF::isInstruction( *itr ) ) { // Convert the character into an instruction Instruction instr = BF::convert( *itr ); // // Is this a jump instruction? // if ( instr.opcode() == OP_JMP_FWD ) { // This is a forward jump. Record its position jumps.push(icount); } else if ( instr.opcode() == OP_JMP_BAC ) { // This is a backward jump. Pop the corresponding // forward jump marker off the stack, and update both // of the instructions with the location of their // corresponding targets. int backpos = jumps.top(); jumps.pop(); int dist = icount - backpos; assert( dist > 0 ); // Update the [ character temp[backpos].setParam( dist ); // Update the ] (current) character instr.setParam( dist ); } // // Was the last character a repeat of +, -, >, < // if ( ( instr.opcode() == last.opcode() ) && ( instr.opcode() == OP_PTR_INC || instr.opcode() == OP_PTR_DEC || instr.opcode() == OP_MEM_INC || instr.opcode() == OP_MEM_DEC ) ) { // Get the last character, and update its occurrence temp[icount-1].setParam( temp[icount-1].param() + 1 ); } else { // Add the instruction to the instruction stream temp.push_back( instr ); // Remember last instruction last = instr; // Increment instruction counter icount += 1; } // Remember the last instruction last = instr; } } // Verify the jump stack is empty. If not, then there is a // mismatched jump somewhere! if (! jumps.empty() ) { raiseError( "Mismatched jump detected when compiling" ); return false; } // Insert end of program instruction temp.push_back( Instruction( OP_EOF, 0 ) ); // Save it to m_instructions m_instructions.swap( temp ); m_ip = m_instructions.begin(); return true; }
void LowerEmAsyncify::transformAsyncFunction(Function &F, Instructions const& AsyncCalls) { assert(!AsyncCalls.empty()); // Pass 0 // collect all the return instructions from the original function // will use later std::vector<ReturnInst*> OrigReturns; for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ++I) { if (ReturnInst *RI = dyn_cast<ReturnInst>(&*I)) { OrigReturns.push_back(RI); } } // Pass 1 // Scan each async call and make the basic structure: // All these will be cloned into the callback functions // - allocate the async context before calling an async function // - check async right after calling an async function, save context & return if async, continue if not // - retrieve the async return value and free the async context if the called function turns out to be sync std::vector<AsyncCallEntry> AsyncCallEntries; AsyncCallEntries.reserve(AsyncCalls.size()); for (Instructions::const_iterator I = AsyncCalls.begin(), E = AsyncCalls.end(); I != E; ++I) { // prepare blocks Instruction *CurAsyncCall = *I; // The block containing the async call BasicBlock *CurBlock = CurAsyncCall->getParent(); // The block should run after the async call BasicBlock *AfterCallBlock = SplitBlock(CurBlock, CurAsyncCall->getNextNode()); // The block where we store the context and return BasicBlock *SaveAsyncCtxBlock = BasicBlock::Create(TheModule->getContext(), "SaveAsyncCtx", &F, AfterCallBlock); // return a dummy value at the end, to make the block valid new UnreachableInst(TheModule->getContext(), SaveAsyncCtxBlock); // allocate the context before making the call // we don't know the size yet, will fix it later // we cannot insert the instruction later because, // we need to make sure that all the instructions and blocks are fixed before we can generate DT and find context variables // In CallHandler.h `sp` will be put as the second parameter // such that we can take a note of the original sp CallInst *AllocAsyncCtxInst = CallInst::Create(AllocAsyncCtxFunction, Constant::getNullValue(I32), "AsyncCtx", CurAsyncCall); // Right after the call // check async and return if so // TODO: we can define truly async functions and partial async functions { // remove old terminator, which came from SplitBlock CurBlock->getTerminator()->eraseFromParent(); // go to SaveAsyncCtxBlock if the previous call is async // otherwise just continue to AfterCallBlock CallInst *CheckAsync = CallInst::Create(CheckAsyncFunction, "IsAsync", CurBlock); BranchInst::Create(SaveAsyncCtxBlock, AfterCallBlock, CheckAsync, CurBlock); } // take a note of this async call AsyncCallEntry CurAsyncCallEntry; CurAsyncCallEntry.AsyncCallInst = CurAsyncCall; CurAsyncCallEntry.AfterCallBlock = AfterCallBlock; CurAsyncCallEntry.AllocAsyncCtxInst = AllocAsyncCtxInst; CurAsyncCallEntry.SaveAsyncCtxBlock = SaveAsyncCtxBlock; // create an empty function for the callback, which will be constructed later CurAsyncCallEntry.CallbackFunc = Function::Create(CallbackFunctionType, F.getLinkage(), F.getName() + "__async_cb", TheModule); AsyncCallEntries.push_back(CurAsyncCallEntry); } // Pass 2 // analyze the context variables and construct SaveAsyncCtxBlock for each async call // also calculate the size of the context and allocate the async context accordingly for (std::vector<AsyncCallEntry>::iterator EI = AsyncCallEntries.begin(), EE = AsyncCallEntries.end(); EI != EE; ++EI) { AsyncCallEntry & CurEntry = *EI; // Collect everything to be saved FindContextVariables(CurEntry); // Pack the variables as a struct { // TODO: sort them from large memeber to small ones, in order to make the struct compact even when aligned SmallVector<Type*, 8> Types; Types.push_back(CallbackFunctionType->getPointerTo()); for (Values::iterator VI = CurEntry.ContextVariables.begin(), VE = CurEntry.ContextVariables.end(); VI != VE; ++VI) { Types.push_back((*VI)->getType()); } CurEntry.ContextStructType = StructType::get(TheModule->getContext(), Types); } // fix the size of allocation CurEntry.AllocAsyncCtxInst->setOperand(0, ConstantInt::get(I32, DL->getTypeStoreSize(CurEntry.ContextStructType))); // construct SaveAsyncCtxBlock { // fill in SaveAsyncCtxBlock // temporarily remove the terminator for convenience CurEntry.SaveAsyncCtxBlock->getTerminator()->eraseFromParent(); assert(CurEntry.SaveAsyncCtxBlock->empty()); Type *AsyncCtxAddrTy = CurEntry.ContextStructType->getPointerTo(); BitCastInst *AsyncCtxAddr = new BitCastInst(CurEntry.AllocAsyncCtxInst, AsyncCtxAddrTy, "AsyncCtxAddr", CurEntry.SaveAsyncCtxBlock); SmallVector<Value*, 2> Indices; // store the callback { Indices.push_back(ConstantInt::get(I32, 0)); Indices.push_back(ConstantInt::get(I32, 0)); GetElementPtrInst *AsyncVarAddr = GetElementPtrInst::Create(AsyncCtxAddrTy, AsyncCtxAddr, Indices, "", CurEntry.SaveAsyncCtxBlock); new StoreInst(CurEntry.CallbackFunc, AsyncVarAddr, CurEntry.SaveAsyncCtxBlock); } // store the context variables for (size_t i = 0; i < CurEntry.ContextVariables.size(); ++i) { Indices.clear(); Indices.push_back(ConstantInt::get(I32, 0)); Indices.push_back(ConstantInt::get(I32, i + 1)); // the 0th element is the callback function GetElementPtrInst *AsyncVarAddr = GetElementPtrInst::Create(AsyncCtxAddrTy, AsyncCtxAddr, Indices, "", CurEntry.SaveAsyncCtxBlock); new StoreInst(CurEntry.ContextVariables[i], AsyncVarAddr, CurEntry.SaveAsyncCtxBlock); } // to exit the block, we want to return without unwinding the stack frame CallInst::Create(DoNotUnwindFunction, "", CurEntry.SaveAsyncCtxBlock); ReturnInst::Create(TheModule->getContext(), (F.getReturnType()->isVoidTy() ? 0 : Constant::getNullValue(F.getReturnType())), CurEntry.SaveAsyncCtxBlock); } } // Pass 3 // now all the SaveAsyncCtxBlock's have been constructed // we can clone F and construct callback functions // we could not construct the callbacks in Pass 2 because we need _all_ those SaveAsyncCtxBlock's appear in _each_ callback for (std::vector<AsyncCallEntry>::iterator EI = AsyncCallEntries.begin(), EE = AsyncCallEntries.end(); EI != EE; ++EI) { AsyncCallEntry & CurEntry = *EI; Function *CurCallbackFunc = CurEntry.CallbackFunc; ValueToValueMapTy VMap; // Add the entry block // load variables from the context // also update VMap for CloneFunction BasicBlock *EntryBlock = BasicBlock::Create(TheModule->getContext(), "AsyncCallbackEntry", CurCallbackFunc); std::vector<LoadInst *> LoadedAsyncVars; { Type *AsyncCtxAddrTy = CurEntry.ContextStructType->getPointerTo(); BitCastInst *AsyncCtxAddr = new BitCastInst(CurCallbackFunc->arg_begin(), AsyncCtxAddrTy, "AsyncCtx", EntryBlock); SmallVector<Value*, 2> Indices; for (size_t i = 0; i < CurEntry.ContextVariables.size(); ++i) { Indices.clear(); Indices.push_back(ConstantInt::get(I32, 0)); Indices.push_back(ConstantInt::get(I32, i + 1)); // the 0th element of AsyncCtx is the callback function GetElementPtrInst *AsyncVarAddr = GetElementPtrInst::Create(AsyncCtxAddrTy, AsyncCtxAddr, Indices, "", EntryBlock); LoadedAsyncVars.push_back(new LoadInst(AsyncVarAddr, "", EntryBlock)); // we want the argument to be replaced by the loaded value if (isa<Argument>(CurEntry.ContextVariables[i])) VMap[CurEntry.ContextVariables[i]] = LoadedAsyncVars.back(); } } // we don't need any argument, just leave dummy entries there to cheat CloneFunctionInto for (Function::const_arg_iterator AI = F.arg_begin(), AE = F.arg_end(); AI != AE; ++AI) { if (VMap.count(AI) == 0) VMap[AI] = Constant::getNullValue(AI->getType()); } // Clone the function { SmallVector<ReturnInst*, 8> Returns; CloneFunctionInto(CurCallbackFunc, &F, VMap, false, Returns); // return type of the callback functions is always void // need to fix the return type if (!F.getReturnType()->isVoidTy()) { // for those return instructions that are from the original function // it means we are 'truly' leaving this function // need to store the return value right before ruturn for (size_t i = 0; i < OrigReturns.size(); ++i) { ReturnInst *RI = cast<ReturnInst>(VMap[OrigReturns[i]]); // Need to store the return value into the global area CallInst *RawRetValAddr = CallInst::Create(GetAsyncReturnValueAddrFunction, "", RI); BitCastInst *RetValAddr = new BitCastInst(RawRetValAddr, F.getReturnType()->getPointerTo(), "AsyncRetValAddr", RI); new StoreInst(RI->getOperand(0), RetValAddr, RI); } // we want to unwind the stack back to where it was before the original function as called // but we don't actually need to do this here // at this point it must be true that no callback is pended // so the scheduler will correct the stack pointer and pop the frame // here we just fix the return type for (size_t i = 0; i < Returns.size(); ++i) { ReplaceInstWithInst(Returns[i], ReturnInst::Create(TheModule->getContext())); } } } // the callback function does not have any return value // so clear all the attributes for return { AttributeSet Attrs = CurCallbackFunc->getAttributes(); CurCallbackFunc->setAttributes( Attrs.removeAttributes(TheModule->getContext(), AttributeSet::ReturnIndex, Attrs.getRetAttributes()) ); } // in the callback function, we never allocate a new async frame // instead we reuse the existing one for (std::vector<AsyncCallEntry>::iterator EI = AsyncCallEntries.begin(), EE = AsyncCallEntries.end(); EI != EE; ++EI) { Instruction *I = cast<Instruction>(VMap[EI->AllocAsyncCtxInst]); ReplaceInstWithInst(I, CallInst::Create(ReallocAsyncCtxFunction, I->getOperand(0), "ReallocAsyncCtx")); } // mapped entry point & async call BasicBlock *ResumeBlock = cast<BasicBlock>(VMap[CurEntry.AfterCallBlock]); Instruction *MappedAsyncCall = cast<Instruction>(VMap[CurEntry.AsyncCallInst]); // To save space, for each async call in the callback function, we just ignore the sync case, and leave it to the scheduler // TODO need an option for this { for (std::vector<AsyncCallEntry>::iterator EI = AsyncCallEntries.begin(), EE = AsyncCallEntries.end(); EI != EE; ++EI) { AsyncCallEntry & CurEntry = *EI; Instruction *MappedAsyncCallInst = cast<Instruction>(VMap[CurEntry.AsyncCallInst]); BasicBlock *MappedAsyncCallBlock = MappedAsyncCallInst->getParent(); BasicBlock *MappedAfterCallBlock = cast<BasicBlock>(VMap[CurEntry.AfterCallBlock]); // for the sync case of the call, go to NewBlock (instead of MappedAfterCallBlock) BasicBlock *NewBlock = BasicBlock::Create(TheModule->getContext(), "", CurCallbackFunc, MappedAfterCallBlock); MappedAsyncCallBlock->getTerminator()->setSuccessor(1, NewBlock); // store the return value if (!MappedAsyncCallInst->use_empty()) { CallInst *RawRetValAddr = CallInst::Create(GetAsyncReturnValueAddrFunction, "", NewBlock); BitCastInst *RetValAddr = new BitCastInst(RawRetValAddr, MappedAsyncCallInst->getType()->getPointerTo(), "AsyncRetValAddr", NewBlock); new StoreInst(MappedAsyncCallInst, RetValAddr, NewBlock); } // tell the scheduler that we want to keep the current async stack frame CallInst::Create(DoNotUnwindAsyncFunction, "", NewBlock); // finally we go to the SaveAsyncCtxBlock, to register the callbac, save the local variables and leave BasicBlock *MappedSaveAsyncCtxBlock = cast<BasicBlock>(VMap[CurEntry.SaveAsyncCtxBlock]); BranchInst::Create(MappedSaveAsyncCtxBlock, NewBlock); } } std::vector<AllocaInst*> ToPromote; // applying loaded variables in the entry block { BasicBlockSet ReachableBlocks = FindReachableBlocksFrom(ResumeBlock); for (size_t i = 0; i < CurEntry.ContextVariables.size(); ++i) { Value *OrigVar = CurEntry.ContextVariables[i]; if (isa<Argument>(OrigVar)) continue; // already processed Value *CurVar = VMap[OrigVar]; assert(CurVar != MappedAsyncCall); if (Instruction *Inst = dyn_cast<Instruction>(CurVar)) { if (ReachableBlocks.count(Inst->getParent())) { // Inst could be either defined or loaded from the async context // Do the dirty works in memory // TODO: might need to check the safety first // TODO: can we create phi directly? AllocaInst *Addr = DemoteRegToStack(*Inst, false); new StoreInst(LoadedAsyncVars[i], Addr, EntryBlock); ToPromote.push_back(Addr); } else { // The parent block is not reachable, which means there is no confliction // it's safe to replace Inst with the loaded value assert(Inst != LoadedAsyncVars[i]); // this should only happen when OrigVar is an Argument Inst->replaceAllUsesWith(LoadedAsyncVars[i]); } } } } // resolve the return value of the previous async function // it could be the value just loaded from the global area // or directly returned by the function (in its sync case) if (!CurEntry.AsyncCallInst->use_empty()) { // load the async return value CallInst *RawRetValAddr = CallInst::Create(GetAsyncReturnValueAddrFunction, "", EntryBlock); BitCastInst *RetValAddr = new BitCastInst(RawRetValAddr, MappedAsyncCall->getType()->getPointerTo(), "AsyncRetValAddr", EntryBlock); LoadInst *RetVal = new LoadInst(RetValAddr, "AsyncRetVal", EntryBlock); AllocaInst *Addr = DemoteRegToStack(*MappedAsyncCall, false); new StoreInst(RetVal, Addr, EntryBlock); ToPromote.push_back(Addr); } // TODO remove unreachable blocks before creating phi // We go right to ResumeBlock from the EntryBlock BranchInst::Create(ResumeBlock, EntryBlock); /* * Creating phi's * Normal stack frames and async stack frames are interleaving with each other. * In a callback function, if we call an async function, we might need to realloc the async ctx. * at this point we don't want anything stored after the ctx, * such that we can free and extend the ctx by simply update STACKTOP. * Therefore we don't want any alloca's in callback functions. * */ if (!ToPromote.empty()) { DominatorTreeWrapperPass DTW; DTW.runOnFunction(*CurCallbackFunc); PromoteMemToReg(ToPromote, DTW.getDomTree()); } removeUnreachableBlocks(*CurCallbackFunc); } // Pass 4 // Here are modifications to the original function, which we won't want to be cloned into the callback functions for (std::vector<AsyncCallEntry>::iterator EI = AsyncCallEntries.begin(), EE = AsyncCallEntries.end(); EI != EE; ++EI) { AsyncCallEntry & CurEntry = *EI; // remove the frame if no async functinon has been called CallInst::Create(FreeAsyncCtxFunction, CurEntry.AllocAsyncCtxInst, "", CurEntry.AfterCallBlock->getFirstNonPHI()); } }
void linkInstructionBranches(Instructions &instructions) { /* Go through all instructions and link them according to the flow graph. * * In specifics, link each instruction's follower, the instruction that * naturally follows if no branches are taken. Also fill in the branches * array, which contains all branches an instruction can take. This * directly creates an address type for each instruction: does it start * a subroutine, is it a jump destination, is it a tail of a jump or none * of these? */ for (Instructions::iterator i = instructions.begin(); i != instructions.end(); ++i) { // If this is an instruction that has a natural follower, link it if ((i->opcode != kOpcodeJMP) && (i->opcode != kOpcodeRETN)) { Instructions::iterator follower = i + 1; i->follower = (follower != instructions.end()) ? &*follower : 0; if (follower != instructions.end()) follower->predecessors.push_back(&*i); } // Link destinations of unconditional branches if ((i->opcode == kOpcodeJMP) || (i->opcode == kOpcodeJSR) || (i->opcode == kOpcodeSTORESTATE)) { assert(((i->opcode == kOpcodeSTORESTATE) && (i->argCount == 3)) || (i->argCount == 1)); Instruction *branch = findInstruction(instructions, i->address + i->args[0]); if (!branch) throw Common::Exception("Can't find destination of unconditional branch"); i->branches.push_back(branch); if (i->opcode == kOpcodeJSR) setAddressType(branch, kAddressTypeSubRoutine); else if (i->opcode == kOpcodeSTORESTATE) setAddressType(branch, kAddressTypeStoreState); else { setAddressType(branch, kAddressTypeJumpLabel); branch->predecessors.push_back(&*i); } setAddressType(const_cast<Instruction *>(i->follower), kAddressTypeTail); } // Link destinations of conditional branches if ((i->opcode == kOpcodeJZ) || (i->opcode == kOpcodeJNZ)) { assert(i->argCount == 1); if (!i->follower) throw Common::Exception("Conditional branch has no false destination"); Instruction *branch = findInstruction(instructions, i->address + i->args[0]); if (!branch) throw Common::Exception("Can't find destination of conditional branch"); setAddressType(branch, kAddressTypeJumpLabel); setAddressType(const_cast<Instruction *>(i->follower), kAddressTypeTail); i->branches.push_back(branch); // True branch i->branches.push_back(i->follower); // False branch branch->predecessors.push_back(&*i); } } }