mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to fx-team
This commit is contained in:
commit
b351224cce
@ -10,19 +10,29 @@ relativesrcdir = @relativesrcdir@
|
|||||||
|
|
||||||
include $(DEPTH)/config/autoconf.mk
|
include $(DEPTH)/config/autoconf.mk
|
||||||
|
|
||||||
MOCHITEST_BROWSER_FILES = browser_405664.js \
|
MOCHITEST_BROWSER_FILES = \
|
||||||
browser_addEngine.js \
|
browser_405664.js \
|
||||||
browser_contextmenu.js \
|
browser_addEngine.js \
|
||||||
testEngine.xml \
|
browser_contextmenu.js \
|
||||||
testEngine_mozsearch.xml \
|
testEngine.xml \
|
||||||
testEngine.src \
|
testEngine_mozsearch.xml \
|
||||||
browser_426329.js \
|
testEngine.src \
|
||||||
426329.xml \
|
browser_426329.js \
|
||||||
browser_483086.js \
|
426329.xml \
|
||||||
483086-1.xml \
|
browser_483086.js \
|
||||||
483086-2.xml \
|
483086-1.xml \
|
||||||
test.html \
|
483086-2.xml \
|
||||||
browser_private_search.js \
|
test.html \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
|
||||||
|
MOCHITEST_BROWSER_FILES += \
|
||||||
|
browser_private_search_perwindowpb.js \
|
||||||
|
$(NULL)
|
||||||
|
else
|
||||||
|
MOCHITEST_BROWSER_FILES += \
|
||||||
|
browser_private_search.js \
|
||||||
|
$(NULL)
|
||||||
|
endif
|
||||||
|
|
||||||
include $(topsrcdir)/config/rules.mk
|
include $(topsrcdir)/config/rules.mk
|
||||||
|
@ -0,0 +1,115 @@
|
|||||||
|
// This test performs a search in a public window, then a different
|
||||||
|
// search in a private window, and then checks in the public window
|
||||||
|
// whether there is an autocomplete entry for the private search.
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
|
||||||
|
let engineURL =
|
||||||
|
"http://mochi.test:8888/browser/browser/components/search/test/";
|
||||||
|
let windowsToClose = [];
|
||||||
|
registerCleanupFunction(function() {
|
||||||
|
let engine = Services.search.getEngineByName("Bug 426329");
|
||||||
|
Services.search.removeEngine(engine);
|
||||||
|
windowsToClose.forEach(function(win) {
|
||||||
|
win.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function onPageLoad(aWin, aCallback) {
|
||||||
|
aWin.gBrowser.addEventListener("DOMContentLoaded", function load(aEvent) {
|
||||||
|
let doc = aEvent.originalTarget;
|
||||||
|
info(doc.location.href);
|
||||||
|
if (doc.location.href.indexOf(engineURL) != -1) {
|
||||||
|
aWin.gBrowser.removeEventListener("DOMContentLoaded", load, false);
|
||||||
|
aCallback();
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function performSearch(aWin, aIsPrivate, aCallback) {
|
||||||
|
let searchBar = aWin.BrowserSearch.searchBar;
|
||||||
|
ok(searchBar, "got search bar");
|
||||||
|
onPageLoad(aWin, aCallback);
|
||||||
|
|
||||||
|
searchBar.value = aIsPrivate ? "private test" : "public test";
|
||||||
|
searchBar.focus();
|
||||||
|
EventUtils.synthesizeKey("VK_RETURN", {}, aWin);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addEngine(aCallback) {
|
||||||
|
function observer(aSub, aTopic, aData) {
|
||||||
|
switch (aData) {
|
||||||
|
case "engine-current":
|
||||||
|
ok(Services.search.currentEngine.name == "Bug 426329",
|
||||||
|
"currentEngine set");
|
||||||
|
aCallback();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Services.obs.addObserver(observer, "browser-search-engine-modified", false);
|
||||||
|
Services.search.addEngine(
|
||||||
|
engineURL + "426329.xml", Ci.nsISearchEngine.DATA_XML,
|
||||||
|
"data:image/x-icon,%00", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testOnWindow(aIsPrivate, aCallback) {
|
||||||
|
let win = OpenBrowserWindow({ private: aIsPrivate });
|
||||||
|
win.addEventListener("load", function onLoad() {
|
||||||
|
win.removeEventListener("load", onLoad, false);
|
||||||
|
windowsToClose.push(win);
|
||||||
|
executeSoon(function() aCallback(win));
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
addEngine(function() {
|
||||||
|
testOnWindow(false, function(win) {
|
||||||
|
performSearch(win, false, function() {
|
||||||
|
testOnWindow(true, function(win) {
|
||||||
|
performSearch(win, true, function() {
|
||||||
|
testOnWindow(false, function(win) {
|
||||||
|
checkSearchPopup(win, finish);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkSearchPopup(aWin, aCallback) {
|
||||||
|
let searchBar = aWin.BrowserSearch.searchBar;
|
||||||
|
searchBar.value = "p";
|
||||||
|
searchBar.focus();
|
||||||
|
|
||||||
|
let popup = searchBar.textbox.popup;
|
||||||
|
popup.addEventListener("popupshowing", function showing() {
|
||||||
|
popup.removeEventListener("popupshowing", showing, false);
|
||||||
|
|
||||||
|
let entries = getMenuEntries(searchBar);
|
||||||
|
for (let i = 0; i < entries.length; i++) {
|
||||||
|
isnot(entries[0], "private test",
|
||||||
|
"shouldn't see private autocomplete entries");
|
||||||
|
}
|
||||||
|
|
||||||
|
searchBar.textbox.toggleHistoryPopup();
|
||||||
|
searchBar.value = "";
|
||||||
|
aCallback();
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
searchBar.textbox.showHistoryPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMenuEntries(searchBar) {
|
||||||
|
let entries = [];
|
||||||
|
let autocompleteMenu = searchBar.textbox.popup;
|
||||||
|
// Could perhaps pull values directly from the controller, but it seems
|
||||||
|
// more reliable to test the values that are actually in the tree?
|
||||||
|
let column = autocompleteMenu.tree.columns[0];
|
||||||
|
let numRows = autocompleteMenu.tree.view.rowCount;
|
||||||
|
for (let i = 0; i < numRows; i++) {
|
||||||
|
entries.push(autocompleteMenu.tree.view.getValueAt(i, column));
|
||||||
|
}
|
||||||
|
return entries;
|
||||||
|
}
|
@ -194,7 +194,7 @@ void OggCodecState::ReleasePacket(ogg_packet* aPacket) {
|
|||||||
delete aPacket;
|
delete aPacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PacketQueue::Append(ogg_packet* aPacket) {
|
void OggPacketQueue::Append(ogg_packet* aPacket) {
|
||||||
nsDeque::Push(aPacket);
|
nsDeque::Push(aPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
// Deallocates a packet, used in PacketQueue below.
|
// Deallocates a packet, used in OggPacketQueue below.
|
||||||
class OggPacketDeallocator : public nsDequeFunctor {
|
class OggPacketDeallocator : public nsDequeFunctor {
|
||||||
virtual void* operator() (void* aPacket) {
|
virtual void* operator() (void* aPacket) {
|
||||||
ogg_packet* p = static_cast<ogg_packet*>(aPacket);
|
ogg_packet* p = static_cast<ogg_packet*>(aPacket);
|
||||||
@ -58,10 +58,10 @@ class OggPacketDeallocator : public nsDequeFunctor {
|
|||||||
// frames/samples, reducing the amount of frames/samples we must decode to
|
// frames/samples, reducing the amount of frames/samples we must decode to
|
||||||
// determine start-time at a particular offset, and gives us finer control
|
// determine start-time at a particular offset, and gives us finer control
|
||||||
// over memory usage.
|
// over memory usage.
|
||||||
class PacketQueue : private nsDeque {
|
class OggPacketQueue : private nsDeque {
|
||||||
public:
|
public:
|
||||||
PacketQueue() : nsDeque(new OggPacketDeallocator()) {}
|
OggPacketQueue() : nsDeque(new OggPacketDeallocator()) {}
|
||||||
~PacketQueue() { Erase(); }
|
~OggPacketQueue() { Erase(); }
|
||||||
bool IsEmpty() { return nsDeque::GetSize() == 0; }
|
bool IsEmpty() { return nsDeque::GetSize() == 0; }
|
||||||
void Append(ogg_packet* aPacket);
|
void Append(ogg_packet* aPacket);
|
||||||
ogg_packet* PopFront() { return static_cast<ogg_packet*>(nsDeque::PopFront()); }
|
ogg_packet* PopFront() { return static_cast<ogg_packet*>(nsDeque::PopFront()); }
|
||||||
@ -167,7 +167,7 @@ public:
|
|||||||
|
|
||||||
// Queue of as yet undecoded packets. Packets are guaranteed to have
|
// Queue of as yet undecoded packets. Packets are guaranteed to have
|
||||||
// a valid granulepos.
|
// a valid granulepos.
|
||||||
PacketQueue mPackets;
|
OggPacketQueue mPackets;
|
||||||
|
|
||||||
// Is the bitstream active; whether we're decoding and playing this bitstream.
|
// Is the bitstream active; whether we're decoding and playing this bitstream.
|
||||||
bool mActive;
|
bool mActive;
|
||||||
|
@ -571,6 +571,11 @@ bool OmxDecoder::ReadAudio(AudioFrame *aFrame, int64_t aSeekTimeUs)
|
|||||||
return ReadAudio(aFrame, aSeekTimeUs);
|
return ReadAudio(aFrame, aSeekTimeUs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (err == ERROR_END_OF_STREAM) {
|
||||||
|
if (aFrame->mSize == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -526,11 +526,11 @@ nsReturnRef<NesteggPacketHolder> WebMReader::NextPacket(TrackType aTrackType)
|
|||||||
{
|
{
|
||||||
// The packet queue that packets will be pushed on if they
|
// The packet queue that packets will be pushed on if they
|
||||||
// are not the type we are interested in.
|
// are not the type we are interested in.
|
||||||
PacketQueue& otherPackets =
|
WebMPacketQueue& otherPackets =
|
||||||
aTrackType == VIDEO ? mAudioPackets : mVideoPackets;
|
aTrackType == VIDEO ? mAudioPackets : mVideoPackets;
|
||||||
|
|
||||||
// The packet queue for the type that we are interested in.
|
// The packet queue for the type that we are interested in.
|
||||||
PacketQueue &packets =
|
WebMPacketQueue &packets =
|
||||||
aTrackType == VIDEO ? mVideoPackets : mAudioPackets;
|
aTrackType == VIDEO ? mVideoPackets : mAudioPackets;
|
||||||
|
|
||||||
// Flag to indicate that we do need to playback these types of
|
// Flag to indicate that we do need to playback these types of
|
||||||
|
@ -62,13 +62,13 @@ class PacketQueueDeallocator : public nsDequeFunctor {
|
|||||||
// Typesafe queue for holding nestegg packets. It has
|
// Typesafe queue for holding nestegg packets. It has
|
||||||
// ownership of the items in the queue and will free them
|
// ownership of the items in the queue and will free them
|
||||||
// when destroyed.
|
// when destroyed.
|
||||||
class PacketQueue : private nsDeque {
|
class WebMPacketQueue : private nsDeque {
|
||||||
public:
|
public:
|
||||||
PacketQueue()
|
WebMPacketQueue()
|
||||||
: nsDeque(new PacketQueueDeallocator())
|
: nsDeque(new PacketQueueDeallocator())
|
||||||
{}
|
{}
|
||||||
|
|
||||||
~PacketQueue() {
|
~WebMPacketQueue() {
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,12 +77,12 @@ class PacketQueue : private nsDeque {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void Push(NesteggPacketHolder* aItem) {
|
inline void Push(NesteggPacketHolder* aItem) {
|
||||||
NS_ASSERTION(aItem, "NULL pushed to PacketQueue");
|
NS_ASSERTION(aItem, "NULL pushed to WebMPacketQueue");
|
||||||
nsDeque::Push(aItem);
|
nsDeque::Push(aItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void PushFront(NesteggPacketHolder* aItem) {
|
inline void PushFront(NesteggPacketHolder* aItem) {
|
||||||
NS_ASSERTION(aItem, "NULL pushed to PacketQueue");
|
NS_ASSERTION(aItem, "NULL pushed to WebMPacketQueue");
|
||||||
nsDeque::PushFront(aItem);
|
nsDeque::PushFront(aItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,8 +199,8 @@ private:
|
|||||||
|
|
||||||
// Queue of video and audio packets that have been read but not decoded. These
|
// Queue of video and audio packets that have been read but not decoded. These
|
||||||
// must only be accessed from the state machine thread.
|
// must only be accessed from the state machine thread.
|
||||||
PacketQueue mVideoPackets;
|
WebMPacketQueue mVideoPackets;
|
||||||
PacketQueue mAudioPackets;
|
WebMPacketQueue mAudioPackets;
|
||||||
|
|
||||||
// Index of video and audio track to play
|
// Index of video and audio track to play
|
||||||
uint32_t mVideoTrack;
|
uint32_t mVideoTrack;
|
||||||
|
@ -1435,6 +1435,70 @@ CodeGenerator::maybeCreateScriptCounts()
|
|||||||
return counts;
|
return counts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Structure for managing the state tracked for a block by script counters.
|
||||||
|
struct ScriptCountBlockState
|
||||||
|
{
|
||||||
|
IonBlockCounts █
|
||||||
|
MacroAssembler &masm;
|
||||||
|
|
||||||
|
Sprinter printer;
|
||||||
|
|
||||||
|
uint32 instructionBytes;
|
||||||
|
uint32 spillBytes;
|
||||||
|
|
||||||
|
// Pointer to instructionBytes, spillBytes, or NULL, depending on the last
|
||||||
|
// instruction processed.
|
||||||
|
uint32 *last;
|
||||||
|
uint32 lastLength;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ScriptCountBlockState(IonBlockCounts *block, MacroAssembler *masm)
|
||||||
|
: block(*block), masm(*masm),
|
||||||
|
printer(GetIonContext()->cx),
|
||||||
|
instructionBytes(0), spillBytes(0), last(NULL), lastLength(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool init()
|
||||||
|
{
|
||||||
|
if (!printer.init())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Bump the hit count for the block at the start. This code is not
|
||||||
|
// included in either the text for the block or the instruction byte
|
||||||
|
// counts.
|
||||||
|
masm.inc64(AbsoluteAddress(block.addressOfHitCount()));
|
||||||
|
|
||||||
|
// Collect human readable assembly for the code generated in the block.
|
||||||
|
masm.setPrinter(&printer);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void visitInstruction(LInstruction *ins)
|
||||||
|
{
|
||||||
|
if (last)
|
||||||
|
*last += masm.size() - lastLength;
|
||||||
|
lastLength = masm.size();
|
||||||
|
last = ins->isMoveGroup() ? &spillBytes : &instructionBytes;
|
||||||
|
|
||||||
|
// Prefix stream of assembly instructions with their LIR instruction name.
|
||||||
|
printer.printf("[%s]\n", ins->opName());
|
||||||
|
}
|
||||||
|
|
||||||
|
~ScriptCountBlockState()
|
||||||
|
{
|
||||||
|
masm.setPrinter(NULL);
|
||||||
|
|
||||||
|
if (last)
|
||||||
|
*last += masm.size() - lastLength;
|
||||||
|
|
||||||
|
block.setCode(printer.string());
|
||||||
|
block.setInstructionBytes(instructionBytes);
|
||||||
|
block.setSpillBytes(spillBytes);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CodeGenerator::generateBody()
|
CodeGenerator::generateBody()
|
||||||
{
|
{
|
||||||
@ -1451,19 +1515,18 @@ CodeGenerator::generateBody()
|
|||||||
return false;
|
return false;
|
||||||
iter++;
|
iter++;
|
||||||
|
|
||||||
mozilla::Maybe<Sprinter> printer;
|
mozilla::Maybe<ScriptCountBlockState> blockCounts;
|
||||||
if (counts) {
|
if (counts) {
|
||||||
masm.inc64(AbsoluteAddress(counts->block(i).addressOfHitCount()));
|
blockCounts.construct(&counts->block(i), &masm);
|
||||||
printer.construct(GetIonContext()->cx);
|
if (!blockCounts.ref().init())
|
||||||
if (!printer.ref().init())
|
|
||||||
return false;
|
return false;
|
||||||
masm.setPrinter(printer.addr());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; iter != current->end(); iter++) {
|
for (; iter != current->end(); iter++) {
|
||||||
IonSpew(IonSpew_Codegen, "instruction %s", iter->opName());
|
IonSpew(IonSpew_Codegen, "instruction %s", iter->opName());
|
||||||
|
|
||||||
if (counts)
|
if (counts)
|
||||||
printer.ref().printf("[%s]\n", iter->opName());
|
blockCounts.ref().visitInstruction(*iter);
|
||||||
|
|
||||||
if (iter->safepoint() && pushedArgumentSlots_.length()) {
|
if (iter->safepoint() && pushedArgumentSlots_.length()) {
|
||||||
if (!markArgumentSlots(iter->safepoint()))
|
if (!markArgumentSlots(iter->safepoint()))
|
||||||
@ -1475,11 +1538,6 @@ CodeGenerator::generateBody()
|
|||||||
}
|
}
|
||||||
if (masm.oom())
|
if (masm.oom())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (counts) {
|
|
||||||
counts->block(i).setCode(printer.ref().string());
|
|
||||||
masm.setPrinter(NULL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_ASSERT(pushedArgumentSlots_.empty());
|
JS_ASSERT(pushedArgumentSlots_.empty());
|
||||||
|
@ -868,6 +868,17 @@ CompileBackEnd(MIRGenerator *mir)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (js_IonOptions.licm) {
|
||||||
|
LICM licm(mir, graph);
|
||||||
|
if (!licm.analyze())
|
||||||
|
return NULL;
|
||||||
|
IonSpewPass("LICM");
|
||||||
|
AssertGraphCoherency(graph);
|
||||||
|
|
||||||
|
if (mir->shouldCancel("LICM"))
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (js_IonOptions.rangeAnalysis) {
|
if (js_IonOptions.rangeAnalysis) {
|
||||||
RangeAnalysis r(graph);
|
RangeAnalysis r(graph);
|
||||||
if (!r.addBetaNobes())
|
if (!r.addBetaNobes())
|
||||||
@ -903,17 +914,6 @@ CompileBackEnd(MIRGenerator *mir)
|
|||||||
if (mir->shouldCancel("DCE"))
|
if (mir->shouldCancel("DCE"))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (js_IonOptions.licm) {
|
|
||||||
LICM licm(mir, graph);
|
|
||||||
if (!licm.analyze())
|
|
||||||
return NULL;
|
|
||||||
IonSpewPass("LICM");
|
|
||||||
AssertGraphCoherency(graph);
|
|
||||||
|
|
||||||
if (mir->shouldCancel("LICM"))
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (js_IonOptions.edgeCaseAnalysis) {
|
if (js_IonOptions.edgeCaseAnalysis) {
|
||||||
EdgeCaseAnalysis edgeCaseAnalysis(mir, graph);
|
EdgeCaseAnalysis edgeCaseAnalysis(mir, graph);
|
||||||
if (!edgeCaseAnalysis.analyzeLate())
|
if (!edgeCaseAnalysis.analyzeLate())
|
||||||
|
@ -812,7 +812,7 @@ typedef HashMap<uint32,
|
|||||||
static HashNumber
|
static HashNumber
|
||||||
BoundsCheckHashIgnoreOffset(MBoundsCheck *check)
|
BoundsCheckHashIgnoreOffset(MBoundsCheck *check)
|
||||||
{
|
{
|
||||||
LinearSum indexSum = ExtractLinearSum(check->index());
|
SimpleLinearSum indexSum = ExtractLinearSum(check->index());
|
||||||
uintptr_t index = indexSum.term ? uintptr_t(indexSum.term) : 0;
|
uintptr_t index = indexSum.term ? uintptr_t(indexSum.term) : 0;
|
||||||
uintptr_t length = uintptr_t(check->length());
|
uintptr_t length = uintptr_t(check->length());
|
||||||
return index ^ length;
|
return index ^ length;
|
||||||
@ -840,43 +840,105 @@ FindDominatingBoundsCheck(BoundsCheckMap &checks, MBoundsCheck *check, size_t in
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract a linear sum from ins, if possible (otherwise giving the sum 'ins + 0').
|
// Extract a linear sum from ins, if possible (otherwise giving the sum 'ins + 0').
|
||||||
LinearSum
|
SimpleLinearSum
|
||||||
ion::ExtractLinearSum(MDefinition *ins)
|
ion::ExtractLinearSum(MDefinition *ins)
|
||||||
{
|
{
|
||||||
|
if (ins->isBeta())
|
||||||
|
ins = ins->getOperand(0);
|
||||||
|
|
||||||
if (ins->type() != MIRType_Int32)
|
if (ins->type() != MIRType_Int32)
|
||||||
return LinearSum(ins, 0);
|
return SimpleLinearSum(ins, 0);
|
||||||
|
|
||||||
if (ins->isConstant()) {
|
if (ins->isConstant()) {
|
||||||
const Value &v = ins->toConstant()->value();
|
const Value &v = ins->toConstant()->value();
|
||||||
JS_ASSERT(v.isInt32());
|
JS_ASSERT(v.isInt32());
|
||||||
return LinearSum(NULL, v.toInt32());
|
return SimpleLinearSum(NULL, v.toInt32());
|
||||||
} else if (ins->isAdd() || ins->isSub()) {
|
} else if (ins->isAdd() || ins->isSub()) {
|
||||||
MDefinition *lhs = ins->getOperand(0);
|
MDefinition *lhs = ins->getOperand(0);
|
||||||
MDefinition *rhs = ins->getOperand(1);
|
MDefinition *rhs = ins->getOperand(1);
|
||||||
if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) {
|
if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) {
|
||||||
LinearSum lsum = ExtractLinearSum(lhs);
|
SimpleLinearSum lsum = ExtractLinearSum(lhs);
|
||||||
LinearSum rsum = ExtractLinearSum(rhs);
|
SimpleLinearSum rsum = ExtractLinearSum(rhs);
|
||||||
|
|
||||||
JS_ASSERT(lsum.term || rsum.term);
|
|
||||||
if (lsum.term && rsum.term)
|
if (lsum.term && rsum.term)
|
||||||
return LinearSum(ins, 0);
|
return SimpleLinearSum(ins, 0);
|
||||||
|
|
||||||
// Check if this is of the form <SUM> + n, n + <SUM> or <SUM> - n.
|
// Check if this is of the form <SUM> + n, n + <SUM> or <SUM> - n.
|
||||||
if (ins->isAdd()) {
|
if (ins->isAdd()) {
|
||||||
int32 constant;
|
int32 constant;
|
||||||
if (!SafeAdd(lsum.constant, rsum.constant, &constant))
|
if (!SafeAdd(lsum.constant, rsum.constant, &constant))
|
||||||
return LinearSum(ins, 0);
|
return SimpleLinearSum(ins, 0);
|
||||||
return LinearSum(lsum.term ? lsum.term : rsum.term, constant);
|
return SimpleLinearSum(lsum.term ? lsum.term : rsum.term, constant);
|
||||||
} else if (lsum.term) {
|
} else if (lsum.term) {
|
||||||
int32 constant;
|
int32 constant;
|
||||||
if (!SafeSub(lsum.constant, rsum.constant, &constant))
|
if (!SafeSub(lsum.constant, rsum.constant, &constant))
|
||||||
return LinearSum(ins, 0);
|
return SimpleLinearSum(ins, 0);
|
||||||
return LinearSum(lsum.term, constant);
|
return SimpleLinearSum(lsum.term, constant);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return LinearSum(ins, 0);
|
return SimpleLinearSum(ins, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract a linear inequality holding when a boolean test goes in the
|
||||||
|
// specified direction, of the form 'lhs + lhsN <= rhs' (or >=).
|
||||||
|
bool
|
||||||
|
ion::ExtractLinearInequality(MTest *test, BranchDirection direction,
|
||||||
|
SimpleLinearSum *plhs, MDefinition **prhs, bool *plessEqual)
|
||||||
|
{
|
||||||
|
if (!test->getOperand(0)->isCompare())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
MCompare *compare = test->getOperand(0)->toCompare();
|
||||||
|
|
||||||
|
MDefinition *lhs = compare->getOperand(0);
|
||||||
|
MDefinition *rhs = compare->getOperand(1);
|
||||||
|
|
||||||
|
if (compare->specialization() != MIRType_Int32)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
JS_ASSERT(lhs->type() == MIRType_Int32);
|
||||||
|
JS_ASSERT(rhs->type() == MIRType_Int32);
|
||||||
|
|
||||||
|
JSOp jsop = compare->jsop();
|
||||||
|
if (direction == FALSE_BRANCH)
|
||||||
|
jsop = analyze::NegateCompareOp(jsop);
|
||||||
|
|
||||||
|
SimpleLinearSum lsum = ExtractLinearSum(lhs);
|
||||||
|
SimpleLinearSum rsum = ExtractLinearSum(rhs);
|
||||||
|
|
||||||
|
if (!SafeSub(lsum.constant, rsum.constant, &lsum.constant))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Normalize operations to use <= or >=.
|
||||||
|
switch (jsop) {
|
||||||
|
case JSOP_LE:
|
||||||
|
*plessEqual = true;
|
||||||
|
break;
|
||||||
|
case JSOP_LT:
|
||||||
|
/* x < y ==> x + 1 <= y */
|
||||||
|
if (!SafeAdd(lsum.constant, 1, &lsum.constant))
|
||||||
|
return false;
|
||||||
|
*plessEqual = true;
|
||||||
|
break;
|
||||||
|
case JSOP_GE:
|
||||||
|
*plessEqual = false;
|
||||||
|
break;
|
||||||
|
case JSOP_GT:
|
||||||
|
/* x > y ==> x - 1 >= y */
|
||||||
|
if (!SafeSub(lsum.constant, 1, &lsum.constant))
|
||||||
|
return false;
|
||||||
|
*plessEqual = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*plhs = lsum;
|
||||||
|
*prhs = rsum.term;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@ -889,8 +951,8 @@ TryEliminateBoundsCheck(MBoundsCheck *dominating, MBoundsCheck *dominated, bool
|
|||||||
if (dominating->length() != dominated->length())
|
if (dominating->length() != dominated->length())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
LinearSum sumA = ExtractLinearSum(dominating->index());
|
SimpleLinearSum sumA = ExtractLinearSum(dominating->index());
|
||||||
LinearSum sumB = ExtractLinearSum(dominated->index());
|
SimpleLinearSum sumB = ExtractLinearSum(dominated->index());
|
||||||
|
|
||||||
// Both terms should be NULL or the same definition.
|
// Both terms should be NULL or the same definition.
|
||||||
if (sumA.term != sumB.term)
|
if (sumA.term != sumB.term)
|
||||||
@ -1010,3 +1072,86 @@ ion::EliminateRedundantBoundsChecks(MIRGraph &graph)
|
|||||||
JS_ASSERT(index == graph.numBlocks());
|
JS_ASSERT(index == graph.numBlocks());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
LinearSum::multiply(int32 scale)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < terms_.length(); i++) {
|
||||||
|
if (!SafeMul(scale, terms_[i].scale, &terms_[i].scale))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return SafeMul(scale, constant_, &constant_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
LinearSum::add(const LinearSum &other)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < other.terms_.length(); i++) {
|
||||||
|
if (!add(other.terms_[i].term, other.terms_[i].scale))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return add(other.constant_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
LinearSum::add(MDefinition *term, int32 scale)
|
||||||
|
{
|
||||||
|
JS_ASSERT(term);
|
||||||
|
|
||||||
|
if (scale == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (term->isConstant()) {
|
||||||
|
int32 constant = term->toConstant()->value().toInt32();
|
||||||
|
if (!SafeMul(constant, scale, &constant))
|
||||||
|
return false;
|
||||||
|
return add(constant);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < terms_.length(); i++) {
|
||||||
|
if (term == terms_[i].term) {
|
||||||
|
if (!SafeAdd(scale, terms_[i].scale, &terms_[i].scale))
|
||||||
|
return false;
|
||||||
|
if (terms_[i].scale == 0) {
|
||||||
|
terms_[i] = terms_.back();
|
||||||
|
terms_.popBack();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
terms_.append(LinearTerm(term, scale));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
LinearSum::add(int32 constant)
|
||||||
|
{
|
||||||
|
return SafeAdd(constant, constant_, &constant_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LinearSum::print(Sprinter &sp) const
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < terms_.length(); i++) {
|
||||||
|
int32 scale = terms_[i].scale;
|
||||||
|
int32 id = terms_[i].term->id();
|
||||||
|
JS_ASSERT(scale);
|
||||||
|
if (scale > 0) {
|
||||||
|
if (i)
|
||||||
|
sp.printf("+");
|
||||||
|
if (scale == 1)
|
||||||
|
sp.printf("#%d", id);
|
||||||
|
else
|
||||||
|
sp.printf("%d*#%d", scale, id);
|
||||||
|
} else if (scale == -1) {
|
||||||
|
sp.printf("-#%d", id);
|
||||||
|
} else {
|
||||||
|
sp.printf("%d*#%d", scale, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (constant_ > 0)
|
||||||
|
sp.printf("+%d", constant_);
|
||||||
|
else if (constant_ < 0)
|
||||||
|
sp.printf("%d", constant_);
|
||||||
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
// This file declares various analysis passes that operate on MIR.
|
// This file declares various analysis passes that operate on MIR.
|
||||||
|
|
||||||
#include "IonAllocPolicy.h"
|
#include "IonAllocPolicy.h"
|
||||||
|
#include "MIR.h"
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
namespace ion {
|
namespace ion {
|
||||||
@ -45,23 +46,69 @@ AssertGraphCoherency(MIRGraph &graph);
|
|||||||
bool
|
bool
|
||||||
EliminateRedundantBoundsChecks(MIRGraph &graph);
|
EliminateRedundantBoundsChecks(MIRGraph &graph);
|
||||||
|
|
||||||
// Linear sum of term(s). For now the only linear sums which can be represented
|
|
||||||
// are 'n' or 'x + n' (for any computation x).
|
|
||||||
class MDefinition;
|
class MDefinition;
|
||||||
|
|
||||||
struct LinearSum
|
// Simple linear sum of the form 'n' or 'x + n'.
|
||||||
|
struct SimpleLinearSum
|
||||||
{
|
{
|
||||||
MDefinition *term;
|
MDefinition *term;
|
||||||
int32 constant;
|
int32 constant;
|
||||||
|
|
||||||
LinearSum(MDefinition *term, int32 constant)
|
SimpleLinearSum(MDefinition *term, int32 constant)
|
||||||
: term(term), constant(constant)
|
: term(term), constant(constant)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
LinearSum
|
SimpleLinearSum
|
||||||
ExtractLinearSum(MDefinition *ins);
|
ExtractLinearSum(MDefinition *ins);
|
||||||
|
|
||||||
|
bool
|
||||||
|
ExtractLinearInequality(MTest *test, BranchDirection direction,
|
||||||
|
SimpleLinearSum *plhs, MDefinition **prhs, bool *plessEqual);
|
||||||
|
|
||||||
|
struct LinearTerm
|
||||||
|
{
|
||||||
|
MDefinition *term;
|
||||||
|
int32 scale;
|
||||||
|
|
||||||
|
LinearTerm(MDefinition *term, int32 scale)
|
||||||
|
: term(term), scale(scale)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// General linear sum of the form 'x1*n1 + x2*n2 + ... + n'
|
||||||
|
class LinearSum
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LinearSum()
|
||||||
|
: constant_(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LinearSum(const LinearSum &other)
|
||||||
|
: constant_(other.constant_)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < other.terms_.length(); i++)
|
||||||
|
terms_.append(other.terms_[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool multiply(int32 scale);
|
||||||
|
bool add(const LinearSum &other);
|
||||||
|
bool add(MDefinition *term, int32 scale);
|
||||||
|
bool add(int32 constant);
|
||||||
|
|
||||||
|
int32 constant() const { return constant_; }
|
||||||
|
size_t numTerms() const { return terms_.length(); }
|
||||||
|
LinearTerm term(size_t i) const { return terms_[i]; }
|
||||||
|
|
||||||
|
void print(Sprinter &sp) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vector<LinearTerm, 2, IonAllocPolicy> terms_;
|
||||||
|
int32 constant_;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace ion
|
} // namespace ion
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
|
@ -420,9 +420,14 @@ struct IonBlockCounts
|
|||||||
// Hit count for this block.
|
// Hit count for this block.
|
||||||
uint64 hitCount_;
|
uint64 hitCount_;
|
||||||
|
|
||||||
// Information about the code generated for this block.
|
// Text information about the code generated for this block.
|
||||||
char *code_;
|
char *code_;
|
||||||
|
|
||||||
|
// Number of bytes of code generated in this block. Spill code is counted
|
||||||
|
// separately from other, instruction implementing code.
|
||||||
|
uint32 instructionBytes_;
|
||||||
|
uint32 spillBytes_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
bool init(uint32 id, uint32 offset, uint32 numSuccessors) {
|
bool init(uint32 id, uint32 offset, uint32 numSuccessors) {
|
||||||
@ -485,6 +490,22 @@ struct IonBlockCounts
|
|||||||
const char *code() const {
|
const char *code() const {
|
||||||
return code_;
|
return code_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setInstructionBytes(uint32 bytes) {
|
||||||
|
instructionBytes_ = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 instructionBytes() const {
|
||||||
|
return instructionBytes_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSpillBytes(uint32 bytes) {
|
||||||
|
spillBytes_ = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 spillBytes() const {
|
||||||
|
return spillBytes_;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Execution information for a compiled script which may persist after the
|
// Execution information for a compiled script which may persist after the
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "MIR.h"
|
#include "MIR.h"
|
||||||
#include "MIRGraph.h"
|
#include "MIRGraph.h"
|
||||||
#include "LinearScan.h"
|
#include "LinearScan.h"
|
||||||
|
#include "RangeAnalysis.h"
|
||||||
using namespace js;
|
using namespace js;
|
||||||
using namespace js::ion;
|
using namespace js::ion;
|
||||||
|
|
||||||
@ -255,8 +256,14 @@ JSONSpewer::spewMDef(MDefinition *def)
|
|||||||
integerValue(use.def()->id());
|
integerValue(use.def()->id());
|
||||||
endList();
|
endList();
|
||||||
|
|
||||||
stringProperty("type", "%s : [%d, %d]", StringFromMIRType(def->type()),
|
if (def->range()) {
|
||||||
def->range()->lower(), def->range()->upper());
|
Sprinter sp(GetIonContext()->cx);
|
||||||
|
sp.init();
|
||||||
|
def->range()->print(sp);
|
||||||
|
stringProperty("type", "%s : %s", sp.string());
|
||||||
|
} else {
|
||||||
|
stringProperty("type", "%s", StringFromMIRType(def->type()));
|
||||||
|
}
|
||||||
|
|
||||||
if (def->isInstruction()) {
|
if (def->isInstruction()) {
|
||||||
if (MResumePoint *rp = def->toInstruction()->resumePoint())
|
if (MResumePoint *rp = def->toInstruction()->resumePoint())
|
||||||
|
@ -17,64 +17,6 @@
|
|||||||
using namespace js;
|
using namespace js;
|
||||||
using namespace js::ion;
|
using namespace js::ion;
|
||||||
|
|
||||||
bool
|
|
||||||
ion::ExtractLinearInequality(MTest *test, BranchDirection direction,
|
|
||||||
LinearSum *plhs, MDefinition **prhs, bool *plessEqual)
|
|
||||||
{
|
|
||||||
if (!test->getOperand(0)->isCompare())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
MCompare *compare = test->getOperand(0)->toCompare();
|
|
||||||
|
|
||||||
MDefinition *lhs = compare->getOperand(0);
|
|
||||||
MDefinition *rhs = compare->getOperand(1);
|
|
||||||
|
|
||||||
if (compare->specialization() != MIRType_Int32)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
JS_ASSERT(lhs->type() == MIRType_Int32);
|
|
||||||
JS_ASSERT(rhs->type() == MIRType_Int32);
|
|
||||||
|
|
||||||
JSOp jsop = compare->jsop();
|
|
||||||
if (direction == FALSE_BRANCH)
|
|
||||||
jsop = analyze::NegateCompareOp(jsop);
|
|
||||||
|
|
||||||
LinearSum lsum = ExtractLinearSum(lhs);
|
|
||||||
LinearSum rsum = ExtractLinearSum(rhs);
|
|
||||||
|
|
||||||
if (!SafeSub(lsum.constant, rsum.constant, &lsum.constant))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Normalize operations to use <= or >=.
|
|
||||||
switch (jsop) {
|
|
||||||
case JSOP_LE:
|
|
||||||
*plessEqual = true;
|
|
||||||
break;
|
|
||||||
case JSOP_LT:
|
|
||||||
/* x < y ==> x + 1 <= y */
|
|
||||||
if (!SafeAdd(lsum.constant, 1, &lsum.constant))
|
|
||||||
return false;
|
|
||||||
*plessEqual = true;
|
|
||||||
break;
|
|
||||||
case JSOP_GE:
|
|
||||||
*plessEqual = false;
|
|
||||||
break;
|
|
||||||
case JSOP_GT:
|
|
||||||
/* x > y ==> x - 1 >= y */
|
|
||||||
if (!SafeSub(lsum.constant, 1, &lsum.constant))
|
|
||||||
return false;
|
|
||||||
*plessEqual = false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*plhs = lsum;
|
|
||||||
*prhs = rsum.term;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
LICM::LICM(MIRGenerator *mir, MIRGraph &graph)
|
LICM::LICM(MIRGenerator *mir, MIRGraph &graph)
|
||||||
: mir(mir), graph(graph)
|
: mir(mir), graph(graph)
|
||||||
{
|
{
|
||||||
@ -178,7 +120,6 @@ bool
|
|||||||
Loop::optimize()
|
Loop::optimize()
|
||||||
{
|
{
|
||||||
InstructionQueue invariantInstructions;
|
InstructionQueue invariantInstructions;
|
||||||
InstructionQueue boundsChecks;
|
|
||||||
|
|
||||||
IonSpew(IonSpew_LICM, "These instructions are in the loop: ");
|
IonSpew(IonSpew_LICM, "These instructions are in the loop: ");
|
||||||
|
|
||||||
@ -220,62 +161,17 @@ Loop::optimize()
|
|||||||
|
|
||||||
if (IonSpewEnabled(IonSpew_LICM))
|
if (IonSpewEnabled(IonSpew_LICM))
|
||||||
fprintf(IonSpewFile, " Loop Invariant!\n");
|
fprintf(IonSpewFile, " Loop Invariant!\n");
|
||||||
} else if (ins->isBoundsCheck()) {
|
|
||||||
if (!boundsChecks.append(ins))
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hoistInstructions(invariantInstructions, boundsChecks))
|
if (!hoistInstructions(invariantInstructions))
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Loop::hoistInstructions(InstructionQueue &toHoist, InstructionQueue &boundsChecks)
|
Loop::hoistInstructions(InstructionQueue &toHoist)
|
||||||
{
|
{
|
||||||
// Hoist bounds checks first, so that hoistBoundsCheck can test for
|
|
||||||
// invariant instructions, but delay actual insertion until the end to
|
|
||||||
// handle dependencies on loop invariant instructions.
|
|
||||||
InstructionQueue hoistedChecks;
|
|
||||||
for (size_t i = 0; i < boundsChecks.length(); i++) {
|
|
||||||
MBoundsCheck *ins = boundsChecks[i]->toBoundsCheck();
|
|
||||||
if (isLoopInvariant(ins) || !isInLoop(ins))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Try to find a test dominating the bounds check which can be
|
|
||||||
// transformed into a hoistable check. Stop after the first such check
|
|
||||||
// which could be transformed (the one which will be the closest to the
|
|
||||||
// access in the source).
|
|
||||||
MBasicBlock *block = ins->block();
|
|
||||||
while (true) {
|
|
||||||
BranchDirection direction;
|
|
||||||
MTest *branch = block->immediateDominatorBranch(&direction);
|
|
||||||
if (branch) {
|
|
||||||
MInstruction *upper, *lower;
|
|
||||||
tryHoistBoundsCheck(ins, branch, direction, &upper, &lower);
|
|
||||||
if (upper && !hoistedChecks.append(upper))
|
|
||||||
return false;
|
|
||||||
if (lower && !hoistedChecks.append(lower))
|
|
||||||
return false;
|
|
||||||
if (upper || lower) {
|
|
||||||
// Note: replace all uses of the original bounds check with the
|
|
||||||
// actual index. This is usually done during bounds check elimination,
|
|
||||||
// but in this case it's safe to do it here since the load/store is
|
|
||||||
// definitely not loop-invariant, so we will never move it before
|
|
||||||
// one of the bounds checks we just added.
|
|
||||||
ins->replaceAllUsesWith(ins->index());
|
|
||||||
ins->block()->discard(ins);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MBasicBlock *dom = block->immediateDominator();
|
|
||||||
if (dom == block)
|
|
||||||
break;
|
|
||||||
block = dom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move all instructions to the preLoop_ block just before the control instruction.
|
// Move all instructions to the preLoop_ block just before the control instruction.
|
||||||
for (size_t i = 0; i < toHoist.length(); i++) {
|
for (size_t i = 0; i < toHoist.length(); i++) {
|
||||||
MInstruction *ins = toHoist[i];
|
MInstruction *ins = toHoist[i];
|
||||||
@ -292,11 +188,6 @@ Loop::hoistInstructions(InstructionQueue &toHoist, InstructionQueue &boundsCheck
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < hoistedChecks.length(); i++) {
|
|
||||||
MInstruction *ins = hoistedChecks[i];
|
|
||||||
preLoop_->insertBefore(preLoop_->lastIns(), ins);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,186 +261,3 @@ Loop::popFromWorklist()
|
|||||||
toReturn->setNotInWorklist();
|
toReturn->setNotInWorklist();
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to compute hoistable checks for the upper and lower bound on ins,
|
|
||||||
// according to a test in the loop which dominates ins.
|
|
||||||
//
|
|
||||||
// Given a bounds check within a loop which is not loop invariant, we would
|
|
||||||
// like to compute loop invariant bounds checks which imply that the inner
|
|
||||||
// check will succeed. These invariant checks can then be added to the
|
|
||||||
// preheader, and the inner check eliminated.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
//
|
|
||||||
// for (i = v; i < n; i++)
|
|
||||||
// x[i] = 0;
|
|
||||||
//
|
|
||||||
// There are two constraints captured by the bounds check here: i >= 0, and
|
|
||||||
// i < length(x). 'i' is not loop invariant, but we can still hoist these
|
|
||||||
// checks:
|
|
||||||
//
|
|
||||||
// - At the point of the check, it is known that i < n. Given this,
|
|
||||||
// if n <= length(x) then i < length(x), and since n and length(x) are loop
|
|
||||||
// invariant the former condition can be hoisted and the i < length(x) check
|
|
||||||
// removed.
|
|
||||||
//
|
|
||||||
// - i is only incremented within the loop, so if its initial value is >= 0
|
|
||||||
// then all its values within the loop will also be >= 0. The lower bounds
|
|
||||||
// check can be hoisted as v >= 0.
|
|
||||||
//
|
|
||||||
// tryHoistBoundsCheck encodes this logic. Given a bounds check B and a test T
|
|
||||||
// in the loop dominating that bounds check, where B and T share a non-invariant
|
|
||||||
// term lhs, a new check C is computed such that T && C imply B.
|
|
||||||
void
|
|
||||||
Loop::tryHoistBoundsCheck(MBoundsCheck *ins, MTest *test, BranchDirection direction,
|
|
||||||
MInstruction **pupper, MInstruction **plower)
|
|
||||||
{
|
|
||||||
*pupper = NULL;
|
|
||||||
*plower = NULL;
|
|
||||||
|
|
||||||
if (!isLoopInvariant(ins->length()))
|
|
||||||
return;
|
|
||||||
|
|
||||||
LinearSum lhs(NULL, 0);
|
|
||||||
MDefinition *rhs;
|
|
||||||
bool lessEqual;
|
|
||||||
if (!ExtractLinearInequality(test, direction, &lhs, &rhs, &lessEqual))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Ensure the rhs is a loop invariant term.
|
|
||||||
if (rhs && !isLoopInvariant(rhs)) {
|
|
||||||
if (lhs.term && !isLoopInvariant(lhs.term))
|
|
||||||
return;
|
|
||||||
MDefinition *temp = lhs.term;
|
|
||||||
lhs.term = rhs;
|
|
||||||
rhs = temp;
|
|
||||||
if (!SafeSub(0, lhs.constant, &lhs.constant))
|
|
||||||
return;
|
|
||||||
lessEqual = !lessEqual;
|
|
||||||
}
|
|
||||||
|
|
||||||
JS_ASSERT_IF(rhs, isLoopInvariant(rhs));
|
|
||||||
|
|
||||||
// Ensure the lhs is a phi node from the start of the loop body.
|
|
||||||
if (!lhs.term || !lhs.term->isPhi() || lhs.term->block() != header_)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Check if the lhs in the conditional matches the bounds check index.
|
|
||||||
LinearSum index = ExtractLinearSum(ins->index());
|
|
||||||
if (index.term != lhs.term)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!lessEqual)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// At the point of the access, it is known that lhs + lhsN <= rhs, and the
|
|
||||||
// bounds check is that lhs + indexN + maximum < length. To ensure the
|
|
||||||
// bounds check holds then, we need to ensure that:
|
|
||||||
//
|
|
||||||
// rhs - lhsN + indexN + maximum < length
|
|
||||||
|
|
||||||
int32 adjustment;
|
|
||||||
if (!SafeSub(index.constant, lhs.constant, &adjustment))
|
|
||||||
return;
|
|
||||||
if (!SafeAdd(adjustment, ins->maximum(), &adjustment))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// For the lower bound, check that lhs + indexN + minimum >= 0, e.g.
|
|
||||||
//
|
|
||||||
// lhs >= -indexN - minimum
|
|
||||||
//
|
|
||||||
// lhs is not loop invariant, but if this condition holds of the backing
|
|
||||||
// variable at loop entry and the variable's value never decreases in the
|
|
||||||
// loop body, it will hold throughout the loop.
|
|
||||||
|
|
||||||
uint32 position = preLoop_->positionInPhiSuccessor();
|
|
||||||
MDefinition *initialIndex = lhs.term->toPhi()->getOperand(position);
|
|
||||||
if (!nonDecreasing(initialIndex, lhs.term))
|
|
||||||
return;
|
|
||||||
|
|
||||||
int32 lowerBound;
|
|
||||||
if (!SafeSub(0, index.constant, &lowerBound))
|
|
||||||
return;
|
|
||||||
if (!SafeSub(lowerBound, ins->minimum(), &lowerBound))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// XXX limit on how much can be hoisted, to ensure ballast works?
|
|
||||||
|
|
||||||
if (!rhs) {
|
|
||||||
rhs = MConstant::New(Int32Value(adjustment));
|
|
||||||
adjustment = 0;
|
|
||||||
preLoop_->insertBefore(preLoop_->lastIns(), rhs->toInstruction());
|
|
||||||
}
|
|
||||||
|
|
||||||
MBoundsCheck *upper = MBoundsCheck::New(rhs, ins->length());
|
|
||||||
upper->setMinimum(adjustment);
|
|
||||||
upper->setMaximum(adjustment);
|
|
||||||
|
|
||||||
MBoundsCheckLower *lower = MBoundsCheckLower::New(initialIndex);
|
|
||||||
lower->setMinimum(lowerBound);
|
|
||||||
|
|
||||||
*pupper = upper;
|
|
||||||
*plower = lower;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine whether the possible value of start (a phi node within the loop)
|
|
||||||
// can become smaller than an initial value at loop entry.
|
|
||||||
bool
|
|
||||||
Loop::nonDecreasing(MDefinition *initial, MDefinition *start)
|
|
||||||
{
|
|
||||||
MDefinitionVector worklist;
|
|
||||||
MDefinitionVector seen;
|
|
||||||
|
|
||||||
if (!worklist.append(start))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
while (!worklist.empty()) {
|
|
||||||
MDefinition *def = worklist.popCopy();
|
|
||||||
bool duplicate = false;
|
|
||||||
for (size_t i = 0; i < seen.length() && !duplicate; i++) {
|
|
||||||
if (seen[i] == def)
|
|
||||||
duplicate = true;
|
|
||||||
}
|
|
||||||
if (duplicate)
|
|
||||||
continue;
|
|
||||||
if (!seen.append(def))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (def->type() != MIRType_Int32)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!isInLoop(def)) {
|
|
||||||
if (def != initial)
|
|
||||||
return false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (def->isPhi()) {
|
|
||||||
MPhi *phi = def->toPhi();
|
|
||||||
for (size_t i = 0; i < phi->numOperands(); i++) {
|
|
||||||
if (!worklist.append(phi->getOperand(i)))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (def->isAdd()) {
|
|
||||||
if (def->toAdd()->specialization() != MIRType_Int32)
|
|
||||||
return false;
|
|
||||||
MDefinition *lhs = def->toAdd()->getOperand(0);
|
|
||||||
MDefinition *rhs = def->toAdd()->getOperand(1);
|
|
||||||
if (!rhs->isConstant())
|
|
||||||
return false;
|
|
||||||
Value v = rhs->toConstant()->value();
|
|
||||||
if (!v.isInt32() || v.toInt32() < 0)
|
|
||||||
return false;
|
|
||||||
if (!worklist.append(lhs))
|
|
||||||
return false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
@ -33,12 +33,6 @@ class LICM
|
|||||||
bool analyze();
|
bool analyze();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Extract a linear inequality holding when a boolean test goes in the
|
|
||||||
// specified direction, of the form 'lhs + lhsN <= rhs' (or >=).
|
|
||||||
bool
|
|
||||||
ExtractLinearInequality(MTest *test, BranchDirection direction,
|
|
||||||
LinearSum *plhs, MDefinition **prhs, bool *plessEqual);
|
|
||||||
|
|
||||||
class Loop
|
class Loop
|
||||||
{
|
{
|
||||||
MIRGenerator *mir;
|
MIRGenerator *mir;
|
||||||
@ -77,7 +71,7 @@ class Loop
|
|||||||
// Along the way it adds instructions to the worklist for invariance testing.
|
// Along the way it adds instructions to the worklist for invariance testing.
|
||||||
LoopReturn iterateLoopBlocks(MBasicBlock *current);
|
LoopReturn iterateLoopBlocks(MBasicBlock *current);
|
||||||
|
|
||||||
bool hoistInstructions(InstructionQueue &toHoist, InstructionQueue &boundsChecks);
|
bool hoistInstructions(InstructionQueue &toHoist);
|
||||||
|
|
||||||
// Utility methods for invariance testing and instruction hoisting.
|
// Utility methods for invariance testing and instruction hoisting.
|
||||||
bool isInLoop(MDefinition *ins);
|
bool isInLoop(MDefinition *ins);
|
||||||
@ -96,15 +90,6 @@ class Loop
|
|||||||
inline bool isHoistable(const MDefinition *ins) const {
|
inline bool isHoistable(const MDefinition *ins) const {
|
||||||
return ins->isMovable() && !ins->isEffectful();
|
return ins->isMovable() && !ins->isEffectful();
|
||||||
}
|
}
|
||||||
|
|
||||||
// State for hoisting bounds checks. Even if the terms involved in a bounds
|
|
||||||
// check are not loop invariant, we analyze the tests and increments done
|
|
||||||
// in the loop to try to find a stronger condition which can be hoisted.
|
|
||||||
|
|
||||||
void tryHoistBoundsCheck(MBoundsCheck *ins, MTest *test, BranchDirection direction,
|
|
||||||
MInstruction **pupper, MInstruction **plower);
|
|
||||||
|
|
||||||
bool nonDecreasing(MDefinition *initial, MDefinition *start);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ion
|
} // namespace ion
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "MIR.h"
|
#include "MIR.h"
|
||||||
#include "MIRGraph.h"
|
#include "MIRGraph.h"
|
||||||
#include "IonSpewer.h"
|
#include "IonSpewer.h"
|
||||||
|
#include "RangeAnalysis.h"
|
||||||
#include "jsanalyze.h"
|
#include "jsanalyze.h"
|
||||||
#include "jsbool.h"
|
#include "jsbool.h"
|
||||||
#include "jsnum.h"
|
#include "jsnum.h"
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "MIR.h"
|
#include "MIR.h"
|
||||||
#include "MIRGraph.h"
|
#include "MIRGraph.h"
|
||||||
#include "EdgeCaseAnalysis.h"
|
#include "EdgeCaseAnalysis.h"
|
||||||
|
#include "RangeAnalysis.h"
|
||||||
#include "IonSpewer.h"
|
#include "IonSpewer.h"
|
||||||
#include "jsnum.h"
|
#include "jsnum.h"
|
||||||
#include "jsstr.h"
|
#include "jsstr.h"
|
||||||
@ -284,11 +285,6 @@ MConstant::MConstant(const js::Value &vp)
|
|||||||
{
|
{
|
||||||
setResultType(MIRTypeFromValue(vp));
|
setResultType(MIRTypeFromValue(vp));
|
||||||
setMovable();
|
setMovable();
|
||||||
|
|
||||||
if (type() == MIRType_Int32) {
|
|
||||||
range()->setLower(value().toInt32());
|
|
||||||
range()->setUpper(value().toInt32());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HashNumber
|
HashNumber
|
||||||
@ -476,53 +472,6 @@ MPhi::addInput(MDefinition *ins)
|
|||||||
return inputs_.append(ins);
|
return inputs_.append(ins);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
MPhi::recomputeRange()
|
|
||||||
{
|
|
||||||
if (type() != MIRType_Int32)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Use RangeUpdater rather than Range because it needs to
|
|
||||||
// track if it has been updated yet.
|
|
||||||
RangeUpdater r;
|
|
||||||
JS_ASSERT(getOperand(0)->op() != MDefinition::Op_OsrValue);
|
|
||||||
bool updated = false;
|
|
||||||
for (size_t i = 0; i < numOperands(); i++) {
|
|
||||||
if (getOperand(i)->block()->earlyAbort()) {
|
|
||||||
IonSpew(IonSpew_Range, "Ignoring unreachable input %d", getOperand(i)->id());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isOSRLikeValue(getOperand(i))) {
|
|
||||||
if (block()->isLoopHeader()) {
|
|
||||||
IonSpew(IonSpew_Range, " Updating input #%d (inst %d)", i, getOperand(i)->id());
|
|
||||||
changeCounts_[i].updateRange(getOperand(i)->range());
|
|
||||||
r.unionWith(&changeCounts_[i]);
|
|
||||||
} else {
|
|
||||||
r.unionWith(getOperand(i)->range());
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
if (IonSpewEnabled(IonSpew_Range)) {
|
|
||||||
fprintf(IonSpewFile, " %d:", getOperand(i)->id());
|
|
||||||
getOperand(i)->range()->printRange(IonSpewFile);
|
|
||||||
fprintf(IonSpewFile, " => ");
|
|
||||||
r.printRange(IonSpewFile);
|
|
||||||
fprintf(IonSpewFile, "\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!updated) {
|
|
||||||
IonSpew(IonSpew_Range, "My block is unreachable %d", id());
|
|
||||||
block()->setEarlyAbort();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return range()->update(r.getRange());
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32
|
uint32
|
||||||
MPrepareCall::argc() const
|
MPrepareCall::argc() const
|
||||||
{
|
{
|
||||||
@ -894,6 +843,12 @@ MAdd::updateForReplacement(MDefinition *ins_)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
MAdd::fallible()
|
||||||
|
{
|
||||||
|
return !isTruncated() && (!range() || !range()->isFinite());
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MSub::analyzeTruncateBackward()
|
MSub::analyzeTruncateBackward()
|
||||||
{
|
{
|
||||||
@ -911,6 +866,12 @@ MSub::updateForReplacement(MDefinition *ins_)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
MSub::fallible()
|
||||||
|
{
|
||||||
|
return !isTruncated() && (!range() || !range()->isFinite());
|
||||||
|
}
|
||||||
|
|
||||||
MDefinition *
|
MDefinition *
|
||||||
MMul::foldsTo(bool useValueNumbers)
|
MMul::foldsTo(bool useValueNumbers)
|
||||||
{
|
{
|
||||||
@ -975,6 +936,24 @@ MMul::updateForReplacement(MDefinition *ins_)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
MMul::canOverflow()
|
||||||
|
{
|
||||||
|
if (implicitTruncate_)
|
||||||
|
return false;
|
||||||
|
return !range() || !range()->isFinite();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
MMul::canBeNegativeZero()
|
||||||
|
{
|
||||||
|
if (!range())
|
||||||
|
return canBeNegativeZero_;
|
||||||
|
if (range()->lower() > 0 || range()->upper() < 0)
|
||||||
|
return false;
|
||||||
|
return canBeNegativeZero_;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MBinaryArithInstruction::infer(JSContext *cx, const TypeOracle::BinaryTypes &b)
|
MBinaryArithInstruction::infer(JSContext *cx, const TypeOracle::BinaryTypes &b)
|
||||||
{
|
{
|
||||||
@ -1530,6 +1509,12 @@ MNot::foldsTo(bool useValueNumbers)
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
MBoundsCheckLower::fallible()
|
||||||
|
{
|
||||||
|
return !range() || range()->lower() < minimum_;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MBeta::printOpcode(FILE *fp)
|
MBeta::printOpcode(FILE *fp)
|
||||||
{
|
{
|
||||||
@ -1537,17 +1522,23 @@ MBeta::printOpcode(FILE *fp)
|
|||||||
fprintf(fp, " ");
|
fprintf(fp, " ");
|
||||||
getOperand(0)->printName(fp);
|
getOperand(0)->printName(fp);
|
||||||
fprintf(fp, " ");
|
fprintf(fp, " ");
|
||||||
comparison_.printRange(fp);
|
|
||||||
|
Sprinter sp(GetIonContext()->cx);
|
||||||
|
sp.init();
|
||||||
|
comparison_->print(sp);
|
||||||
|
fprintf(fp, "%s", sp.string());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
MBeta::recomputeRange()
|
MBeta::computeRange()
|
||||||
{
|
{
|
||||||
bool nullRange = false;
|
bool emptyRange = false;
|
||||||
bool ret = range()->update(Range::intersect(val_->range(), &comparison_, &nullRange));
|
|
||||||
if (nullRange) {
|
Range *range = Range::intersect(val_->range(), comparison_, &emptyRange);
|
||||||
IonSpew(IonSpew_Range, "Marking block for inst %d unexitable", id());
|
if (emptyRange) {
|
||||||
block()->setEarlyAbort();
|
IonSpew(IonSpew_Range, "Marking block for inst %d unexitable", id());
|
||||||
|
block()->setEarlyAbort();
|
||||||
|
} else {
|
||||||
|
setRange(range);
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
179
js/src/ion/MIR.h
179
js/src/ion/MIR.h
@ -24,12 +24,14 @@
|
|||||||
#include "IonMacroAssembler.h"
|
#include "IonMacroAssembler.h"
|
||||||
#include "Bailouts.h"
|
#include "Bailouts.h"
|
||||||
#include "FixedList.h"
|
#include "FixedList.h"
|
||||||
#include "RangeAnalysis.h"
|
|
||||||
#include "CompilerRoot.h"
|
#include "CompilerRoot.h"
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
namespace ion {
|
namespace ion {
|
||||||
|
|
||||||
class ValueNumberData;
|
class ValueNumberData;
|
||||||
|
class Range;
|
||||||
|
|
||||||
static const inline
|
static const inline
|
||||||
MIRType MIRTypeFromValue(const js::Value &vp)
|
MIRType MIRTypeFromValue(const js::Value &vp)
|
||||||
{
|
{
|
||||||
@ -228,11 +230,7 @@ class MDefinition : public MNode
|
|||||||
uint32 id_; // Instruction ID, which after block re-ordering
|
uint32 id_; // Instruction ID, which after block re-ordering
|
||||||
// is sorted within a basic block.
|
// is sorted within a basic block.
|
||||||
ValueNumberData *valueNumber_; // The instruction's value number (see GVN for details in use)
|
ValueNumberData *valueNumber_; // The instruction's value number (see GVN for details in use)
|
||||||
|
Range *range_; // Any computed range for this def.
|
||||||
// Bug 765126: This should be a pointer. The range should only be allocated if range analysis is
|
|
||||||
// enabled.
|
|
||||||
Range range_; // The most specific known range for this def.
|
|
||||||
|
|
||||||
MIRType resultType_; // Representation of result type.
|
MIRType resultType_; // Representation of result type.
|
||||||
uint32 flags_; // Bit flags.
|
uint32 flags_; // Bit flags.
|
||||||
union {
|
union {
|
||||||
@ -292,8 +290,11 @@ class MDefinition : public MNode
|
|||||||
return trackedPc_;
|
return trackedPc_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Range *range() {
|
Range *range() const {
|
||||||
return &range_;
|
return range_;
|
||||||
|
}
|
||||||
|
void setRange(Range *range) {
|
||||||
|
range_ = range;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual HashNumber valueHash() const;
|
virtual HashNumber valueHash() const;
|
||||||
@ -306,9 +307,10 @@ class MDefinition : public MNode
|
|||||||
virtual void analyzeEdgeCasesBackward();
|
virtual void analyzeEdgeCasesBackward();
|
||||||
virtual void analyzeTruncateBackward();
|
virtual void analyzeTruncateBackward();
|
||||||
bool earlyAbortCheck();
|
bool earlyAbortCheck();
|
||||||
// Propagate a range. Return true if the range changed.
|
|
||||||
virtual bool recomputeRange() {
|
// Compute an absolute or symbolic range for the value of this node.
|
||||||
return false;
|
// Ranges are only computed for definitions whose type is int32.
|
||||||
|
virtual void computeRange() {
|
||||||
}
|
}
|
||||||
|
|
||||||
MNode::Kind kind() const {
|
MNode::Kind kind() const {
|
||||||
@ -623,6 +625,8 @@ class MConstant : public MNullaryInstruction
|
|||||||
AliasSet getAliasSet() const {
|
AliasSet getAliasSet() const {
|
||||||
return AliasSet::None();
|
return AliasSet::None();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void computeRange();
|
||||||
};
|
};
|
||||||
|
|
||||||
class MParameter : public MNullaryInstruction
|
class MParameter : public MNullaryInstruction
|
||||||
@ -852,6 +856,17 @@ class MGoto : public MAryControlInstruction<0, 1>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum BranchDirection {
|
||||||
|
FALSE_BRANCH,
|
||||||
|
TRUE_BRANCH
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline BranchDirection
|
||||||
|
NegateBranchDirection(BranchDirection dir)
|
||||||
|
{
|
||||||
|
return (dir == FALSE_BRANCH) ? TRUE_BRANCH : FALSE_BRANCH;
|
||||||
|
}
|
||||||
|
|
||||||
// Tests if the input instruction evaluates to true or false, and jumps to the
|
// Tests if the input instruction evaluates to true or false, and jumps to the
|
||||||
// start of a corresponding basic block.
|
// start of a corresponding basic block.
|
||||||
class MTest
|
class MTest
|
||||||
@ -875,6 +890,9 @@ class MTest
|
|||||||
MBasicBlock *ifFalse() const {
|
MBasicBlock *ifFalse() const {
|
||||||
return getSuccessor(1);
|
return getSuccessor(1);
|
||||||
}
|
}
|
||||||
|
MBasicBlock *branchSuccessor(BranchDirection dir) const {
|
||||||
|
return (dir == TRUE_BRANCH) ? ifTrue() : ifFalse();
|
||||||
|
}
|
||||||
TypePolicy *typePolicy() {
|
TypePolicy *typePolicy() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -1701,7 +1719,6 @@ class MToInt32 : public MUnaryInstruction
|
|||||||
{
|
{
|
||||||
setResultType(MIRType_Int32);
|
setResultType(MIRType_Int32);
|
||||||
setMovable();
|
setMovable();
|
||||||
range()->set(JSVAL_INT_MIN, JSVAL_INT_MAX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -1742,7 +1759,6 @@ class MTruncateToInt32 : public MUnaryInstruction
|
|||||||
{
|
{
|
||||||
setResultType(MIRType_Int32);
|
setResultType(MIRType_Int32);
|
||||||
setMovable();
|
setMovable();
|
||||||
range()->set(JSVAL_INT_MIN, JSVAL_INT_MAX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -1808,7 +1824,6 @@ class MBitNot
|
|||||||
{
|
{
|
||||||
setResultType(MIRType_Int32);
|
setResultType(MIRType_Int32);
|
||||||
setMovable();
|
setMovable();
|
||||||
range()->set(JSVAL_INT_MIN, JSVAL_INT_MAX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -1904,7 +1919,6 @@ class MBinaryBitwiseInstruction
|
|||||||
{
|
{
|
||||||
setResultType(MIRType_Int32);
|
setResultType(MIRType_Int32);
|
||||||
setMovable();
|
setMovable();
|
||||||
range()->set(JSVAL_INT_MIN, JSVAL_INT_MAX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -1947,12 +1961,7 @@ class MBitAnd : public MBinaryBitwiseInstruction
|
|||||||
MDefinition *foldIfEqual() {
|
MDefinition *foldIfEqual() {
|
||||||
return getOperand(0); // x & x => x;
|
return getOperand(0); // x & x => x;
|
||||||
}
|
}
|
||||||
bool recomputeRange() {
|
void computeRange();
|
||||||
Range *left = getOperand(0)->range();
|
|
||||||
Range *right = getOperand(1)->range();
|
|
||||||
return range()->update(Range::and_(left, right));
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class MBitOr : public MBinaryBitwiseInstruction
|
class MBitOr : public MBinaryBitwiseInstruction
|
||||||
@ -2031,15 +2040,7 @@ class MLsh : public MShiftInstruction
|
|||||||
return getOperand(0);
|
return getOperand(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool recomputeRange() {
|
void computeRange();
|
||||||
MDefinition *right = getOperand(1);
|
|
||||||
if (!right->isConstant())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
int32 c = right->toConstant()->value().toInt32();
|
|
||||||
const Range *other = getOperand(0)->range();
|
|
||||||
return range()->update(Range::shl(other, c));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class MRsh : public MShiftInstruction
|
class MRsh : public MShiftInstruction
|
||||||
@ -2057,15 +2058,7 @@ class MRsh : public MShiftInstruction
|
|||||||
// x >> 0 => x
|
// x >> 0 => x
|
||||||
return getOperand(0);
|
return getOperand(0);
|
||||||
}
|
}
|
||||||
bool recomputeRange() {
|
void computeRange();
|
||||||
MDefinition *right = getOperand(1);
|
|
||||||
if (!right->isConstant())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
int32 c = right->toConstant()->value().toInt32();
|
|
||||||
Range *other = getOperand(0)->range();
|
|
||||||
return range()->update(Range::shr(other, c));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class MUrsh : public MShiftInstruction
|
class MUrsh : public MShiftInstruction
|
||||||
@ -2140,6 +2133,11 @@ class MBinaryArithInstruction
|
|||||||
|
|
||||||
void infer(JSContext *cx, const TypeOracle::BinaryTypes &b);
|
void infer(JSContext *cx, const TypeOracle::BinaryTypes &b);
|
||||||
|
|
||||||
|
void setInt32() {
|
||||||
|
specialization_ = MIRType_Int32;
|
||||||
|
setResultType(MIRType_Int32);
|
||||||
|
}
|
||||||
|
|
||||||
bool congruentTo(MDefinition *const &ins) const {
|
bool congruentTo(MDefinition *const &ins) const {
|
||||||
return MBinaryInstruction::congruentTo(ins);
|
return MBinaryInstruction::congruentTo(ins);
|
||||||
}
|
}
|
||||||
@ -2226,17 +2224,7 @@ class MAbs
|
|||||||
AliasSet getAliasSet() const {
|
AliasSet getAliasSet() const {
|
||||||
return AliasSet::None();
|
return AliasSet::None();
|
||||||
}
|
}
|
||||||
bool recomputeRange() {
|
void computeRange();
|
||||||
if (specialization_ != MIRType_Int32)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Range *other = getOperand(0)->range();
|
|
||||||
Range r(0,
|
|
||||||
Max(Range::abs64((int64_t)other->lower()),
|
|
||||||
Range::abs64((int64_t)other->upper())));
|
|
||||||
|
|
||||||
return range()->update(r);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Inline implementation of Math.sqrt().
|
// Inline implementation of Math.sqrt().
|
||||||
@ -2439,18 +2427,8 @@ class MAdd : public MBinaryArithInstruction
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fallible() {
|
bool fallible();
|
||||||
return !isTruncated() && !range()->isFinite();
|
void computeRange();
|
||||||
}
|
|
||||||
|
|
||||||
bool recomputeRange() {
|
|
||||||
if (specialization() != MIRType_Int32)
|
|
||||||
return false;
|
|
||||||
Range *left = getOperand(0)->range();
|
|
||||||
Range *right = getOperand(1)->range();
|
|
||||||
Range next = isTruncated() ? Range::addTruncate(left,right) : Range::add(left, right);
|
|
||||||
return range()->update(next);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class MSub : public MBinaryArithInstruction
|
class MSub : public MBinaryArithInstruction
|
||||||
@ -2482,18 +2460,8 @@ class MSub : public MBinaryArithInstruction
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fallible() {
|
bool fallible();
|
||||||
return !isTruncated() && !range()->isFinite();
|
void computeRange();
|
||||||
}
|
|
||||||
|
|
||||||
bool recomputeRange() {
|
|
||||||
if (specialization() != MIRType_Int32)
|
|
||||||
return false;
|
|
||||||
Range *left = getOperand(0)->range();
|
|
||||||
Range *right = getOperand(1)->range();
|
|
||||||
Range next = isTruncated() ? Range::subTruncate(left,right) : Range::sub(left, right);
|
|
||||||
return range()->update(next);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class MMul : public MBinaryArithInstruction
|
class MMul : public MBinaryArithInstruction
|
||||||
@ -2540,30 +2508,16 @@ class MMul : public MBinaryArithInstruction
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool canOverflow() {
|
bool canOverflow();
|
||||||
return !implicitTruncate_ && !range()->isFinite();
|
bool canBeNegativeZero();
|
||||||
}
|
|
||||||
|
|
||||||
bool canBeNegativeZero() {
|
|
||||||
if (range()->lower() > 0 || range()->upper() < 0)
|
|
||||||
return false;
|
|
||||||
return canBeNegativeZero_;
|
|
||||||
}
|
|
||||||
bool updateForReplacement(MDefinition *ins);
|
bool updateForReplacement(MDefinition *ins);
|
||||||
|
|
||||||
bool fallible() {
|
bool fallible() {
|
||||||
return canBeNegativeZero_ || canOverflow();
|
return canBeNegativeZero_ || canOverflow();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool recomputeRange() {
|
void computeRange();
|
||||||
if (specialization() != MIRType_Int32)
|
|
||||||
return false;
|
|
||||||
Range *left = getOperand(0)->range();
|
|
||||||
Range *right = getOperand(1)->range();
|
|
||||||
if (isPossibleTruncated())
|
|
||||||
implicitTruncate_ = !Range::precisionLossMul(left, right);
|
|
||||||
return range()->update(Range::mul(left, right));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isPossibleTruncated() const {
|
bool isPossibleTruncated() const {
|
||||||
return possibleTruncate_;
|
return possibleTruncate_;
|
||||||
@ -2655,21 +2609,7 @@ class MMod : public MBinaryArithInstruction
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool recomputeRange() {
|
void computeRange();
|
||||||
if (specialization() != MIRType_Int32)
|
|
||||||
return false;
|
|
||||||
Range *rhs = getOperand(1)->range();
|
|
||||||
int64_t a = Range::abs64((int64_t)rhs->lower());
|
|
||||||
int64_t b = Range::abs64((int64_t)rhs->upper());
|
|
||||||
if (a ==0 && b == 0) {
|
|
||||||
// We should never take something % 0.
|
|
||||||
Range r(INT_MIN, INT_MAX);
|
|
||||||
return range()->update(r);
|
|
||||||
}
|
|
||||||
int64_t bound = Max(1-a, b-1);
|
|
||||||
Range r(-bound, bound);
|
|
||||||
return range()->update(r);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class MConcat
|
class MConcat
|
||||||
@ -2709,8 +2649,6 @@ class MCharCodeAt
|
|||||||
{
|
{
|
||||||
setMovable();
|
setMovable();
|
||||||
setResultType(MIRType_Int32);
|
setResultType(MIRType_Int32);
|
||||||
range()->set(0, 65535); //ECMA 262 says that the integer will be
|
|
||||||
//non-negative and less than 65535.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -2728,6 +2666,8 @@ class MCharCodeAt
|
|||||||
// Strings are immutable, so there is no implicit dependency.
|
// Strings are immutable, so there is no implicit dependency.
|
||||||
return AliasSet::None();
|
return AliasSet::None();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void computeRange();
|
||||||
};
|
};
|
||||||
|
|
||||||
class MFromCharCode
|
class MFromCharCode
|
||||||
@ -2760,9 +2700,6 @@ class MPhi : public MDefinition, public InlineForwardListNode<MPhi>
|
|||||||
bool triedToSpecialize_;
|
bool triedToSpecialize_;
|
||||||
bool hasBytecodeUses_;
|
bool hasBytecodeUses_;
|
||||||
bool isIterator_;
|
bool isIterator_;
|
||||||
// For every input to the phi, track how many times it has changed
|
|
||||||
// Only used in loop headers, so it defaults to 0 elements to conserve space
|
|
||||||
js::Vector<RangeChangeCount, 0, IonAllocPolicy> changeCounts_;
|
|
||||||
MPhi(uint32 slot)
|
MPhi(uint32 slot)
|
||||||
: slot_(slot),
|
: slot_(slot),
|
||||||
triedToSpecialize_(false),
|
triedToSpecialize_(false),
|
||||||
@ -2819,10 +2756,7 @@ class MPhi : public MDefinition, public InlineForwardListNode<MPhi>
|
|||||||
AliasSet getAliasSet() const {
|
AliasSet getAliasSet() const {
|
||||||
return AliasSet::None();
|
return AliasSet::None();
|
||||||
}
|
}
|
||||||
bool recomputeRange();
|
void computeRange();
|
||||||
bool initCounts() {
|
|
||||||
return changeCounts_.resize(inputs_.length());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// The goal of a Beta node is to split a def at a conditionally taken
|
// The goal of a Beta node is to split a def at a conditionally taken
|
||||||
@ -2830,19 +2764,20 @@ class MPhi : public MDefinition, public InlineForwardListNode<MPhi>
|
|||||||
class MBeta : public MUnaryInstruction
|
class MBeta : public MUnaryInstruction
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
Range comparison_;
|
const Range *comparison_;
|
||||||
MDefinition *val_;
|
MDefinition *val_;
|
||||||
MBeta(MDefinition *val, const Range &comp)
|
MBeta(MDefinition *val, const Range *comp)
|
||||||
: MUnaryInstruction(val),
|
: MUnaryInstruction(val),
|
||||||
comparison_(comp),
|
comparison_(comp),
|
||||||
val_(val)
|
val_(val)
|
||||||
{
|
{
|
||||||
|
setResultType(val->type());
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
INSTRUCTION_HEADER(Beta);
|
INSTRUCTION_HEADER(Beta);
|
||||||
void printOpcode(FILE *fp);
|
void printOpcode(FILE *fp);
|
||||||
static MBeta *New(MDefinition *val, const Range &comp)
|
static MBeta *New(MDefinition *val, const Range *comp)
|
||||||
{
|
{
|
||||||
return new MBeta(val, comp);
|
return new MBeta(val, comp);
|
||||||
}
|
}
|
||||||
@ -2851,7 +2786,7 @@ class MBeta : public MUnaryInstruction
|
|||||||
return AliasSet::None();
|
return AliasSet::None();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool recomputeRange();
|
void computeRange();
|
||||||
};
|
};
|
||||||
|
|
||||||
// MIR representation of a Value on the OSR StackFrame.
|
// MIR representation of a Value on the OSR StackFrame.
|
||||||
@ -3509,9 +3444,7 @@ class MBoundsCheckLower
|
|||||||
AliasSet getAliasSet() const {
|
AliasSet getAliasSet() const {
|
||||||
return AliasSet::None();
|
return AliasSet::None();
|
||||||
}
|
}
|
||||||
bool fallible() {
|
bool fallible();
|
||||||
return range()->lower() < minimum_;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load a value from a dense array's element vector and does a hole check if the
|
// Load a value from a dense array's element vector and does a hole check if the
|
||||||
@ -3987,7 +3920,6 @@ class MClampToUint8
|
|||||||
{
|
{
|
||||||
setResultType(MIRType_Int32);
|
setResultType(MIRType_Int32);
|
||||||
setMovable();
|
setMovable();
|
||||||
range()->set(0, 255);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -4011,6 +3943,7 @@ class MClampToUint8
|
|||||||
AliasSet getAliasSet() const {
|
AliasSet getAliasSet() const {
|
||||||
return AliasSet::None();
|
return AliasSet::None();
|
||||||
}
|
}
|
||||||
|
void computeRange();
|
||||||
};
|
};
|
||||||
|
|
||||||
class MLoadFixedSlot
|
class MLoadFixedSlot
|
||||||
|
@ -30,11 +30,6 @@ typedef InlineForwardListIterator<MPhi> MPhiIterator;
|
|||||||
|
|
||||||
class LBlock;
|
class LBlock;
|
||||||
|
|
||||||
enum BranchDirection {
|
|
||||||
FALSE_BRANCH,
|
|
||||||
TRUE_BRANCH
|
|
||||||
};
|
|
||||||
|
|
||||||
class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
|
class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -11,6 +11,7 @@
|
|||||||
#include "wtf/Platform.h"
|
#include "wtf/Platform.h"
|
||||||
#include "MIR.h"
|
#include "MIR.h"
|
||||||
#include "CompileInfo.h"
|
#include "CompileInfo.h"
|
||||||
|
#include "IonAnalysis.h"
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
namespace ion {
|
namespace ion {
|
||||||
@ -18,6 +19,52 @@ namespace ion {
|
|||||||
class MBasicBlock;
|
class MBasicBlock;
|
||||||
class MIRGraph;
|
class MIRGraph;
|
||||||
|
|
||||||
|
// An upper bound computed on the number of backedges a loop will take.
|
||||||
|
// This count only includes backedges taken while running Ion code: for OSR
|
||||||
|
// loops, this will exclude iterations that executed in the interpreter or in
|
||||||
|
// baseline compiled code.
|
||||||
|
struct LoopIterationBound : public TempObject
|
||||||
|
{
|
||||||
|
// Loop for which this bound applies.
|
||||||
|
MBasicBlock *header;
|
||||||
|
|
||||||
|
// Test from which this bound was derived. Code in the loop body which this
|
||||||
|
// test dominates (will include the backedge) will execute at most 'bound'
|
||||||
|
// times. Other code in the loop will execute at most '1 + Max(bound, 0)'
|
||||||
|
// times.
|
||||||
|
MTest *test;
|
||||||
|
|
||||||
|
// Symbolic bound computed for the number of backedge executions.
|
||||||
|
LinearSum sum;
|
||||||
|
|
||||||
|
LoopIterationBound(MBasicBlock *header, MTest *test, LinearSum sum)
|
||||||
|
: header(header), test(test), sum(sum)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// A symbolic upper or lower bound computed for a term.
|
||||||
|
struct SymbolicBound : public TempObject
|
||||||
|
{
|
||||||
|
// Any loop iteration bound from which this was derived.
|
||||||
|
//
|
||||||
|
// If non-NULL, then 'sum' is only valid within the loop body, at points
|
||||||
|
// dominated by the loop bound's test (see LoopIterationBound).
|
||||||
|
//
|
||||||
|
// If NULL, then 'sum' is always valid.
|
||||||
|
LoopIterationBound *loop;
|
||||||
|
|
||||||
|
// Computed symbolic bound, see above.
|
||||||
|
LinearSum sum;
|
||||||
|
|
||||||
|
SymbolicBound(LoopIterationBound *loop, LinearSum sum)
|
||||||
|
: loop(loop), sum(sum)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(Sprinter &sp) const;
|
||||||
|
};
|
||||||
|
|
||||||
class RangeAnalysis
|
class RangeAnalysis
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
@ -33,16 +80,20 @@ class RangeAnalysis
|
|||||||
bool addBetaNobes();
|
bool addBetaNobes();
|
||||||
bool analyze();
|
bool analyze();
|
||||||
bool removeBetaNobes();
|
bool removeBetaNobes();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void analyzeLoop(MBasicBlock *header);
|
||||||
|
LoopIterationBound *analyzeLoopIterationCount(MBasicBlock *header,
|
||||||
|
MTest *test, BranchDirection direction);
|
||||||
|
void analyzeLoopPhi(MBasicBlock *header, LoopIterationBound *loopBound, MPhi *phi);
|
||||||
|
bool tryHoistBoundsCheck(MBasicBlock *header, MBoundsCheck *ins);
|
||||||
|
void markBlocksInLoopBody(MBasicBlock *header, MBasicBlock *current);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RangeChangeCount;
|
class Range : public TempObject {
|
||||||
class Range {
|
|
||||||
private:
|
private:
|
||||||
// :TODO: we should do symbolic range evaluation, where we have
|
// Absolute ranges.
|
||||||
// information of the form v1 < v2 for arbitrary defs v1 and v2, not
|
//
|
||||||
// just constants of type int32.
|
|
||||||
// (Bug 766592)
|
|
||||||
|
|
||||||
// We represent ranges where the endpoints can be in the set:
|
// We represent ranges where the endpoints can be in the set:
|
||||||
// {-infty} U [INT_MIN, INT_MAX] U {infty}. A bound of +/-
|
// {-infty} U [INT_MIN, INT_MAX] U {infty}. A bound of +/-
|
||||||
// infty means that the value may have overflowed in that
|
// infty means that the value may have overflowed in that
|
||||||
@ -69,33 +120,38 @@ class Range {
|
|||||||
int32 upper_;
|
int32 upper_;
|
||||||
bool upper_infinite_;
|
bool upper_infinite_;
|
||||||
|
|
||||||
|
// Any symbolic lower or upper bound computed for this term.
|
||||||
|
const SymbolicBound *symbolicLower_;
|
||||||
|
const SymbolicBound *symbolicUpper_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Range()
|
Range()
|
||||||
: lower_(JSVAL_INT_MIN),
|
: lower_(JSVAL_INT_MIN),
|
||||||
lower_infinite_(true),
|
lower_infinite_(true),
|
||||||
upper_(JSVAL_INT_MAX),
|
upper_(JSVAL_INT_MAX),
|
||||||
upper_infinite_(true)
|
upper_infinite_(true),
|
||||||
|
symbolicLower_(NULL),
|
||||||
|
symbolicUpper_(NULL)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
Range(int64_t l, int64_t h) {
|
Range(int64_t l, int64_t h)
|
||||||
|
: symbolicLower_(NULL),
|
||||||
|
symbolicUpper_(NULL)
|
||||||
|
{
|
||||||
setLower(l);
|
setLower(l);
|
||||||
setUpper(h);
|
setUpper(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
Range(const Range &other)
|
Range(const Range &other)
|
||||||
: lower_(other.lower_),
|
: lower_(other.lower_),
|
||||||
lower_infinite_(other.lower_infinite_),
|
lower_infinite_(other.lower_infinite_),
|
||||||
upper_(other.upper_),
|
upper_(other.upper_),
|
||||||
upper_infinite_(other.upper_infinite_)
|
upper_infinite_(other.upper_infinite_),
|
||||||
|
symbolicLower_(NULL),
|
||||||
|
symbolicUpper_(NULL)
|
||||||
{}
|
{}
|
||||||
static Range Truncate(int64_t l, int64_t h) {
|
|
||||||
Range ret(l,h);
|
static Range *Truncate(int64_t l, int64_t h);
|
||||||
if (!ret.isFinite()) {
|
|
||||||
ret.makeLowerInfinite();
|
|
||||||
ret.makeUpperInfinite();
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int64_t abs64(int64_t x) {
|
static int64_t abs64(int64_t x) {
|
||||||
#ifdef WTF_OS_WINDOWS
|
#ifdef WTF_OS_WINDOWS
|
||||||
@ -105,7 +161,7 @@ class Range {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void printRange(FILE *fp);
|
void print(Sprinter &sp) const;
|
||||||
bool update(const Range *other);
|
bool update(const Range *other);
|
||||||
bool update(const Range &other) {
|
bool update(const Range &other) {
|
||||||
return update(&other);
|
return update(&other);
|
||||||
@ -116,15 +172,15 @@ class Range {
|
|||||||
// copying when chaining together unions when handling Phi
|
// copying when chaining together unions when handling Phi
|
||||||
// nodes.
|
// nodes.
|
||||||
void unionWith(const Range *other);
|
void unionWith(const Range *other);
|
||||||
static Range intersect(const Range *lhs, const Range *rhs, bool *nullRange);
|
static Range * intersect(const Range *lhs, const Range *rhs, bool *emptyRange);
|
||||||
static Range addTruncate(const Range *lhs, const Range *rhs);
|
static Range * addTruncate(const Range *lhs, const Range *rhs);
|
||||||
static Range subTruncate(const Range *lhs, const Range *rhs);
|
static Range * subTruncate(const Range *lhs, const Range *rhs);
|
||||||
static Range add(const Range *lhs, const Range *rhs);
|
static Range * add(const Range *lhs, const Range *rhs);
|
||||||
static Range sub(const Range *lhs, const Range *rhs);
|
static Range * sub(const Range *lhs, const Range *rhs);
|
||||||
static Range mul(const Range *lhs, const Range *rhs);
|
static Range * mul(const Range *lhs, const Range *rhs);
|
||||||
static Range and_(const Range *lhs, const Range *rhs);
|
static Range * and_(const Range *lhs, const Range *rhs);
|
||||||
static Range shl(const Range *lhs, int32 c);
|
static Range * shl(const Range *lhs, int32 c);
|
||||||
static Range shr(const Range *lhs, int32 c);
|
static Range * shr(const Range *lhs, int32 c);
|
||||||
|
|
||||||
static bool precisionLossMul(const Range *lhs, const Range *rhs);
|
static bool precisionLossMul(const Range *lhs, const Range *rhs);
|
||||||
|
|
||||||
@ -184,35 +240,20 @@ class Range {
|
|||||||
setLower(l);
|
setLower(l);
|
||||||
setUpper(h);
|
setUpper(h);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
struct RangeChangeCount {
|
const SymbolicBound *symbolicLower() const {
|
||||||
Range oldRange;
|
return symbolicLower_;
|
||||||
unsigned char lowerCount_ : 4;
|
}
|
||||||
unsigned char upperCount_ : 4;
|
const SymbolicBound *symbolicUpper() const {
|
||||||
RangeChangeCount() : oldRange(), lowerCount_(0), upperCount_(0) {};
|
return symbolicUpper_;
|
||||||
void updateRange(Range *newRange) {
|
}
|
||||||
JS_ASSERT(newRange->lower() >= oldRange.lower());
|
|
||||||
if (newRange->lower() != oldRange.lower())
|
void setSymbolicLower(SymbolicBound *bound) {
|
||||||
lowerCount_ = lowerCount_ < 15 ? lowerCount_ + 1 : lowerCount_;
|
symbolicLower_ = bound;
|
||||||
JS_ASSERT(newRange->upper() <= oldRange.upper());
|
}
|
||||||
if (newRange->upper() != oldRange.upper())
|
void setSymbolicUpper(SymbolicBound *bound) {
|
||||||
upperCount_ = upperCount_ < 15 ? upperCount_ + 1 : upperCount_;
|
symbolicUpper_ = bound;
|
||||||
oldRange = *newRange;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
class RangeUpdater {
|
|
||||||
Range r_;
|
|
||||||
bool lowerSet_;
|
|
||||||
bool upperSet_;
|
|
||||||
public:
|
|
||||||
RangeUpdater() : r_(), lowerSet_(false), upperSet_(false) {}
|
|
||||||
void unionWith(const Range *other);
|
|
||||||
void unionWith(RangeChangeCount *other);
|
|
||||||
void updateLower(const Range * other);
|
|
||||||
void updateUpper(const Range * other);
|
|
||||||
Range *getRange() { JS_ASSERT(lowerSet_ && upperSet_); return &r_; }
|
|
||||||
void printRange(FILE *fp) { r_.printRange(fp); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ion
|
} // namespace ion
|
||||||
|
@ -2008,7 +2008,7 @@ JITCodeHasCheck(HandleScript script, jsbytecode *pc, RecompileKind kind)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (script->hasAnyIonScript())
|
if (script->hasAnyIonScript() || script->isIonCompilingOffThread())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -328,7 +328,8 @@ js_DumpPCCounts(JSContext *cx, HandleScript script, js::Sprinter *sp)
|
|||||||
Sprint(sp, "BB #%lu [%05u]", block.id(), block.offset());
|
Sprint(sp, "BB #%lu [%05u]", block.id(), block.offset());
|
||||||
for (size_t j = 0; j < block.numSuccessors(); j++)
|
for (size_t j = 0; j < block.numSuccessors(); j++)
|
||||||
Sprint(sp, " -> #%lu", block.successor(j));
|
Sprint(sp, " -> #%lu", block.successor(j));
|
||||||
Sprint(sp, " :: %llu hits\n", block.hitCount());
|
Sprint(sp, " :: %llu hits %u instruction bytes %u spill bytes\n",
|
||||||
|
block.hitCount(), block.instructionBytes(), block.spillBytes());
|
||||||
Sprint(sp, "%s\n", block.code());
|
Sprint(sp, "%s\n", block.code());
|
||||||
}
|
}
|
||||||
ionCounts = ionCounts->previous();
|
ionCounts = ionCounts->previous();
|
||||||
@ -7209,6 +7210,12 @@ GetPCCountJSON(JSContext *cx, const ScriptAndCounts &sac, StringBuffer &buf)
|
|||||||
return false;
|
return false;
|
||||||
buf.append(str);
|
buf.append(str);
|
||||||
|
|
||||||
|
AppendJSONProperty(buf, "instructionBytes");
|
||||||
|
NumberValueToStringBuffer(cx, Int32Value(block.instructionBytes()), buf);
|
||||||
|
|
||||||
|
AppendJSONProperty(buf, "spillBytes");
|
||||||
|
NumberValueToStringBuffer(cx, Int32Value(block.spillBytes()), buf);
|
||||||
|
|
||||||
buf.append('}');
|
buf.append('}');
|
||||||
}
|
}
|
||||||
buf.append(']');
|
buf.append(']');
|
||||||
|
@ -113,7 +113,7 @@ class AudioSendAndReceive
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const unsigned int PLAYOUT_SAMPLE_FREQUENCY; //default is 16000
|
static const unsigned int PLAYOUT_SAMPLE_FREQUENCY; //default is 16000
|
||||||
static const unsigned int PLAYOUT_SAMPLE_LENGTH; //default is 160
|
static const unsigned int PLAYOUT_SAMPLE_LENGTH; //default is 160000
|
||||||
|
|
||||||
AudioSendAndReceive()
|
AudioSendAndReceive()
|
||||||
{
|
{
|
||||||
@ -150,7 +150,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
const unsigned int AudioSendAndReceive::PLAYOUT_SAMPLE_FREQUENCY = 16000;
|
const unsigned int AudioSendAndReceive::PLAYOUT_SAMPLE_FREQUENCY = 16000;
|
||||||
const unsigned int AudioSendAndReceive::PLAYOUT_SAMPLE_LENGTH = 160;
|
const unsigned int AudioSendAndReceive::PLAYOUT_SAMPLE_LENGTH = 160000;
|
||||||
|
|
||||||
int AudioSendAndReceive::WriteWaveHeader(int rate, int channels, FILE* outFile)
|
int AudioSendAndReceive::WriteWaveHeader(int rate, int channels, FILE* outFile)
|
||||||
{
|
{
|
||||||
@ -271,7 +271,7 @@ void AudioSendAndReceive::GenerateAndReadSamples()
|
|||||||
int16_t audioOutput[PLAYOUT_SAMPLE_LENGTH];
|
int16_t audioOutput[PLAYOUT_SAMPLE_LENGTH];
|
||||||
short* inbuf;
|
short* inbuf;
|
||||||
int sampleLengthDecoded = 0;
|
int sampleLengthDecoded = 0;
|
||||||
int SAMPLES = (PLAYOUT_SAMPLE_FREQUENCY * 10)/1000; //10 milliseconds
|
int SAMPLES = (PLAYOUT_SAMPLE_FREQUENCY * 10); //10 seconds
|
||||||
int CHANNELS = 1; //mono audio
|
int CHANNELS = 1; //mono audio
|
||||||
int sampleLengthInBytes = sizeof(audioInput);
|
int sampleLengthInBytes = sizeof(audioInput);
|
||||||
//generated audio buffer
|
//generated audio buffer
|
||||||
@ -329,7 +329,7 @@ void AudioSendAndReceive::GenerateAndReadSamples()
|
|||||||
cerr << "Couldn't Write " << sampleLengthInBytes << "bytes" << endl;
|
cerr << "Couldn't Write " << sampleLengthInBytes << "bytes" << endl;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}while(numSamplesReadFromInput <= (SAMPLES));
|
}while(numSamplesReadFromInput < SAMPLES);
|
||||||
|
|
||||||
FinishWaveHeader(outFile);
|
FinishWaveHeader(outFile);
|
||||||
fclose(outFile);
|
fclose(outFile);
|
||||||
|
41
mobile/android/base/CheckableLinearLayout.java
Normal file
41
mobile/android/base/CheckableLinearLayout.java
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package org.mozilla.gecko;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.Checkable;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
|
||||||
|
public class CheckableLinearLayout extends LinearLayout implements Checkable {
|
||||||
|
|
||||||
|
private CheckBox mCheckBox;
|
||||||
|
|
||||||
|
public CheckableLinearLayout(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isChecked() {
|
||||||
|
return mCheckBox != null ? mCheckBox.isChecked() : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChecked(boolean isChecked) {
|
||||||
|
if (mCheckBox != null)
|
||||||
|
mCheckBox.setChecked(isChecked);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void toggle() {
|
||||||
|
if (mCheckBox != null)
|
||||||
|
mCheckBox.toggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFinishInflate() {
|
||||||
|
super.onFinishInflate();
|
||||||
|
|
||||||
|
mCheckBox = (CheckBox) findViewById(R.id.checkbox);
|
||||||
|
mCheckBox.setClickable(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -27,6 +27,7 @@ import org.json.JSONObject;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
|
import android.app.Dialog;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
@ -82,10 +83,12 @@ import android.view.WindowManager;
|
|||||||
import android.view.accessibility.AccessibilityEvent;
|
import android.view.accessibility.AccessibilityEvent;
|
||||||
import android.view.accessibility.AccessibilityManager;
|
import android.view.accessibility.AccessibilityManager;
|
||||||
import android.widget.AbsoluteLayout;
|
import android.widget.AbsoluteLayout;
|
||||||
|
import android.widget.CheckBox;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
|
import android.widget.SimpleAdapter;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
@ -1108,14 +1111,14 @@ abstract public class GeckoApp
|
|||||||
/**
|
/**
|
||||||
* @param aPermissions
|
* @param aPermissions
|
||||||
* Array of JSON objects to represent site permissions.
|
* Array of JSON objects to represent site permissions.
|
||||||
* Example: { type: "offline-app", setting: "Store Offline Data: Allow" }
|
* Example: { type: "offline-app", setting: "Store Offline Data", value: "Allow" }
|
||||||
*/
|
*/
|
||||||
private void showSiteSettingsDialog(String aHost, JSONArray aPermissions) {
|
private void showSiteSettingsDialog(String aHost, JSONArray aPermissions) {
|
||||||
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
|
||||||
View customTitleView = getLayoutInflater().inflate(R.layout.site_setting_title, null);
|
View customTitleView = getLayoutInflater().inflate(R.layout.site_setting_title, null);
|
||||||
((TextView) customTitleView.findViewById(R.id.title)).setText(R.string.site_settings_title);
|
((TextView) customTitleView.findViewById(R.id.title)).setText(R.string.site_settings_title);
|
||||||
((TextView) customTitleView.findViewById(R.id.host)).setText(aHost);
|
((TextView) customTitleView.findViewById(R.id.host)).setText(aHost);
|
||||||
builder.setCustomTitle(customTitleView);
|
builder.setCustomTitle(customTitleView);
|
||||||
|
|
||||||
// If there are no permissions to clear, show the user a message about that.
|
// If there are no permissions to clear, show the user a message about that.
|
||||||
@ -1123,24 +1126,32 @@ abstract public class GeckoApp
|
|||||||
if (aPermissions.length() == 0) {
|
if (aPermissions.length() == 0) {
|
||||||
builder.setMessage(R.string.site_settings_no_settings);
|
builder.setMessage(R.string.site_settings_no_settings);
|
||||||
} else {
|
} else {
|
||||||
// Eventually we should use a list adapter and custom checkable list items
|
|
||||||
// to make a two-line UI to match the mock-ups
|
ArrayList <HashMap<String, String>> itemList = new ArrayList <HashMap<String, String>>();
|
||||||
CharSequence[] items = new CharSequence[aPermissions.length()];
|
|
||||||
boolean[] states = new boolean[aPermissions.length()];
|
|
||||||
for (int i = 0; i < aPermissions.length(); i++) {
|
for (int i = 0; i < aPermissions.length(); i++) {
|
||||||
try {
|
try {
|
||||||
items[i] = aPermissions.getJSONObject(i).getString("setting");
|
JSONObject permObj = aPermissions.getJSONObject(i);
|
||||||
// Make all the items checked by default.
|
HashMap<String, String> map = new HashMap<String, String>();
|
||||||
states[i] = true;
|
map.put("setting", permObj.getString("setting"));
|
||||||
|
map.put("value", permObj.getString("value"));
|
||||||
|
itemList.add(map);
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
Log.w(LOGTAG, "Exception populating settings items.", e);
|
Log.w(LOGTAG, "Exception populating settings items.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
builder.setMultiChoiceItems(items, states, new DialogInterface.OnMultiChoiceClickListener(){
|
|
||||||
public void onClick(DialogInterface dialog, int item, boolean state) {
|
// setMultiChoiceItems doesn't support using an adapter, so we're creating a hack with
|
||||||
// Do nothing
|
// setSingleChoiceItems and changing the choiceMode below when we create the dialog
|
||||||
}
|
builder.setSingleChoiceItems(new SimpleAdapter(
|
||||||
});
|
GeckoApp.this,
|
||||||
|
itemList,
|
||||||
|
R.layout.site_setting_item,
|
||||||
|
new String[] { "setting", "value" },
|
||||||
|
new int[] { R.id.setting, R.id.value }
|
||||||
|
), -1, new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int id) { }
|
||||||
|
});
|
||||||
|
|
||||||
builder.setPositiveButton(R.string.site_settings_clear, new DialogInterface.OnClickListener() {
|
builder.setPositiveButton(R.string.site_settings_clear, new DialogInterface.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
ListView listView = ((AlertDialog) dialog).getListView();
|
ListView listView = ((AlertDialog) dialog).getListView();
|
||||||
@ -1148,12 +1159,12 @@ abstract public class GeckoApp
|
|||||||
|
|
||||||
// An array of the indices of the permissions we want to clear
|
// An array of the indices of the permissions we want to clear
|
||||||
JSONArray permissionsToClear = new JSONArray();
|
JSONArray permissionsToClear = new JSONArray();
|
||||||
for (int i = 0; i < checkedItemPositions.size(); i++) {
|
for (int i = 0; i < checkedItemPositions.size(); i++)
|
||||||
boolean checked = checkedItemPositions.get(i);
|
if (checkedItemPositions.get(i))
|
||||||
if (checked)
|
|
||||||
permissionsToClear.put(i);
|
permissionsToClear.put(i);
|
||||||
}
|
|
||||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Permissions:Clear", permissionsToClear.toString()));
|
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(
|
||||||
|
"Permissions:Clear", permissionsToClear.toString()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1161,12 +1172,21 @@ abstract public class GeckoApp
|
|||||||
builder.setNegativeButton(R.string.site_settings_cancel, new DialogInterface.OnClickListener(){
|
builder.setNegativeButton(R.string.site_settings_cancel, new DialogInterface.OnClickListener(){
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
dialog.cancel();
|
dialog.cancel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
mMainHandler.post(new Runnable() {
|
mMainHandler.post(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
builder.create().show();
|
Dialog dialog = builder.create();
|
||||||
|
dialog.show();
|
||||||
|
|
||||||
|
ListView listView = ((AlertDialog) dialog).getListView();
|
||||||
|
if (listView != null) {
|
||||||
|
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||||
|
int listSize = listView.getAdapter().getCount();
|
||||||
|
for (int i = 0; i < listSize; i++)
|
||||||
|
listView.setItemChecked(i, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,7 @@ FENNEC_JAVA_FILES = \
|
|||||||
CameraImageResultHandler.java \
|
CameraImageResultHandler.java \
|
||||||
CameraVideoResultHandler.java \
|
CameraVideoResultHandler.java \
|
||||||
CanvasDelegate.java \
|
CanvasDelegate.java \
|
||||||
|
CheckableLinearLayout.java \
|
||||||
SyncPreference.java \
|
SyncPreference.java \
|
||||||
db/BrowserDB.java \
|
db/BrowserDB.java \
|
||||||
db/LocalBrowserDB.java \
|
db/LocalBrowserDB.java \
|
||||||
@ -364,6 +365,7 @@ RES_LAYOUT = \
|
|||||||
res/layout/notification_icon_text.xml \
|
res/layout/notification_icon_text.xml \
|
||||||
res/layout/notification_progress.xml \
|
res/layout/notification_progress.xml \
|
||||||
res/layout/notification_progress_text.xml \
|
res/layout/notification_progress_text.xml \
|
||||||
|
res/layout/site_setting_item.xml \
|
||||||
res/layout/site_setting_title.xml \
|
res/layout/site_setting_title.xml \
|
||||||
res/layout/setup_screen.xml \
|
res/layout/setup_screen.xml \
|
||||||
res/layout/shared_ui_components.xml \
|
res/layout/shared_ui_components.xml \
|
||||||
|
53
mobile/android/base/resources/layout/site_setting_item.xml
Normal file
53
mobile/android/base/resources/layout/site_setting_item.xml
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<org.mozilla.gecko.CheckableLinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingLeft="16dip"
|
||||||
|
android:paddingRight="12dip"
|
||||||
|
android:minHeight="?android:attr/listPreferredItemHeightSmall"
|
||||||
|
android:focusable="false">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:focusable="false">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/setting"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:textColor="?android:attr/textColorAlertDialogListItem"
|
||||||
|
android:gravity="center_vertical|left"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="marquee"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:textColor="?android:attr/textColorAlertDialogListItem"
|
||||||
|
android:gravity="center_vertical|left"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="marquee"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/checkbox"
|
||||||
|
android:layout_width="35dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingRight="12dip"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:focusable="false"
|
||||||
|
android:clickable="false"/>
|
||||||
|
|
||||||
|
</org.mozilla.gecko.CheckableLinearLayout>
|
@ -6011,18 +6011,15 @@ var PermissionsHelper = {
|
|||||||
"allowed" : "denied";
|
"allowed" : "denied";
|
||||||
let valueString = Strings.browser.GetStringFromName(typeStrings[valueKey]);
|
let valueString = Strings.browser.GetStringFromName(typeStrings[valueKey]);
|
||||||
|
|
||||||
// If we implement a two-line UI, we will need to pass the label and
|
|
||||||
// value individually and let java handle the formatting
|
|
||||||
let setting = Strings.browser.formatStringFromName("siteSettings.labelToValue",
|
|
||||||
[ label, valueString ], 2);
|
|
||||||
permissions.push({
|
permissions.push({
|
||||||
type: type,
|
type: type,
|
||||||
setting: setting
|
setting: label,
|
||||||
|
value: valueString
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep track of permissions, so we know which ones to clear
|
// Keep track of permissions, so we know which ones to clear
|
||||||
this._currentPermissions = permissions;
|
this._currentPermissions = permissions;
|
||||||
|
|
||||||
let host;
|
let host;
|
||||||
try {
|
try {
|
||||||
|
@ -7,6 +7,9 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <prlong.h>
|
||||||
|
#include <prprf.h>
|
||||||
|
#include <prtime.h>
|
||||||
#include "nsProfileLock.h"
|
#include "nsProfileLock.h"
|
||||||
|
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
@ -122,6 +125,8 @@ private:
|
|||||||
|
|
||||||
NS_HIDDEN_(nsresult) Init();
|
NS_HIDDEN_(nsresult) Init();
|
||||||
|
|
||||||
|
nsresult CreateTimesInternal(nsIFile *profileDir);
|
||||||
|
|
||||||
nsresult CreateProfileInternal(nsIFile* aRootDir,
|
nsresult CreateProfileInternal(nsIFile* aRootDir,
|
||||||
nsIFile* aLocalDir,
|
nsIFile* aLocalDir,
|
||||||
const nsACString& aName,
|
const nsACString& aName,
|
||||||
@ -813,6 +818,12 @@ nsToolkitProfileService::CreateProfileInternal(nsIFile* aRootDir,
|
|||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We created a new profile dir. Let's store a creation timestamp.
|
||||||
|
// Note that this code path does not apply if the profile dir was
|
||||||
|
// created prior to launching.
|
||||||
|
rv = CreateTimesInternal(rootDir);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
nsToolkitProfile* last = aForExternalApp ? nullptr : mFirst;
|
nsToolkitProfile* last = aForExternalApp ? nullptr : mFirst;
|
||||||
if (last) {
|
if (last) {
|
||||||
while (last->mNext)
|
while (last->mNext)
|
||||||
@ -827,6 +838,40 @@ nsToolkitProfileService::CreateProfileInternal(nsIFile* aRootDir,
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsToolkitProfileService::CreateTimesInternal(nsIFile* aProfileDir)
|
||||||
|
{
|
||||||
|
nsresult rv = NS_ERROR_FAILURE;
|
||||||
|
nsCOMPtr<nsIFile> creationLog;
|
||||||
|
rv = aProfileDir->Clone(getter_AddRefs(creationLog));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = creationLog->AppendNative(NS_LITERAL_CSTRING("times.json"));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
bool exists = false;
|
||||||
|
creationLog->Exists(&exists);
|
||||||
|
if (exists) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = creationLog->Create(nsIFile::NORMAL_FILE_TYPE, 0700);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
// We don't care about microsecond resolution.
|
||||||
|
PRInt64 msec;
|
||||||
|
LL_DIV(msec, PR_Now(), PR_USEC_PER_MSEC);
|
||||||
|
|
||||||
|
// Write it out.
|
||||||
|
PRFileDesc *writeFile;
|
||||||
|
rv = creationLog->OpenNSPRFileDesc(PR_WRONLY, 0700, &writeFile);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
PR_fprintf(writeFile, "{\n\"created\": %lld\n}\n", msec);
|
||||||
|
PR_Close(writeFile);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsToolkitProfileService::GetProfileCount(uint32_t *aResult)
|
nsToolkitProfileService::GetProfileCount(uint32_t *aResult)
|
||||||
{
|
{
|
||||||
|
@ -29,8 +29,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=543854
|
|||||||
const ASCIIName = "myprofile";
|
const ASCIIName = "myprofile";
|
||||||
const UnicodeName = "\u09A0\u09BE\u0995\u09C1\u09B0"; // A Bengali name
|
const UnicodeName = "\u09A0\u09BE\u0995\u09C1\u09B0"; // A Bengali name
|
||||||
|
|
||||||
|
var gIOService;
|
||||||
var gProfileService;
|
var gProfileService;
|
||||||
|
|
||||||
|
gIOService = Cc["@mozilla.org/network/io-service;1"].
|
||||||
|
getService(Ci.nsIIOService);
|
||||||
|
|
||||||
gProfileService = Cc["@mozilla.org/toolkit/profile-service;1"].
|
gProfileService = Cc["@mozilla.org/toolkit/profile-service;1"].
|
||||||
getService(Ci.nsIToolkitProfileService);
|
getService(Ci.nsIToolkitProfileService);
|
||||||
|
|
||||||
@ -38,24 +42,72 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=543854
|
|||||||
createProfile(UnicodeName);
|
createProfile(UnicodeName);
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
|
|
||||||
function createProfile(profileName)
|
/**
|
||||||
{
|
* Read the contents of an nsIFile. Throws on error.
|
||||||
var profile = gProfileService.createProfile(null, null, profileName);
|
|
||||||
|
* @param file an nsIFile instance.
|
||||||
|
* @return string contents.
|
||||||
|
*/
|
||||||
|
function readFile(file) {
|
||||||
|
let fstream = Cc["@mozilla.org/network/file-input-stream;1"].
|
||||||
|
createInstance(Ci.nsIFileInputStream);
|
||||||
|
let sstream = Cc["@mozilla.org/scriptableinputstream;1"].
|
||||||
|
createInstance(Components.interfaces.nsIScriptableInputStream);
|
||||||
|
|
||||||
|
const RO = 0x01;
|
||||||
|
const READ_OTHERS = 4;
|
||||||
|
|
||||||
|
fstream.init(file, RO, READ_OTHERS, 0);
|
||||||
|
sstream.init(fstream);
|
||||||
|
let out = sstream.read(sstream.available());
|
||||||
|
sstream.close();
|
||||||
|
fstream.close();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkBounds(lowerBound, value, upperBound) {
|
||||||
|
ok(lowerBound <= value, "value " + value +
|
||||||
|
" is above lower bound " + lowerBound);
|
||||||
|
ok(upperBound >= value, "value " + value +
|
||||||
|
" is within upper bound " + upperBound);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createProfile(profileName) {
|
||||||
|
// Filesystem precision is lower than Date precision.
|
||||||
|
let lowerBound = Date.now() - 1000;
|
||||||
|
|
||||||
|
let profile = gProfileService.createProfile(null, null, profileName);
|
||||||
|
|
||||||
// check that the directory was created
|
// check that the directory was created
|
||||||
isnot(profile, null, "Profile " + profileName + " created");
|
isnot(profile, null, "Profile " + profileName + " created");
|
||||||
|
|
||||||
var profileDir = profile.rootDir;
|
let profileDir = profile.rootDir;
|
||||||
|
|
||||||
ok(profileDir.exists(), "Profile dir created");
|
ok(profileDir.exists(), "Profile dir created");
|
||||||
ok(profileDir.isDirectory(), "Profile dir is a directory");
|
ok(profileDir.isDirectory(), "Profile dir is a directory");
|
||||||
|
|
||||||
var profileDirPath = profileDir.path;
|
let profileDirPath = profileDir.path;
|
||||||
|
|
||||||
is(profileDirPath.substr(profileDirPath.length - profileName.length),
|
is(profileDirPath.substr(profileDirPath.length - profileName.length),
|
||||||
profileName, "Profile dir has expected name");
|
profileName, "Profile dir has expected name");
|
||||||
|
|
||||||
// clean up the profile
|
// Ensure that our timestamp file was created.
|
||||||
|
let jsonFile = profileDir.clone();
|
||||||
|
jsonFile.append("times.json");
|
||||||
|
ok(jsonFile.path, "Path is " + jsonFile.path);
|
||||||
|
ok(jsonFile.exists(), "Times file was created");
|
||||||
|
ok(jsonFile.isFile(), "Times file is a file");
|
||||||
|
let json = JSON.parse(readFile(jsonFile));
|
||||||
|
|
||||||
|
let upperBound = Date.now() + 1000;
|
||||||
|
|
||||||
|
let created = json.created;
|
||||||
|
ok(created, "created is set");
|
||||||
|
|
||||||
|
// Check against real clock time.
|
||||||
|
checkBounds(lowerBound, created, upperBound);
|
||||||
|
|
||||||
|
// Clean up the profile.
|
||||||
profile.remove(true);
|
profile.remove(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user