void HiRes5Engine::setupOpcodeTables() { AdlEngine_v4::setupOpcodeTables(); _actOpcodes[0x0b] = opcode(&HiRes5Engine::o_checkItemTimeLimits); _actOpcodes[0x13] = opcode(&HiRes5Engine::o_startAnimation); _actOpcodes[0x1e] = opcode(&HiRes5Engine::o_winGame); }
void DexInstruction::verify_encoding() const { auto test = m_count ? new DexInstruction(opcode()) : new DexInstruction(opcode(), 0); if (dests_size()) { test->set_dest(dest()); } for (unsigned i = 0; i < srcs_size(); i++) { test->set_src(i, src(i)); } if (has_range_base()) test->set_range_base(range_base()); if (has_range_size()) test->set_range_size(range_size()); if (has_arg_word_count()) test->set_arg_word_count(arg_word_count()); if (has_literal()) test->set_literal(literal()); if (has_offset()) test->set_offset(offset()); assert_log(m_opcode == test->m_opcode, "%x %x\n", m_opcode, test->m_opcode); for (unsigned i = 0; i < m_count; i++) { assert_log(m_arg[i] == test->m_arg[i], "(%x %x) (%x %x)", m_opcode, m_arg[i], test->m_opcode, test->m_arg[i]); } delete test; }
ValueKey Value::key() const { switch (opcode()) { case FramePointer: return ValueKey(opcode(), type()); case Identity: case Sqrt: case SExt8: case SExt16: case SExt32: case ZExt32: case Clz: case Trunc: case FRound: case IToD: case DToI32: case Check: return ValueKey(opcode(), type(), child(0)); case Add: case Sub: case Mul: case ChillDiv: case Mod: case BitAnd: case BitOr: case BitXor: case Shl: case SShr: case ZShr: case Equal: case NotEqual: case LessThan: case GreaterThan: case Above: case Below: case AboveEqual: case BelowEqual: case Div: case CheckAdd: case CheckSub: case CheckMul: return ValueKey(opcode(), type(), child(0), child(1)); case Select: return ValueKey(opcode(), type(), child(0), child(1), child(2)); case Const32: return ValueKey(Const32, type(), static_cast<int64_t>(asInt32())); case Const64: return ValueKey(Const64, type(), asInt64()); case ConstDouble: return ValueKey(ConstDouble, type(), asDouble()); case ArgumentReg: return ValueKey( ArgumentReg, type(), static_cast<int64_t>(as<ArgumentRegValue>()->argumentReg().index())); default: return ValueKey(); } }
Value* ValueKey::materialize(Procedure& proc, Origin origin) const { switch (opcode()) { case FramePointer: return proc.add<Value>(opcode(), type(), origin); case Identity: case Sqrt: case SExt8: case SExt16: case SExt32: case ZExt32: case Clz: case Trunc: case IToD: case IToF: case FloatToDouble: case DoubleToFloat: case Check: return proc.add<Value>(opcode(), type(), origin, child(proc, 0)); case Add: case Sub: case Mul: case ChillDiv: case Mod: case BitAnd: case BitOr: case BitXor: case Shl: case SShr: case ZShr: case Equal: case NotEqual: case LessThan: case GreaterThan: case Above: case Below: case AboveEqual: case BelowEqual: case Div: return proc.add<Value>(opcode(), type(), origin, child(proc, 0), child(proc, 1)); case Select: return proc.add<Value>(opcode(), type(), origin, child(proc, 0), child(proc, 1), child(proc, 2)); case Const32: return proc.add<Const32Value>(origin, static_cast<int32_t>(value())); case Const64: return proc.add<Const64Value>(origin, value()); case ConstDouble: return proc.add<ConstDoubleValue>(origin, doubleValue()); case ConstFloat: return proc.add<ConstFloatValue>(origin, floatValue()); case ArgumentReg: return proc.add<ArgumentRegValue>(origin, Reg::fromIndex(static_cast<unsigned>(value()))); case SlotBase: return proc.add<SlotBaseValue>(origin, proc.stackSlots()[value()]); default: return nullptr; } }
TEST(PropagationTest1, localDCE1) { g_redex = new RedexContext(); const char* dexfile = std::getenv("dexfile"); ASSERT_NE(nullptr, dexfile); std::vector<DexStore> stores; DexMetadata dm; dm.set_id("classes"); DexStore root_store(dm); root_store.add_classes(load_classes_from_dex(dexfile)); DexClasses& classes = root_store.get_dexen().back(); stores.emplace_back(std::move(root_store)); std::cout << "Loaded classes: " << classes.size() << std::endl ; TRACE(DCE, 2, "Code before:\n"); for(const auto& cls : classes) { TRACE(DCE, 2, "Class %s\n", SHOW(cls)); for (const auto& dm : cls->get_dmethods()) { TRACE(DCE, 2, "dmethod: %s\n", dm->get_name()->c_str()); if (strcmp(dm->get_name()->c_str(), "propagate") == 0) { TRACE(DCE, 2, "dmethod: %s\n", SHOW(dm->get_code())); } } } std::vector<Pass*> passes = { new PeepholePass(), new LocalDcePass(), }; PassManager manager(passes); manager.set_testing_mode(); Json::Value conf_obj = Json::nullValue; ConfigFiles dummy_cfg(conf_obj); manager.run_passes(stores, dummy_cfg); TRACE(DCE, 2, "Code after:\n"); for(const auto& cls : classes) { TRACE(DCE, 2, "Class %s\n", SHOW(cls)); for (const auto& dm : cls->get_dmethods()) { TRACE(DCE, 2, "dmethod: %s\n", dm->get_name()->c_str()); if (strcmp(dm->get_name()->c_str(), "propagate") == 0) { TRACE(DCE, 2, "dmethod: %s\n", SHOW(dm->get_code())); for (auto& mie : InstructionIterable(dm->get_code())) { auto instruction = mie.insn; // Make sure there is no invoke-virtual in the optimized method. ASSERT_NE(instruction->opcode(), OPCODE_INVOKE_VIRTUAL); // Make sure there is no const-class in the optimized method. ASSERT_NE(instruction->opcode(), OPCODE_CONST_CLASS); } } } } }
void compile() { while(word()) { // For each word in input if (line > 2) skip(); // Skip to end of line if we are in a comment ident = string(); // Find string identity if (ident == 0) continue; // If it is an empty string continue fprintf(stderr,"found string ["); dump(); fprintf(stderr,"]\n"); opcode() ? byte(opcode()): // If opcode compile it otherwise method() ? function(): // Else see if it is a method, and compile a function call find() ? nop() : unknown(); // Otherwise see if it is an object, or unknown if (key == 0x60) line = 0; // reset line on newline } }
DexOpcode* DexOpcode::set_arg_word_count(uint16_t count) { auto DEBUG_ONLY format = opcode_format(opcode()); assert(format == FMT_f35c); assert((count & 0xf) == count); m_opcode = (m_opcode & 0x0fff) | (count << 12); return this; }
void EiC_peephole(code_t *C, int *visit) { int i, n,j; n = nextinst(C); for(i = 0; i < n; ++i) if(isgoto(C,i)) { j = i + ivalcode(C,i); if(j >= n) continue; if(opcode(C,j) == jmpu || opcode(C,i) == opcode(C,j)) { /*visit[i+ivalcode(C,i)]--;*/ ivalcode(C,i) += ivalcode(C,j); } } }
WT_Result WT_Text_Option_Scoring::serialize(WT_Object const &, WT_File & file) const { if (file.heuristics().allow_binary_data()) { // Binary output WD_CHECK (file.write_count(m_count + 1)); for (int ii = 0; ii < m_count; ii++) WD_CHECK (file.write_count(m_positions[ii] + 1)); } else { // ASCII output if (m_count) { WD_CHECK (file.write((WT_Byte) ' ')); WD_CHECK (file.write(opcode())); WD_CHECK (file.write(" (")); WD_CHECK (file.write_ascii(m_count)); WD_CHECK (file.write((WT_Byte) ' ')); WD_CHECK (file.write_ascii(m_positions[0])); for (int ii = 1; ii < m_count; ii++) { WD_CHECK (file.write((WT_Byte) ',')); WD_CHECK (file.write_ascii(m_positions[ii])); } WD_CHECK (file.write("))")); } // if (m_count) } // ascii return WT_Result::Success; }
uint16_t DexInstruction::range_size() const { auto format = opcode_format(opcode()); assert(format == FMT_f3rc || format == FMT_f5rc); if (format == FMT_f5rc) return m_arg[0]; return (m_opcode >> 8) & 0xff; }
uint16_t DexInstruction::range_base() const { auto format = opcode_format(opcode()); assert(format == FMT_f3rc || format == FMT_f5rc); if (format == FMT_f5rc) return m_arg[1]; return m_arg[0]; }
DexInstruction* DexInstruction::set_offset(int32_t offset) { auto format = opcode_format(opcode()); switch (format) { case FMT_f10t: always_assert_log((int32_t)(int8_t)(offset & 0xff) == offset, "offset %d too large for %s", offset, SHOW(this)); m_opcode = (m_opcode & 0xff) | ((offset & 0xff) << 8); return this; case FMT_f20t: case FMT_f21t: case FMT_f22t: always_assert_log((int32_t)(int16_t)(offset & 0xffff) == offset, "offset %d too large for %s", offset, SHOW(this)); m_arg[0] = offset; return this; case FMT_f30t: case FMT_f31t: m_arg[0] = offset; m_arg[1] = offset >> 16; return this; default: assert(false); } not_reached(); }
DexInstruction* DexInstruction::set_literal(int64_t literal) { assert(has_literal()); auto format = opcode_format(opcode()); switch (format) { case FMT_f11n: m_opcode = (m_opcode & 0xfff) | ((literal & 0xf) << 12); return this; case FMT_f21s: m_arg[0] = literal; return this; case FMT_f21h: m_arg[0] = literal >> 16; return this; case FMT_f22b: m_arg[0] = literal >> 48; return this; case FMT_f22s: m_arg[0] = literal; return this; case FMT_f31i: m_arg[0] = literal & 0xffff; m_arg[1] = literal >> 16; return this; case FMT_f51l: m_arg[0] = literal; m_arg[1] = literal >> 16; m_arg[2] = literal >> 32; m_arg[3] = literal >> 48; return this; default: assert(false); } not_reached(); }
int64_t DexInstruction::literal() const { assert(has_literal()); auto format = opcode_format(opcode()); switch (format) { case FMT_f11n: return signext<4>(m_opcode >> 12); case FMT_f21s: return signext<16>(m_arg[0]); case FMT_f21h: return signext<16>(m_arg[0]) << 16; case FMT_f22b: return signext<16>(m_arg[0]) << 48; case FMT_f22s: return signext<16>(m_arg[0]); case FMT_f31i: { auto literal = uint32_t(m_arg[0]) | (uint32_t(m_arg[1]) << 16); return signext<32>(literal); } case FMT_f51l: { auto literal = uint64_t(m_arg[0]) | (uint64_t(m_arg[1]) << 16) | (uint64_t(m_arg[2]) << 32) | (uint64_t(m_arg[3]) << 48); return signext<64>(literal); } default: assert(false); } not_reached(); }
uint16_t DexInstruction::dest() const { auto format = opcode_format(opcode()); switch (format) { case FMT_f12x: case FMT_f12x_2: case FMT_f11n: case FMT_f22s: case FMT_f22c_d: case FMT_f22cs: return (m_opcode >> 8) & 0xf; case FMT_f11x_d: case FMT_f22x: case FMT_f21s: case FMT_f21h: case FMT_f21c_d: case FMT_f23x_d: case FMT_f22b: case FMT_f31i: case FMT_f31c: case FMT_f51l: return (m_opcode >> 8) & 0xff; case FMT_f32x: return m_arg[0]; case FMT_f41c_d: case FMT_f52c_d: return m_arg[0]; default: // All other formats do not define a destination register. always_assert_log(false, "Unhandled opcode: %s", SHOW(this)); } not_reached(); }
void assemble(SE *code,FILE *out,char first) { char *opsym; /* 1) machine code file should always start with number of used env-slots (symbols), 2) machine code should at first add T atom to its environment, like this: (CONST t NAME t). */ if(first==1) fprintf(out,"(%d %d t %d 0 ",symbols_p,O_CONST,O_NAME); else fprintf(out,"( "); while(code!=NULL) { opsym=symval(car(code)); fprintf(out,"%d ",opcode(opsym)); if(strcmp(opsym,"const")==0) { write_se(car(cdr(code)),out); fprintf(out," "); code=cdr(cdr(code)); continue; } else if(strcmp(opsym,"lookup")==0 || strcmp(opsym,"name")==0 || strcmp(opsym,"forget")==0) { fprintf(out,"%d ",sym2num(symval(car(cdr(code))))); code=cdr(cdr(code)); continue; } else if(strcmp(opsym,"select")==0 || strcmp(opsym,"srelect")==0) { assemble(car(cdr(code)),out,0); assemble(car(cdr(cdr(code))),out,0); code=cdr(cdr(cdr(code))); continue; } else if(strcmp(opsym,"proc")==0) { assemble(car(cdr(code)),out,0); code=cdr(cdr(code)); continue; } else { code=cdr(code); } } /* while... */ fprintf(out,") "); }
// Format: <string_or_id>[<n><value_1>...<value_n>]<consistency> // where: // <string_or_id> is a [long string] for <string> and a [short bytes] for <id> // <n> is a [short] (only for execute statements) // <value> is a [bytes] (only for execute statements) // <consistency> is a [short] int32_t Statement::encode_v1(RequestCallback* callback, BufferVec* bufs) const { int32_t length = 0; const int version = 1; bufs->push_back(query_or_id_); length += query_or_id_.size(); if (opcode() == CQL_OPCODE_EXECUTE) { { // <n> [short] Buffer buf(sizeof(uint16_t)); buf.encode_uint16(0, elements().size()); bufs->push_back(buf); length += sizeof(uint16_t); } // <value_1>...<value_n> int32_t result = encode_values(version, callback, bufs); if (result < 0) return result; length += result; } { // <consistency> [short] Buffer buf(sizeof(uint16_t)); buf.encode_uint16(0, callback->consistency()); bufs->push_back(buf); length += sizeof(uint16_t); } return length; }
std::string Statement::query() const { if (opcode() == CQL_OPCODE_QUERY) { return std::string(query_or_id_.data() + sizeof(int32_t), query_or_id_.size() - sizeof(int32_t)); } return std::string(); }
DexOpcode* DexOpcode::set_dest(uint16_t vreg) { auto format = opcode_format(opcode()); switch (format) { case FMT_f12x: case FMT_f12x_2: case FMT_f11n: case FMT_f22s: case FMT_f22c_d: case FMT_f22cs: assert((vreg & 0xf) == vreg); m_opcode = (m_opcode & 0xf0ff) | (vreg << 8); return this; case FMT_f11x_d: case FMT_f22x: case FMT_f21s: case FMT_f21h: case FMT_f21c_d: case FMT_f23x_d: case FMT_f22b: case FMT_f31i: case FMT_f31c: case FMT_f51l: assert((vreg & 0xff) == vreg); m_opcode = (m_opcode & 0x00ff) | (vreg << 8); return this; case FMT_f32x: m_arg[0] = vreg; return this; default: // All other formats do not define a destination register. always_assert_log(false, "Unhandled opcode: %s", SHOW(this)); } not_reached(); }
// Return the vector version of a scalar load node. VectorLoadNode* VectorLoadNode::make(Compile* C, int opc, Node* ctl, Node* mem, Node* adr, const TypePtr* atyp, uint vlen) { int vopc = opcode(opc, vlen); switch(vopc) { case Op_Load16B: return new (C, 3) Load16BNode(ctl, mem, adr, atyp); case Op_Load8B: return new (C, 3) Load8BNode(ctl, mem, adr, atyp); case Op_Load4B: return new (C, 3) Load4BNode(ctl, mem, adr, atyp); case Op_Load8C: return new (C, 3) Load8CNode(ctl, mem, adr, atyp); case Op_Load4C: return new (C, 3) Load4CNode(ctl, mem, adr, atyp); case Op_Load2C: return new (C, 3) Load2CNode(ctl, mem, adr, atyp); case Op_Load8S: return new (C, 3) Load8SNode(ctl, mem, adr, atyp); case Op_Load4S: return new (C, 3) Load4SNode(ctl, mem, adr, atyp); case Op_Load2S: return new (C, 3) Load2SNode(ctl, mem, adr, atyp); case Op_Load4I: return new (C, 3) Load4INode(ctl, mem, adr, atyp); case Op_Load2I: return new (C, 3) Load2INode(ctl, mem, adr, atyp); case Op_Load2L: return new (C, 3) Load2LNode(ctl, mem, adr, atyp); case Op_Load4F: return new (C, 3) Load4FNode(ctl, mem, adr, atyp); case Op_Load2F: return new (C, 3) Load2FNode(ctl, mem, adr, atyp); case Op_Load2D: return new (C, 3) Load2DNode(ctl, mem, adr, atyp); } ShouldNotReachHere(); return NULL; }
// Return the vector version of a scalar store node. VectorStoreNode* VectorStoreNode::make(Compile* C, int opc, Node* ctl, Node* mem, Node* adr, const TypePtr* atyp, VectorNode* val, uint vlen) { int vopc = opcode(opc, vlen); switch(vopc) { case Op_Store16B: return new (C, 4) Store16BNode(ctl, mem, adr, atyp, val); case Op_Store8B: return new (C, 4) Store8BNode(ctl, mem, adr, atyp, val); case Op_Store4B: return new (C, 4) Store4BNode(ctl, mem, adr, atyp, val); case Op_Store8C: return new (C, 4) Store8CNode(ctl, mem, adr, atyp, val); case Op_Store4C: return new (C, 4) Store4CNode(ctl, mem, adr, atyp, val); case Op_Store2C: return new (C, 4) Store2CNode(ctl, mem, adr, atyp, val); case Op_Store4I: return new (C, 4) Store4INode(ctl, mem, adr, atyp, val); case Op_Store2I: return new (C, 4) Store2INode(ctl, mem, adr, atyp, val); case Op_Store2L: return new (C, 4) Store2LNode(ctl, mem, adr, atyp, val); case Op_Store4F: return new (C, 4) Store4FNode(ctl, mem, adr, atyp, val); case Op_Store2F: return new (C, 4) Store2FNode(ctl, mem, adr, atyp, val); case Op_Store2D: return new (C, 4) Store2DNode(ctl, mem, adr, atyp, val); } ShouldNotReachHere(); return NULL; }
bool Value::returnsBool() const { if (type() != Int32) return false; switch (opcode()) { case Const32: return asInt32() == 0 || asInt32() == 1; case BitAnd: return child(1)->isInt32(1); case Equal: case NotEqual: case LessThan: case GreaterThan: case LessEqual: case GreaterEqual: case Above: case Below: case AboveEqual: case BelowEqual: return true; case Phi: // FIXME: We should have a story here. // https://bugs.webkit.org/show_bug.cgi?id=150725 return false; default: return false; } }
bool CommonUniformElimPass::IsLoopHeader(ir::BasicBlock* block_ptr) { auto iItr = block_ptr->tail(); if (iItr == block_ptr->begin()) return false; --iItr; return iItr->opcode() == SpvOpLoopMerge; }
bool CommonUniformElimPass::CommonUniformLoadElimBlock(ir::Function* func) { bool modified = false; for (auto& blk : *func) { uniform2load_id_.clear(); for (auto ii = blk.begin(); ii != blk.end(); ++ii) { if (ii->opcode() != SpvOpLoad) continue; uint32_t varId; ir::Instruction* ptrInst = GetPtr(&*ii, &varId); if (ptrInst->opcode() != SpvOpVariable) continue; if (!IsUniformVar(varId)) continue; if (!IsSamplerOrImageVar(varId)) continue; if (HasUnsupportedDecorates(ii->result_id())) continue; uint32_t replId; const auto uItr = uniform2load_id_.find(varId); if (uItr != uniform2load_id_.end()) { replId = uItr->second; } else { uniform2load_id_[varId] = ii->result_id(); continue; } ReplaceAndDeleteLoad(&*ii, replId, ptrInst); modified = true; } } return modified; }
bool CommonUniformElimPass::UniformAccessChainConvert(ir::Function* func) { bool modified = false; for (auto bi = func->begin(); bi != func->end(); ++bi) { for (auto ii = bi->begin(); ii != bi->end(); ++ii) { if (ii->opcode() != SpvOpLoad) continue; uint32_t varId; ir::Instruction* ptrInst = GetPtr(&*ii, &varId); if (!IsNonPtrAccessChain(ptrInst->opcode())) continue; // Do not convert nested access chains if (ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx) != varId) continue; if (!IsUniformVar(varId)) continue; if (!IsConstantIndexAccessChain(ptrInst)) continue; if (HasUnsupportedDecorates(ii->result_id())) continue; if (HasUnsupportedDecorates(ptrInst->result_id())) continue; std::vector<std::unique_ptr<ir::Instruction>> newInsts; uint32_t replId; GenACLoadRepl(ptrInst, &newInsts, &replId); ReplaceAndDeleteLoad(&*ii, replId, ptrInst); ++ii; ii = ii.InsertBefore(&newInsts); ++ii; modified = true; } } return modified; }
void Pass::AddCalls(ir::Function* func, std::queue<uint32_t>* todo) { for (auto bi = func->begin(); bi != func->end(); ++bi) for (auto ii = bi->begin(); ii != bi->end(); ++ii) if (ii->opcode() == SpvOpFunctionCall) todo->push(ii->GetSingleWordInOperand(0)); }
void patch_iget(DexMethod* meth, IRList::iterator it, DexType* original_field_type) { auto insn = it->insn; const auto op = insn->opcode(); always_assert(is_iget(op)); switch (op) { case OPCODE_IGET_OBJECT: { auto dest = std::next(it)->insn->dest(); auto cast = ModelMethodMerger::make_check_cast(original_field_type, dest); meth->get_code()->insert_after(insn, cast); break; } case OPCODE_IGET_BYTE: { always_assert(original_field_type == get_byte_type()); auto int_to_byte = new IRInstruction(OPCODE_INT_TO_BYTE); patch_iget_for_int_like_types(meth, it, int_to_byte); break; } case OPCODE_IGET_CHAR: { always_assert(original_field_type == get_char_type()); auto int_to_char = new IRInstruction(OPCODE_INT_TO_CHAR); patch_iget_for_int_like_types(meth, it, int_to_char); break; } case OPCODE_IGET_SHORT: { always_assert(original_field_type == get_short_type()); auto int_to_short = new IRInstruction(OPCODE_INT_TO_SHORT); patch_iget_for_int_like_types(meth, it, int_to_short); break; } default: break; } };
uint16_t DexInstruction::arg_word_count() const { auto format = opcode_format(opcode()); assert(format == FMT_f35c || format == FMT_f57c); if (format == FMT_f57c) { return (m_arg[0]) & 0xf; } return (m_opcode >> 12) & 0xf; }
Value* Value::invertedCompare(Procedure& proc) const { if (!numChildren()) return nullptr; if (Optional<Opcode> invertedOpcode = B3::invertedCompare(opcode(), child(0)->type())) return proc.add<Value>(*invertedOpcode, type(), origin(), children()); return nullptr; }
TEST_F(PostVerify, testArrayDataInCaller) { auto cls = find_class_named( classes, "Lcom/facebook/redexinline/InlineTest;"); auto m = find_vmethod_named(*cls, "testArrayDataInCaller"); ASSERT_EQ(nullptr, find_invoke(m, OPCODE_INVOKE_DIRECT, "callerWithIf")); auto last_insn = m->get_code()->get_instructions().back(); ASSERT_EQ(last_insn->opcode(), FOPCODE_FILLED_ARRAY); }