template<class T> uint64_t test_const(T val) { using testfunc = double (*)(); static const Abi test_abi = { .gpUnreserved = RegSet{}, .gpReserved = x64::abi().gp(), .simdUnreserved = RegSet{xmm0}, .simdReserved = x64::abi().simd() - RegSet{xmm0}, .calleeSaved = x64::abi().calleeSaved, .sf = x64::abi().sf }; static uint8_t code[1000]; // None of these tests should use any data. static uint8_t data_buffer[0]; CodeBlock main; main.init(code, sizeof(code), "test"); DataBlock data; data.init(data_buffer, sizeof(data), "data"); Vunit unit; Vasm vasm{unit}; Vtext text { main, data }; auto& v = vasm.main(); unit.entry = v; v << copy{v.cns(val), Vreg{xmm0}}; v << ret{RegSet{xmm0}}; optimizeX64(vasm.unit(), test_abi, true /* regalloc */); CGMeta fixups; LeaseHolder writer{Translator::WriteLease()}; EXPECT_TRUE(writer.canWrite()); emitX64(unit, text, fixups, nullptr); // The above code might use fixups.literals but shouldn't use anything else. fixups.literals.clear(); EXPECT_TRUE(fixups.empty()); union { double d; uint64_t c; } u; u.d = ((testfunc)code)(); return u.c; }
template<class T> uint64_t test_const(T val) { using testfunc = double (*)(); static const Abi test_abi = { .gpUnreserved = RegSet{}, .gpReserved = x64::abi().gp(), .simdUnreserved = RegSet{xmm0}, .simdReserved = x64::abi().simd() - RegSet{xmm0}, .calleeSaved = x64::abi().calleeSaved, .sf = x64::abi().sf }; static uint8_t code[1000]; CodeBlock main; main.init(code, sizeof(code), "test"); Vasm vasm; Vtext text { main }; auto& unit = vasm.unit(); auto& v = vasm.main(); unit.entry = v; v << copy{v.cns(val), Vreg{xmm0}}; v << ret{RegSet{xmm0}}; optimizeX64(vasm.unit(), test_abi); CGMeta fixups; emitX64(unit, text, fixups, nullptr); // The above code might use fixups.literals but shouldn't use anything else. fixups.literals.clear(); EXPECT_TRUE(fixups.empty()); union { double d; uint64_t c; } u; u.d = ((testfunc)code)(); return u.c; }
void relocate(std::vector<TransRelocInfo>& relocs, CodeBlock& dest, CGMeta& fixups) { assertOwnsCodeLock(); assert(!Func::s_treadmill); auto newRelocMapName = Debug::DebugInfo::Get()->getRelocMapName() + ".tmp"; auto newRelocMap = fopen(newRelocMapName.c_str(), "w+"); if (!newRelocMap) return; SCOPE_EXIT { if (newRelocMap) fclose(newRelocMap); }; Func::s_treadmill = true; SCOPE_EXIT { Func::s_treadmill = false; }; auto ignoreEntry = [](const SrcKey& sk) { // We can have entries such as UniqueStubs with no SrcKey // These are ok to process. if (!sk.valid()) return false; // But if the func has been removed from the AtomicHashMap, // we don't want to process it. return !Func::isFuncIdValid(sk.funcID()); }; RelocationInfo rel; size_t num = 0; assert(fixups.alignments.empty()); for (size_t sz = relocs.size(); num < sz; num++) { auto& reloc = relocs[num]; if (ignoreEntry(reloc.sk)) continue; auto start DEBUG_ONLY = dest.frontier(); try { x64::relocate(rel, dest, reloc.start, reloc.end, reloc.fixups, nullptr); } catch (const DataBlockFull& dbf) { break; } if (Trace::moduleEnabledRelease(Trace::mcg, 1)) { Trace::traceRelease( folly::sformat("Relocate: 0x{:08x}+0x{:04x} => 0x{:08x}+0x{:04x}\n", (uintptr_t)reloc.start, reloc.end - reloc.start, (uintptr_t)start, dest.frontier() - start)); } } swap_trick(fixups.alignments); assert(fixups.empty()); x64::adjustForRelocation(rel); for (size_t i = 0; i < num; i++) { if (!ignoreEntry(relocs[i].sk)) { x64::adjustMetaDataForRelocation(rel, nullptr, relocs[i].fixups); } } for (size_t i = 0; i < num; i++) { if (!ignoreEntry(relocs[i].sk)) { relocs[i].fixups.process_only(nullptr); } } // At this point, all the relocated code should be correct, and runable. // But eg if it has unlikely paths into cold code that has not been relocated, // then the cold code will still point back to the original, not the relocated // versions. Similarly reusable stubs will still point to the old code. // Since we can now execute the relocated code, its ok to start fixing these // things now. for (auto& it : srcDB()) { it.second->relocate(rel); } std::unordered_set<Func*> visitedFuncs; CodeSmasher s; for (size_t i = 0; i < num; i++) { auto& reloc = relocs[i]; if (ignoreEntry(reloc.sk)) continue; for (auto& ib : reloc.incomingBranches) { ib.relocate(rel); } if (!reloc.sk.valid()) continue; auto f = const_cast<Func*>(reloc.sk.func()); x64::adjustCodeForRelocation(rel, reloc.fixups); reloc.fixups.clear(); // fixup code references in the corresponding cold block to point // to the new code x64::adjustForRelocation(rel, reloc.coldStart, reloc.coldEnd); if (visitedFuncs.insert(f).second) { if (auto adjusted = rel.adjustedAddressAfter(f->getFuncBody())) { f->setFuncBody(adjusted); } int num = Func::getMaxNumPrologues(f->numParams()); if (num < kNumFixedPrologues) num = kNumFixedPrologues; while (num--) { auto addr = f->getPrologue(num); if (auto adjusted = rel.adjustedAddressAfter(addr)) { f->setPrologue(num, adjusted); } } } if (reloc.end != reloc.start) { s.entries.emplace_back(reloc.start, reloc.end); } } auto relocMap = Debug::DebugInfo::Get()->getRelocMap(); always_assert(relocMap); fseek(relocMap, 0, SEEK_SET); auto deadStubs = getFreeTCStubs(); auto param = PostProcessParam { rel, deadStubs, newRelocMap }; std::set<TCA> liveStubs; readRelocations(relocMap, &liveStubs, postProcess, ¶m); // ensure that any reusable stubs are updated for the relocated code for (auto stub : liveStubs) { FTRACE(1, "Stub: 0x{:08x}\n", (uintptr_t)stub); fixups.reusedStubs.emplace_back(stub); always_assert(!rel.adjustedAddressAfter(stub)); fprintf(newRelocMap, "%" PRIxPTR " 0 %s\n", uintptr_t(stub), "NewStub"); } x64::adjustCodeForRelocation(rel, fixups); unlink(Debug::DebugInfo::Get()->getRelocMapName().c_str()); rename(newRelocMapName.c_str(), Debug::DebugInfo::Get()->getRelocMapName().c_str()); fclose(newRelocMap); newRelocMap = nullptr; freopen(Debug::DebugInfo::Get()->getRelocMapName().c_str(), "r+", relocMap); fseek(relocMap, 0, SEEK_END); okToRelocate = false; Treadmill::enqueue(std::move(s)); }