TEST(offset_map_test, percentile_distance_should_be_computed_correctly) { OffsetMap test = OffsetMap(4, 1); test.ptr(0, 0)->distance = 10; test.ptr(0, 1)->distance = 40; test.ptr(0, 2)->distance = 60; test.ptr(0, 3)->distance = 1000; float gotten_percentile = test.get75PercentileDistance(); ASSERT_EQ(60, gotten_percentile); }
TEST(offset_map_test, flipping_should_work_on_square_image) { OffsetMap test = OffsetMap(101, 101); ASSERT_FALSE(test.isFlipped()); OffsetMapEntry *middle = test.ptr(50, 50); middle->distance = 100; // Check if it was really written there: OffsetMapEntry middle_copy = test.at(50, 50); ASSERT_EQ(100, middle_copy.distance); // Flip offset map, test again. test.flip(); ASSERT_TRUE(test.isFlipped()); OffsetMapEntry middle_copy_flipped = test.at(50, 50); ASSERT_EQ(100, middle_copy_flipped.distance); }
TEST(offset_map_test, flipping_should_work_on_square_image_for_top_left) { OffsetMap test = OffsetMap(100, 100); ASSERT_FALSE(test.isFlipped()); OffsetMapEntry *top_left = test.ptr(0, 0); top_left->distance = 100; // Check if it was really written there: OffsetMapEntry top_left_copy = test.at(0, 0); ASSERT_EQ(100, top_left_copy.distance); // Flip offset map, test again. test.flip(); ASSERT_TRUE(test.isFlipped()); ASSERT_EQ(100, test._width); ASSERT_EQ(100, test._height); OffsetMapEntry *bottom_right_flipped = test.ptr(99, 99); EXPECT_EQ(100, bottom_right_flipped->distance); }
bool ModuleGenerator::convertOutOfRangeBranchesToThunks() { masm_.haltingAlign(CodeAlignment); // Create thunks for callsites that have gone out of range. Use a map to // create one thunk for each callee since there is often high reuse. OffsetMap alreadyThunked; if (!alreadyThunked.init()) return false; for (; lastPatchedCallsite_ < masm_.callSites().length(); lastPatchedCallsite_++) { const CallSiteAndTarget& cs = masm_.callSites()[lastPatchedCallsite_]; if (!cs.isInternal()) continue; uint32_t callerOffset = cs.returnAddressOffset(); MOZ_RELEASE_ASSERT(callerOffset < INT32_MAX); if (funcIsDefined(cs.targetIndex())) { uint32_t calleeOffset = funcCodeRange(cs.targetIndex()).funcNonProfilingEntry(); MOZ_RELEASE_ASSERT(calleeOffset < INT32_MAX); if (uint32_t(abs(int32_t(calleeOffset) - int32_t(callerOffset))) < JumpRange()) { masm_.patchCall(callerOffset, calleeOffset); continue; } } OffsetMap::AddPtr p = alreadyThunked.lookupForAdd(cs.targetIndex()); if (!p) { Offsets offsets; offsets.begin = masm_.currentOffset(); uint32_t thunkOffset = masm_.thunkWithPatch().offset(); if (masm_.oom()) return false; offsets.end = masm_.currentOffset(); if (!metadata_->codeRanges.emplaceBack(CodeRange::CallThunk, offsets)) return false; if (!metadata_->callThunks.emplaceBack(thunkOffset, cs.targetIndex())) return false; if (!alreadyThunked.add(p, cs.targetIndex(), offsets.begin)) return false; } masm_.patchCall(callerOffset, p->value()); } // Create thunks for jumps to stubs. Stubs are always generated at the end // so unconditionally thunk all existing jump sites. for (JumpTarget target : MakeEnumeratedRange(JumpTarget::Limit)) { if (masm_.jumpSites()[target].empty()) continue; for (uint32_t jumpSite : masm_.jumpSites()[target]) { RepatchLabel label; label.use(jumpSite); masm_.bind(&label); } Offsets offsets; offsets.begin = masm_.currentOffset(); uint32_t thunkOffset = masm_.thunkWithPatch().offset(); if (masm_.oom()) return false; offsets.end = masm_.currentOffset(); if (!metadata_->codeRanges.emplaceBack(CodeRange::Inline, offsets)) return false; if (!jumpThunks_[target].append(thunkOffset)) return false; } // Unlike callsites, which need to be persisted in the Module, we can simply // flush jump sites after each patching pass. masm_.clearJumpSites(); return true; }
bool ModuleGenerator::patchCallSites(TrapExitOffsetArray* maybeTrapExits) { masm_.haltingAlign(CodeAlignment); // Create far jumps for calls that have relative offsets that may otherwise // go out of range. Far jumps are created for two cases: direct calls // between function definitions and calls to trap exits by trap out-of-line // paths. Far jump code is shared when possible to reduce bloat. This method // is called both between function bodies (at a frequency determined by the // ISA's jump range) and once at the very end of a module's codegen after // all possible calls/traps have been emitted. OffsetMap existingCallFarJumps; if (!existingCallFarJumps.init()) return false; EnumeratedArray<Trap, Trap::Limit, Maybe<uint32_t>> existingTrapFarJumps; for (; lastPatchedCallsite_ < masm_.callSites().length(); lastPatchedCallsite_++) { const CallSiteAndTarget& cs = masm_.callSites()[lastPatchedCallsite_]; uint32_t callerOffset = cs.returnAddressOffset(); MOZ_RELEASE_ASSERT(callerOffset < INT32_MAX); switch (cs.kind()) { case CallSiteDesc::Dynamic: case CallSiteDesc::Symbolic: break; case CallSiteDesc::Func: { if (funcIsCompiled(cs.funcIndex())) { uint32_t calleeOffset = funcCodeRange(cs.funcIndex()).funcNonProfilingEntry(); MOZ_RELEASE_ASSERT(calleeOffset < INT32_MAX); if (uint32_t(abs(int32_t(calleeOffset) - int32_t(callerOffset))) < JumpRange()) { masm_.patchCall(callerOffset, calleeOffset); break; } } OffsetMap::AddPtr p = existingCallFarJumps.lookupForAdd(cs.funcIndex()); if (!p) { Offsets offsets; offsets.begin = masm_.currentOffset(); uint32_t jumpOffset = masm_.farJumpWithPatch().offset(); offsets.end = masm_.currentOffset(); if (masm_.oom()) return false; if (!metadata_->codeRanges.emplaceBack(CodeRange::FarJumpIsland, offsets)) return false; if (!existingCallFarJumps.add(p, cs.funcIndex(), offsets.begin)) return false; // Record calls' far jumps in metadata since they must be // repatched at runtime when profiling mode is toggled. if (!metadata_->callThunks.emplaceBack(jumpOffset, cs.funcIndex())) return false; } masm_.patchCall(callerOffset, p->value()); break; } case CallSiteDesc::TrapExit: { if (maybeTrapExits) { uint32_t calleeOffset = (*maybeTrapExits)[cs.trap()].begin; MOZ_RELEASE_ASSERT(calleeOffset < INT32_MAX); if (uint32_t(abs(int32_t(calleeOffset) - int32_t(callerOffset))) < JumpRange()) { masm_.patchCall(callerOffset, calleeOffset); break; } } if (!existingTrapFarJumps[cs.trap()]) { Offsets offsets; offsets.begin = masm_.currentOffset(); masm_.append(TrapFarJump(cs.trap(), masm_.farJumpWithPatch())); offsets.end = masm_.currentOffset(); if (masm_.oom()) return false; if (!metadata_->codeRanges.emplaceBack(CodeRange::FarJumpIsland, offsets)) return false; existingTrapFarJumps[cs.trap()] = Some(offsets.begin); } masm_.patchCall(callerOffset, *existingTrapFarJumps[cs.trap()]); break; } } } return true; }