Import Cobalt 4.12096
diff --git a/src/cobalt/base/deep_link_event.h b/src/cobalt/base/deep_link_event.h index 5e3df4b..b5c2afa 100644 --- a/src/cobalt/base/deep_link_event.h +++ b/src/cobalt/base/deep_link_event.h
@@ -20,6 +20,7 @@ #include <string> #include "base/compiler_specific.h" +#include "base/string_util.h" #include "cobalt/base/event.h" namespace base { @@ -28,6 +29,9 @@ public: explicit DeepLinkEvent(const std::string& link) : link_(link) {} const std::string& link() const { return link_; } + bool IsH5vccLink() const { + return StartsWithASCII(link_, "h5vcc", true); + } BASE_EVENT_SUBCLASS(DeepLinkEvent);
diff --git a/src/cobalt/base/user_log.h b/src/cobalt/base/user_log.h index 4c0b99f..5c47910 100644 --- a/src/cobalt/base/user_log.h +++ b/src/cobalt/base/user_log.h
@@ -32,6 +32,8 @@ kAppStatusIndex, kAppSuspendCountIndex, kAppResumeCountIndex, + kAppPauseCountIndex, + kAppUnpauseCountIndex, kNetworkStatusIndex, kNetworkConnectCountIndex, kNetworkDisconnectCountIndex,
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc index 7559d56..bde2b8f 100644 --- a/src/cobalt/browser/application.cc +++ b/src/cobalt/browser/application.cc
@@ -244,6 +244,8 @@ Application::AppStatus Application::app_status_ = Application::kUninitializedAppStatus; +int Application::app_pause_count_ = 0; +int Application::app_unpause_count_ = 0; int Application::app_suspend_count_ = 0; int Application::app_resume_count_ = 0; @@ -428,6 +430,10 @@ base::Bind(&Application::OnApplicationEvent, base::Unretained(this)); event_dispatcher_.AddEventCallback(system_window::ApplicationEvent::TypeId(), application_event_callback_); + deep_link_event_callback_ = + base::Bind(&Application::OnDeepLinkEvent, base::Unretained(this)); + event_dispatcher_.AddEventCallback(base::DeepLinkEvent::TypeId(), + deep_link_event_callback_); #if defined(ENABLE_WEBDRIVER) #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES) @@ -477,6 +483,8 @@ network_event_callback_); event_dispatcher_.RemoveEventCallback( system_window::ApplicationEvent::TypeId(), application_event_callback_); + event_dispatcher_.RemoveEventCallback( + base::DeepLinkEvent::TypeId(), deep_link_event_callback_); app_status_ = kShutDownAppStatus; } @@ -538,17 +546,34 @@ DLOG(INFO) << "Got quit event."; app_status_ = kWillQuitAppStatus; Quit(); + } else if (app_event->type() == system_window::ApplicationEvent::kPause) { + DLOG(INFO) << "Got pause event."; + app_status_ = kPausedAppStatus; + ++app_pause_count_; + } else if (app_event->type() == system_window::ApplicationEvent::kUnpause) { + DLOG(INFO) << "Got unpause event."; + app_status_ = kRunningAppStatus; + ++app_unpause_count_; } else if (app_event->type() == system_window::ApplicationEvent::kSuspend) { DLOG(INFO) << "Got suspend event."; - app_status_ = kPausedAppStatus; + app_status_ = kSuspendedAppStatus; ++app_suspend_count_; } else if (app_event->type() == system_window::ApplicationEvent::kResume) { DLOG(INFO) << "Got resume event."; - app_status_ = kRunningAppStatus; + app_status_ = kPausedAppStatus; ++app_resume_count_; } } +void Application::OnDeepLinkEvent(const base::Event* event) { + const base::DeepLinkEvent* deep_link_event = + base::polymorphic_downcast<const base::DeepLinkEvent*>(event); + // TODO: Remove this when terminal application states are properly handled. + if (deep_link_event->IsH5vccLink()) { + browser_module_->Navigate(GURL(deep_link_event->link())); + } +} + void Application::WebModuleRecreated() { #if defined(ENABLE_WEBDRIVER) if (web_driver_module_) { @@ -592,6 +617,10 @@ &lifetime_in_ms_, sizeof(lifetime_in_ms_)); base::UserLog::Register(base::UserLog::kAppStatusIndex, "AppStatus", &app_status_, sizeof(app_status_)); + base::UserLog::Register(base::UserLog::kAppPauseCountIndex, "PauseCnt", + &app_pause_count_, sizeof(app_pause_count_)); + base::UserLog::Register(base::UserLog::kAppUnpauseCountIndex, "UnpauseCnt", + &app_unpause_count_, sizeof(app_unpause_count_)); base::UserLog::Register(base::UserLog::kAppSuspendCountIndex, "SuspendCnt", &app_suspend_count_, sizeof(app_suspend_count_)); base::UserLog::Register(base::UserLog::kAppResumeCountIndex, "ResumeCnt",
diff --git a/src/cobalt/browser/application.h b/src/cobalt/browser/application.h index 3b5189a..dc4a362 100644 --- a/src/cobalt/browser/application.h +++ b/src/cobalt/browser/application.h
@@ -67,6 +67,9 @@ // Called to handle an application event. void OnApplicationEvent(const base::Event* event); + // Called to handle a deep link event. + void OnDeepLinkEvent(const base::Event* event); + // Called when a navigation occurs in the BrowserModule. void WebModuleRecreated(); @@ -88,6 +91,7 @@ base::EventCallback account_event_callback_; base::EventCallback network_event_callback_; base::EventCallback application_event_callback_; + base::EventCallback deep_link_event_callback_; // Thread checkers to ensure that callbacks for network and application events // always occur on the same thread. @@ -113,6 +117,7 @@ kUninitializedAppStatus, kRunningAppStatus, kPausedAppStatus, + kSuspendedAppStatus, kWillQuitAppStatus, kQuitAppStatus, kShutDownAppStatus, @@ -156,6 +161,8 @@ static AppStatus app_status_; static int app_suspend_count_; static int app_resume_count_; + static int app_pause_count_; + static int app_unpause_count_; static NetworkStatus network_status_; static int network_connect_count_;
diff --git a/src/cobalt/browser/browser.gyp b/src/cobalt/browser/browser.gyp index 88b34e9..1115bbd 100644 --- a/src/cobalt/browser/browser.gyp +++ b/src/cobalt/browser/browser.gyp
@@ -35,6 +35,8 @@ 'resource_provider_array_buffer_allocator.h', 'splash_screen.cc', 'splash_screen.h', + 'storage_upgrade_handler.cc', + 'storage_upgrade_handler.h', 'switches.cc', 'switches.h', 'trace_manager.cc', @@ -117,6 +119,25 @@ }, { + 'target_name': 'browser_test', + 'type': '<(gtest_target_type)', + 'sources': [ + 'storage_upgrade_handler_test.cc', + ], + 'dependencies': [ + '<(DEPTH)/base/base.gyp:run_all_unittests', + '<(DEPTH)/cobalt/base/base.gyp:base', + '<(DEPTH)/cobalt/dom/dom.gyp:dom', + '<(DEPTH)/cobalt/network/network.gyp:network', + '<(DEPTH)/cobalt/storage/storage.gyp:storage', + '<(DEPTH)/cobalt/storage/storage.gyp:storage_upgrade_copy_test_data', + '<(DEPTH)/testing/gmock.gyp:gmock', + '<(DEPTH)/testing/gtest.gyp:gtest', + 'browser', + ], + }, + + { 'target_name': 'browser_copy_test_data', 'type': 'none', 'actions': [
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc index 73de733..621c12a 100644 --- a/src/cobalt/browser/browser_module.cc +++ b/src/cobalt/browser/browser_module.cc
@@ -31,6 +31,7 @@ #include "cobalt/base/tokens.h" #include "cobalt/browser/resource_provider_array_buffer_allocator.h" #include "cobalt/browser/screen_shot_writer.h" +#include "cobalt/browser/storage_upgrade_handler.h" #include "cobalt/browser/switches.h" #include "cobalt/dom/csp_delegate_factory.h" #include "cobalt/dom/keycode.h" @@ -121,7 +122,10 @@ ALLOW_THIS_IN_INITIALIZER_LIST( weak_this_(weak_ptr_factory_.GetWeakPtr())), self_message_loop_(MessageLoop::current()), - storage_manager_(options.storage_manager_options), + storage_manager_( + scoped_ptr<StorageUpgradeHandler>(new StorageUpgradeHandler(url)) + .PassAs<storage::StorageManager::UpgradeHandler>(), + options.storage_manager_options), #if defined(OS_STARBOARD) is_rendered_(false), #endif // OS_STARBOARD
diff --git a/src/cobalt/browser/debug_console/debug_console.js b/src/cobalt/browser/debug_console/debug_console.js index 58282d5..7c2f8a5 100644 --- a/src/cobalt/browser/debug_console/debug_console.js +++ b/src/cobalt/browser/debug_console/debug_console.js
@@ -258,6 +258,7 @@ createCommandInput(); createMessageLog(); createDebuggerClient(); + showHud(false); showConsole(false); createConsoleValues(); initDebugCommands();
diff --git a/src/cobalt/browser/starboard/event_handler.cc b/src/cobalt/browser/starboard/event_handler.cc index be5e494..e244912 100644 --- a/src/cobalt/browser/starboard/event_handler.cc +++ b/src/cobalt/browser/starboard/event_handler.cc
@@ -54,12 +54,18 @@ void EventHandler::DispatchEvent(const SbEvent* starboard_event) const { // Create a Cobalt event from the Starboard event, if recognized. scoped_ptr<base::Event> cobalt_event; - if (starboard_event->type == kSbEventTypeResume) { + if (starboard_event->type == kSbEventTypePause) { cobalt_event.reset(new system_window::ApplicationEvent( - system_window::ApplicationEvent::kResume)); + system_window::ApplicationEvent::kPause)); + } else if (starboard_event->type == kSbEventTypeUnpause) { + cobalt_event.reset(new system_window::ApplicationEvent( + system_window::ApplicationEvent::kUnpause)); } else if (starboard_event->type == kSbEventTypeSuspend) { cobalt_event.reset(new system_window::ApplicationEvent( system_window::ApplicationEvent::kSuspend)); + } else if (starboard_event->type == kSbEventTypeResume) { + cobalt_event.reset(new system_window::ApplicationEvent( + system_window::ApplicationEvent::kResume)); } else if (starboard_event->type == kSbEventTypeNetworkConnect) { cobalt_event.reset( new network::NetworkEvent(network::NetworkEvent::kConnection));
diff --git a/src/cobalt/browser/storage_upgrade_handler.cc b/src/cobalt/browser/storage_upgrade_handler.cc new file mode 100644 index 0000000..b302b91 --- /dev/null +++ b/src/cobalt/browser/storage_upgrade_handler.cc
@@ -0,0 +1,79 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cobalt/browser/storage_upgrade_handler.h" + +#include <vector> + +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "cobalt/dom/local_storage_database.h" +#include "cobalt/dom/storage_area.h" +#include "cobalt/network/persistent_cookie_store.h" +#include "cobalt/storage/storage_manager.h" +#include "cobalt/storage/upgrade/upgrade_reader.h" +#include "net/cookies/canonical_cookie.h" + +namespace cobalt { +namespace browser { +namespace { +void OnCookiesLoaded(const std::vector<net::CanonicalCookie*>& cookies) { + // We don't care about the local copies of the cookies returned to us. + for (size_t i = 0; i < cookies.size(); i++) { + delete cookies[i]; + } +} +} // namespace + +StorageUpgradeHandler::StorageUpgradeHandler(const GURL& url) + : default_local_storage_id_( + dom::StorageArea::GetLocalStorageIdForUrl(url)) {} + +void StorageUpgradeHandler::OnUpgrade(storage::StorageManager* storage, + const char* data, int size) { + storage::upgrade::UpgradeReader upgrade_reader(data, size); + int num_cookies = upgrade_reader.GetNumCookies(); + int num_local_storage_entries = upgrade_reader.GetNumLocalStorageEntries(); + DLOG(INFO) << "Upgrading legacy save data: " << num_cookies << " cookies, " + << num_local_storage_entries << " local storage entries."; + + if (num_cookies > 0) { + scoped_refptr<network::PersistentCookieStore> cookie_store( + new network::PersistentCookieStore(storage)); + // Load the current cookies to ensure the database table is initialized. + cookie_store->Load(base::Bind(OnCookiesLoaded)); + for (int i = 0; i < num_cookies; i++) { + const net::CanonicalCookie* cookie = upgrade_reader.GetCookie(i); + DCHECK(cookie); + cookie_store->AddCookie(*cookie); + } + } + + if (num_local_storage_entries > 0) { + dom::LocalStorageDatabase local_storage_database(storage); + for (int i = 0; i < num_local_storage_entries; i++) { + const storage::upgrade::UpgradeReader::LocalStorageEntry* + local_storage_entry = upgrade_reader.GetLocalStorageEntry(i); + DCHECK(local_storage_entry); + local_storage_database.Write(default_local_storage_id_, + local_storage_entry->key, + local_storage_entry->value); + } + } +} + +} // namespace browser +} // namespace cobalt
diff --git a/src/cobalt/browser/storage_upgrade_handler.h b/src/cobalt/browser/storage_upgrade_handler.h new file mode 100644 index 0000000..15c8962 --- /dev/null +++ b/src/cobalt/browser/storage_upgrade_handler.h
@@ -0,0 +1,46 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COBALT_BROWSER_STORAGE_UPGRADE_HANDLER_H_ +#define COBALT_BROWSER_STORAGE_UPGRADE_HANDLER_H_ + +#include <string> + +#include "cobalt/storage/storage_manager.h" + +namespace cobalt { +namespace browser { + +// Handles save data in upgrade format. +class StorageUpgradeHandler : public storage::StorageManager::UpgradeHandler { + public: + explicit StorageUpgradeHandler(const GURL& gurl); + + void OnUpgrade(storage::StorageManager* storage, const char* data, + int size) OVERRIDE; + + const std::string& default_local_storage_id() const { + return default_local_storage_id_; + } + + private: + std::string default_local_storage_id_; +}; + +} // namespace browser +} // namespace cobalt + +#endif // COBALT_BROWSER_STORAGE_UPGRADE_HANDLER_H_
diff --git a/src/cobalt/browser/storage_upgrade_handler_test.cc b/src/cobalt/browser/storage_upgrade_handler_test.cc new file mode 100644 index 0000000..79ec4a5 --- /dev/null +++ b/src/cobalt/browser/storage_upgrade_handler_test.cc
@@ -0,0 +1,185 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <cstring> +#include <string> +#include <vector> + +#include "base/base_paths.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/path_service.h" +#include "base/platform_file.h" +#include "cobalt/browser/storage_upgrade_handler.h" +#include "cobalt/dom/local_storage_database.h" +#include "cobalt/dom/storage_area.h" +#include "cobalt/network/persistent_cookie_store.h" +#include "cobalt/storage/savegame_fake.h" +#include "cobalt/storage/storage_manager.h" +#include "cobalt/storage/upgrade/upgrade_reader.h" +#include "googleurl/src/gurl.h" +#include "net/cookies/canonical_cookie.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cobalt { +namespace browser { + +namespace { + +class CallbackWaiter { + public: + CallbackWaiter() : was_called_event_(true, false) {} + virtual ~CallbackWaiter() {} + bool TimedWait() { + return was_called_event_.TimedWait(base::TimeDelta::FromSeconds(5)); + } + + protected: + void Signal() { was_called_event_.Signal(); } + + private: + base::WaitableEvent was_called_event_; + + DISALLOW_COPY_AND_ASSIGN(CallbackWaiter); +}; + +class FlushWaiter : public CallbackWaiter { + public: + FlushWaiter() {} + void OnFlushDone() { Signal(); } + + private: + DISALLOW_COPY_AND_ASSIGN(FlushWaiter); +}; + +class CookieWaiter : public CallbackWaiter { + public: + CookieWaiter() {} + ~CookieWaiter() { + for (size_t i = 0; i < cookies_.size(); i++) { + delete cookies_[i]; + } + } + + void OnCookiesLoaded(const std::vector<net::CanonicalCookie*>& cookies) { + cookies_ = cookies; + Signal(); + } + + const std::vector<net::CanonicalCookie*>& GetCookies() const { + return cookies_; + } + + private: + std::vector<net::CanonicalCookie*> cookies_; + DISALLOW_COPY_AND_ASSIGN(CookieWaiter); +}; + +class LocalStorageEntryWaiter : public CallbackWaiter { + public: + LocalStorageEntryWaiter() {} + ~LocalStorageEntryWaiter() {} + + void OnEntriesLoaded(scoped_ptr<dom::StorageArea::StorageMap> entries) { + entries_ = entries.Pass(); + Signal(); + } + + dom::StorageArea::StorageMap* GetEntries() const { return entries_.get(); } + + private: + scoped_ptr<dom::StorageArea::StorageMap> entries_; + DISALLOW_COPY_AND_ASSIGN(LocalStorageEntryWaiter); +}; + +void ReadFileToString(const char* pathname, std::string* string_out) { + EXPECT_TRUE(pathname); + EXPECT_TRUE(string_out); + FilePath file_path; + EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &file_path)); + file_path = file_path.Append(pathname); + EXPECT_TRUE(file_util::ReadFileToString(file_path, string_out)); + const char* data = string_out->c_str(); + const int size = static_cast<int>(string_out->length()); + EXPECT_GT(size, 0); + EXPECT_LE(size, 10 * 1024 * 1024); + EXPECT_TRUE(storage::upgrade::UpgradeReader::IsUpgradeData(data, size)); +} + +int GetNumCookies(storage::StorageManager* storage) { + scoped_refptr<network::PersistentCookieStore> cookie_store( + new network::PersistentCookieStore(storage)); + CookieWaiter waiter; + cookie_store->Load( + base::Bind(&CookieWaiter::OnCookiesLoaded, base::Unretained(&waiter))); + EXPECT_EQ(true, waiter.TimedWait()); + return static_cast<int>(waiter.GetCookies().size()); +} + +int GetNumLocalStorageEntries(storage::StorageManager* storage, + const std::string& identifier) { + dom::LocalStorageDatabase local_storage_database(storage); + LocalStorageEntryWaiter waiter; + local_storage_database.ReadAll( + identifier, base::Bind(&LocalStorageEntryWaiter::OnEntriesLoaded, + base::Unretained(&waiter))); + EXPECT_EQ(true, waiter.TimedWait()); + return static_cast<int>(waiter.GetEntries()->size()); +} + +} // namespace + +TEST(StorageUpgradeHandlerTest, UpgradeFullData) { + MessageLoop message_loop_(MessageLoop::TYPE_DEFAULT); + std::string file_contents; + ReadFileToString("cobalt/storage/upgrade/testdata/full_data_v1.json", + &file_contents); + StorageUpgradeHandler* upgrade_handler = + new StorageUpgradeHandler(GURL("https://www.youtube.com")); + storage::StorageManager::Options options; + options.savegame_options.delete_on_destruction = true; + options.savegame_options.factory = &storage::SavegameFake::Create; + storage::StorageManager storage( + scoped_ptr<storage::StorageManager::UpgradeHandler>(upgrade_handler), + options); + + // Our storage should be empty at this point. + EXPECT_EQ(GetNumCookies(&storage), 0); + EXPECT_EQ(GetNumLocalStorageEntries( + &storage, upgrade_handler->default_local_storage_id()), + 0); + + upgrade_handler->OnUpgrade(&storage, file_contents.c_str(), + static_cast<int>(file_contents.length())); + + FlushWaiter waiter; + storage.FlushNow( + base::Bind(&FlushWaiter::OnFlushDone, base::Unretained(&waiter))); + EXPECT_EQ(true, waiter.TimedWait()); + + // We should now have 2 cookies and 2 local storage entries. + EXPECT_EQ(GetNumCookies(&storage), 2); + EXPECT_EQ(GetNumLocalStorageEntries( + &storage, upgrade_handler->default_local_storage_id()), + 2); + + message_loop_.RunUntilIdle(); +} + +} // namespace browser +} // namespace cobalt
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id index 70cae86..e3f28c7 100644 --- a/src/cobalt/build/build.id +++ b/src/cobalt/build/build.id
@@ -1 +1 @@ -11837 \ No newline at end of file +12096 \ No newline at end of file
diff --git a/src/cobalt/cssom/computed_style.cc b/src/cobalt/cssom/computed_style.cc index 9d5d0c9..c2c1a6e 100644 --- a/src/cobalt/cssom/computed_style.cc +++ b/src/cobalt/cssom/computed_style.cc
@@ -1688,6 +1688,13 @@ absolute_url = url_value->Resolve(base_url_); } + if (!absolute_url.is_valid()) { + DLOG(WARNING) << "Invalid url: " << absolute_url.spec(); + // No further process is needed if the url is invalid. + computed_background_image_ = KeywordValue::GetNone(); + return; + } + computed_background_image_ = new AbsoluteURLValue(absolute_url); }
diff --git a/src/cobalt/dom/local_storage_database_test.cc b/src/cobalt/dom/local_storage_database_test.cc index 55963ec..6f4352c 100644 --- a/src/cobalt/dom/local_storage_database_test.cc +++ b/src/cobalt/dom/local_storage_database_test.cc
@@ -83,17 +83,25 @@ return test_path.Append("local_storage_database_test.bin").value(); } +class DummyUpgradeHandler : public storage::StorageManager::UpgradeHandler { + void OnUpgrade(storage::StorageManager* /*storage*/, const char* /*data*/, + int /*size*/) OVERRIDE {} +}; + class LocalStorageDatabaseTest : public ::testing::Test { protected: LocalStorageDatabaseTest() : message_loop_(MessageLoop::TYPE_DEFAULT), host_("https://www.example.com") { + scoped_ptr<storage::StorageManager::UpgradeHandler> upgrade_handler( + new DummyUpgradeHandler()); storage::StorageManager::Options options; options.savegame_options.path_override = GetSavePath(); options.savegame_options.delete_on_destruction = true; options.savegame_options.factory = &storage::SavegameFake::Create; - storage_manager_.reset(new storage::StorageManager(options)); + storage_manager_.reset( + new storage::StorageManager(upgrade_handler.Pass(), options)); db_.reset(new LocalStorageDatabase(storage_manager_.get())); }
diff --git a/src/cobalt/dom/storage_area.cc b/src/cobalt/dom/storage_area.cc index 775395f..c5e13f8 100644 --- a/src/cobalt/dom/storage_area.cc +++ b/src/cobalt/dom/storage_area.cc
@@ -20,7 +20,6 @@ #include "base/stringprintf.h" #include "cobalt/dom/local_storage_database.h" #include "cobalt/dom/storage.h" -#include "googleurl/src/gurl.h" namespace cobalt { namespace dom { @@ -168,5 +167,10 @@ read_event_.Signal(); } +// static +std::string StorageArea::GetLocalStorageIdForUrl(const GURL& url) { + return OriginToDatabaseIdentifier(url.GetOrigin()) + kLocalStorageSuffix; +} + } // namespace dom } // namespace cobalt
diff --git a/src/cobalt/dom/storage_area.h b/src/cobalt/dom/storage_area.h index c85bf67..634215f 100644 --- a/src/cobalt/dom/storage_area.h +++ b/src/cobalt/dom/storage_area.h
@@ -23,6 +23,7 @@ #include "base/memory/scoped_ptr.h" #include "base/optional.h" #include "base/synchronization/waitable_event.h" +#include "googleurl/src/gurl.h" namespace cobalt { namespace dom { @@ -54,6 +55,8 @@ Storage* storage_node() const { return storage_node_; } const std::string& identifier() const { return identifier_; } + static std::string GetLocalStorageIdForUrl(const GURL& url); + private: void Init(); void OnInitComplete(scoped_ptr<StorageMap> data);
diff --git a/src/cobalt/dom_parser/libxml_html_parser_wrapper.cc b/src/cobalt/dom_parser/libxml_html_parser_wrapper.cc index 75e760b..968dbe5 100644 --- a/src/cobalt/dom_parser/libxml_html_parser_wrapper.cc +++ b/src/cobalt/dom_parser/libxml_html_parser_wrapper.cc
@@ -110,7 +110,7 @@ return; } - if (CheckInputAndUpdateSeverity(data, size) >= kError) { + if (CheckInputAndUpdateSeverity(data, size) == kFatal) { return; }
diff --git a/src/cobalt/dom_parser/libxml_parser_wrapper.cc b/src/cobalt/dom_parser/libxml_parser_wrapper.cc index 67521c2..5e331f7 100644 --- a/src/cobalt/dom_parser/libxml_parser_wrapper.cc +++ b/src/cobalt/dom_parser/libxml_parser_wrapper.cc
@@ -219,12 +219,15 @@ void LibxmlParserWrapper::OnParsingIssue(IssueSeverity severity, const std::string& message) { + DCHECK(severity >= kWarning && severity <= kFatal); if (severity > max_severity_) { max_severity_ = severity; } if (severity < LibxmlParserWrapper::kFatal) { - LOG(WARNING) << message; + LOG(WARNING) << "Libxml " + << (severity == kWarning ? "Warning: " : "Error: ") << message; } else { + LOG(ERROR) << "Libxml Fatal Error: " << message; if (!error_callback_.is_null()) { error_callback_.Run(message); } @@ -238,23 +241,23 @@ LibxmlParserWrapper::IssueSeverity LibxmlParserWrapper::CheckInputAndUpdateSeverity(const char* data, size_t size) { - if (max_severity_ >= kError) { + if (max_severity_ == kFatal) { return max_severity_; } // Check the total input size. total_input_size_ += size; if (total_input_size_ > kMaxTotalInputSize) { - static const char kErrorTooLong[] = "Parser input is too long."; - OnParsingIssue(kError, kErrorTooLong); + static const char kMessageInputTooLong[] = "Parser input is too long."; + OnParsingIssue(kFatal, kMessageInputTooLong); return max_severity_; } // Check the encoding of the input. if (!IsStringUTF8(std::string(data, size))) { - static const char kErrorNotUTF8[] = + static const char kMessageInputNotUTF8[] = "Parser input contains non-UTF8 characters."; - OnParsingIssue(kError, kErrorNotUTF8); + OnParsingIssue(kFatal, kMessageInputNotUTF8); return max_severity_; }
diff --git a/src/cobalt/dom_parser/libxml_parser_wrapper.h b/src/cobalt/dom_parser/libxml_parser_wrapper.h index bbeb21a..98d210e 100644 --- a/src/cobalt/dom_parser/libxml_parser_wrapper.h +++ b/src/cobalt/dom_parser/libxml_parser_wrapper.h
@@ -61,9 +61,9 @@ public: enum IssueSeverity { kNoIssue, - kWarning, - kError, - kFatal, + kWarning, // A simple warning + kError, // A recoverable error + kFatal, // A fatal error kIssueSeverityCount, }; @@ -137,8 +137,9 @@ } private: - // Maximum total input size, 1MB. - static const size_t kMaxTotalInputSize = 1 * 1024 * 1024; + // Maximum total input size, as specified in Libxml's value + // XML_MAX_TEXT_LENGTH in parserInternals.h. + static const size_t kMaxTotalInputSize = 10000000; const scoped_refptr<dom::Document> document_; const scoped_refptr<dom::Node> parent_node_;
diff --git a/src/cobalt/dom_parser/libxml_xml_parser_wrapper.cc b/src/cobalt/dom_parser/libxml_xml_parser_wrapper.cc index c933f5e..787970d 100644 --- a/src/cobalt/dom_parser/libxml_xml_parser_wrapper.cc +++ b/src/cobalt/dom_parser/libxml_xml_parser_wrapper.cc
@@ -79,7 +79,7 @@ return; } - if (CheckInputAndUpdateSeverity(data, size) >= kError) { + if (CheckInputAndUpdateSeverity(data, size) == kFatal) { return; }
diff --git a/src/cobalt/h5vcc/h5vcc_runtime.cc b/src/cobalt/h5vcc/h5vcc_runtime.cc index 8b0f871..43d71cc 100644 --- a/src/cobalt/h5vcc/h5vcc_runtime.cc +++ b/src/cobalt/h5vcc/h5vcc_runtime.cc
@@ -14,9 +14,10 @@ * limitations under the License. */ +#include "cobalt/h5vcc/h5vcc_runtime.h" + #include "cobalt/base/deep_link_event.h" #include "cobalt/base/polymorphic_downcast.h" -#include "cobalt/h5vcc/h5vcc_runtime.h" #include "cobalt/system_window/application_event.h" namespace cobalt { @@ -67,11 +68,11 @@ void H5vccRuntime::OnApplicationEvent(const base::Event* event) { const system_window::ApplicationEvent* app_event = base::polymorphic_downcast<const system_window::ApplicationEvent*>(event); - if (app_event->type() == system_window::ApplicationEvent::kSuspend) { + if (app_event->type() == system_window::ApplicationEvent::kPause) { DLOG(INFO) << "Got pause event."; on_pause()->DispatchEvent(); - } else if (app_event->type() == system_window::ApplicationEvent::kResume) { - DLOG(INFO) << "Got resume event."; + } else if (app_event->type() == system_window::ApplicationEvent::kUnpause) { + DLOG(INFO) << "Got unpause event."; on_resume()->DispatchEvent(); } } @@ -79,8 +80,10 @@ void H5vccRuntime::OnDeepLinkEvent(const base::Event* event) { const base::DeepLinkEvent* deep_link_event = base::polymorphic_downcast<const base::DeepLinkEvent*>(event); - DLOG(INFO) << "Got deep link event: " << deep_link_event->link(); - on_deep_link()->DispatchEvent(deep_link_event->link()); + if (!deep_link_event->IsH5vccLink()) { + DLOG(INFO) << "Got deep link event: " << deep_link_event->link(); + on_deep_link()->DispatchEvent(deep_link_event->link()); + } } } // namespace h5vcc } // namespace cobalt
diff --git a/src/cobalt/network/persistent_cookie_store_test.cc b/src/cobalt/network/persistent_cookie_store_test.cc index 7d2762b..77285d0 100644 --- a/src/cobalt/network/persistent_cookie_store_test.cc +++ b/src/cobalt/network/persistent_cookie_store_test.cc
@@ -120,6 +120,11 @@ DISALLOW_COPY_AND_ASSIGN(CookieVerifier); }; +class DummyUpgradeHandler : public storage::StorageManager::UpgradeHandler { + void OnUpgrade(storage::StorageManager* /*storage*/, const char* /*data*/, + int /*size*/) OVERRIDE {} +}; + std::string GetSavePath() { FilePath test_path; CHECK(PathService::Get(paths::DIR_COBALT_TEST_OUT, &test_path)); @@ -129,12 +134,15 @@ class PersistentCookieStoreTest : public ::testing::Test { protected: PersistentCookieStoreTest() : message_loop_(MessageLoop::TYPE_DEFAULT) { + scoped_ptr<storage::StorageManager::UpgradeHandler> upgrade_handler( + new DummyUpgradeHandler()); storage::StorageManager::Options options; options.savegame_options.path_override = GetSavePath(); options.savegame_options.delete_on_destruction = true; options.savegame_options.factory = &storage::SavegameFake::Create; - storage_manager_.reset(new storage::StorageManager(options)); + storage_manager_.reset( + new storage::StorageManager(upgrade_handler.Pass(), options)); cookie_store_ = new PersistentCookieStore(storage_manager_.get()); }
diff --git a/src/cobalt/renderer/backend/blitter/graphics_context.cc b/src/cobalt/renderer/backend/blitter/graphics_context.cc index b6e4210..3e9613f 100644 --- a/src/cobalt/renderer/backend/blitter/graphics_context.cc +++ b/src/cobalt/renderer/backend/blitter/graphics_context.cc
@@ -75,6 +75,12 @@ return pixels.Pass(); } +void GraphicsContextBlitter::Finish() { + // Note: flushing the context doesn't actually guarantee that drawing has + // finished. + SbBlitterFlushContext(context_); +} + } // namespace backend } // namespace renderer } // namespace cobalt
diff --git a/src/cobalt/renderer/backend/blitter/graphics_context.h b/src/cobalt/renderer/backend/blitter/graphics_context.h index 5c15184..5fc3330 100644 --- a/src/cobalt/renderer/backend/blitter/graphics_context.h +++ b/src/cobalt/renderer/backend/blitter/graphics_context.h
@@ -38,6 +38,7 @@ const math::Size& dimensions) OVERRIDE; scoped_array<uint8_t> DownloadPixelDataAsRGBA( const scoped_refptr<RenderTarget>& render_target) OVERRIDE; + void Finish() OVERRIDE; SbBlitterContext GetSbBlitterContext() const { return context_; } SbBlitterDevice GetSbBlitterDevice() const { return device_; }
diff --git a/src/cobalt/renderer/backend/egl/graphics_context.cc b/src/cobalt/renderer/backend/egl/graphics_context.cc index 3482472..2f39ddb 100644 --- a/src/cobalt/renderer/backend/egl/graphics_context.cc +++ b/src/cobalt/renderer/backend/egl/graphics_context.cc
@@ -211,6 +211,7 @@ GraphicsContextEGL::~GraphicsContextEGL() { MakeCurrent(); + GL_CALL(glFinish()); GL_CALL(glDeleteBuffers(1, &blit_vertex_buffer_)); GL_CALL(glDeleteProgram(blit_program_)); GL_CALL(glDeleteShader(blit_fragment_shader_)); @@ -350,6 +351,11 @@ return pixels.Pass(); } +void GraphicsContextEGL::Finish() { + ScopedMakeCurrent scoped_current_context(this); + GL_CALL(glFinish()); +} + void GraphicsContextEGL::Blit(GLuint texture, int x, int y, int width, int height) { // Render a texture to the specified output rectangle on the render target.
diff --git a/src/cobalt/renderer/backend/egl/graphics_context.h b/src/cobalt/renderer/backend/egl/graphics_context.h index 57d77aa..c69ed50 100644 --- a/src/cobalt/renderer/backend/egl/graphics_context.h +++ b/src/cobalt/renderer/backend/egl/graphics_context.h
@@ -58,6 +58,8 @@ scoped_array<uint8_t> DownloadPixelDataAsRGBA( const scoped_refptr<RenderTarget>& render_target) OVERRIDE; + void Finish() OVERRIDE; + // Helper class to allow one to create a RAII object that will acquire the // current context upon construction and release it upon destruction. class ScopedMakeCurrent { @@ -106,6 +108,13 @@ void Blit(GLuint texture, int x, int y, int width, int height); + bool ReadPixelsNeedVerticalFlip() { + if (!read_pixels_needs_vertical_flip_) { + read_pixels_needs_vertical_flip_ = ComputeReadPixelsNeedVerticalFlip(); + } + return *read_pixels_needs_vertical_flip_; + } + private: // Performs a test to determine if the pixel data returned by glReadPixels // needs to be vertically flipped or not. This test is expensive and so
diff --git a/src/cobalt/renderer/backend/graphics_context.h b/src/cobalt/renderer/backend/graphics_context.h index e5743f5..bd51d53 100644 --- a/src/cobalt/renderer/backend/graphics_context.h +++ b/src/cobalt/renderer/backend/graphics_context.h
@@ -62,6 +62,9 @@ virtual scoped_array<uint8_t> DownloadPixelDataAsRGBA( const scoped_refptr<RenderTarget>& render_target) = 0; + // Waits until all drawing is finished. + virtual void Finish() = 0; + private: GraphicsSystem* system_; };
diff --git a/src/cobalt/renderer/backend/graphics_context_stub.h b/src/cobalt/renderer/backend/graphics_context_stub.h index 5cb36bd..b2e0f1b 100644 --- a/src/cobalt/renderer/backend/graphics_context_stub.h +++ b/src/cobalt/renderer/backend/graphics_context_stub.h
@@ -45,6 +45,8 @@ return scoped_array<uint8_t>( new uint8_t[render_target->GetSize().GetArea() * 4]); } + + void Finish() OVERRIDE {} }; } // namespace backend
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/shaders.gypi b/src/cobalt/renderer/glimp_shaders/glsl/shaders.gypi index b0e8820..7500fc3 100644 --- a/src/cobalt/renderer/glimp_shaders/glsl/shaders.gypi +++ b/src/cobalt/renderer/glimp_shaders/glsl/shaders.gypi
@@ -64,6 +64,7 @@ 'vertex_skia_antialiased_color_only.glsl', 'vertex_skia_antialiased_oval.glsl', 'vertex_skia_color_only.glsl', + 'vertex_mesh.glsl', 'vertex_skia_texcoords_and_color.glsl', 'vertex_skia_texcoords_and_color_with_texcoord_matrix.glsl', 'vertex_skia_texcoords_derived_from_position.glsl',
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/vertex_mesh.glsl b/src/cobalt/renderer/glimp_shaders/glsl/vertex_mesh.glsl new file mode 100644 index 0000000..cce9ab8 --- /dev/null +++ b/src/cobalt/renderer/glimp_shaders/glsl/vertex_mesh.glsl
@@ -0,0 +1,8 @@ +attribute vec3 a_position; +attribute vec2 a_tex_coord; +varying vec2 v_tex_coord; +uniform mat4 u_mvp_matrix; +void main() { + gl_Position = u_mvp_matrix * vec4(a_position.xyz, 1.0); + v_tex_coord = a_tex_coord; +}
diff --git a/src/cobalt/renderer/rasterizer/benchmark.cc b/src/cobalt/renderer/rasterizer/benchmark.cc index 097d0bf..5507cb7 100644 --- a/src/cobalt/renderer/rasterizer/benchmark.cc +++ b/src/cobalt/renderer/rasterizer/benchmark.cc
@@ -121,6 +121,7 @@ // Submit the render tree to be rendered. rasterizer->Submit(animated, test_surface); + graphics_context->Finish(); if (i == 0) { // Enable tracing again after one iteration has passed and any lazy @@ -183,6 +184,10 @@ CreateDefaultRasterizer(graphics_context.get()); ResourceProvider* resource_provider = rasterizer->GetResourceProvider(); + if (!resource_provider->AlphaFormatSupported(alpha_format)) { + // Only run the test if the alpha format is supported. + return; + } const int kIterationCount = 20; const Size kImageSize(400, 400);
diff --git a/src/cobalt/renderer/rasterizer/skia/font.cc b/src/cobalt/renderer/rasterizer/skia/font.cc index 06bbb20..7f357b9 100644 --- a/src/cobalt/renderer/rasterizer/skia/font.cc +++ b/src/cobalt/renderer/rasterizer/skia/font.cc
@@ -117,7 +117,8 @@ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); SkRect skia_bounds; - float width = paint.measureText(&glyph, 2, &skia_bounds); + float width = + paint.measureText(&glyph, sizeof(render_tree::GlyphIndex), &skia_bounds); // Both cache and return the glyph's bounds. if (glyph < kPrimaryPageSize) {
diff --git a/src/cobalt/script/mozjs/mozjs_global_environment.cc b/src/cobalt/script/mozjs/mozjs_global_environment.cc index 9a79991..1cf31fd 100644 --- a/src/cobalt/script/mozjs/mozjs_global_environment.cc +++ b/src/cobalt/script/mozjs/mozjs_global_environment.cc
@@ -426,7 +426,9 @@ if (global_object_proxy && global_object_proxy->last_error_message_) { *(global_object_proxy->last_error_message_) = error_message; } else { - DLOG(ERROR) << "JS Error: " << error_message; + const char *filename = report->filename ? report->filename : "(none)"; + LOG(ERROR) << "JS Error: " << filename << ":" << report->lineno << ":" + << report->column << ": " << error_message; } }
diff --git a/src/cobalt/speech/audio_encoder_flac.cc b/src/cobalt/speech/audio_encoder_flac.cc new file mode 100644 index 0000000..d4403e1 --- /dev/null +++ b/src/cobalt/speech/audio_encoder_flac.cc
@@ -0,0 +1,125 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cobalt/speech/audio_encoder_flac.h" + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/string_number_conversions.h" + +namespace cobalt { +namespace speech { + +namespace { +const char kContentTypeFLAC[] = "audio/x-flac; rate="; +const int kFLACCompressionLevel = 0; // 0 for speed +const int kBitsPerSample = 16; +const float kMaxInt16AsFloat32 = 32767.0f; +} // namespace + +AudioEncoderFlac::AudioEncoderFlac(int sample_rate) + : encoder_(FLAC__stream_encoder_new()) { + DCHECK(encoder_); + + // Set the number of channels to be encoded. + FLAC__stream_encoder_set_channels(encoder_, 1); + // Set the sample resolution of the input to be encoded. + FLAC__stream_encoder_set_bits_per_sample(encoder_, kBitsPerSample); + // Set the sample rate (in Hz) of the input to be encoded. + FLAC__stream_encoder_set_sample_rate(encoder_, + static_cast<uint32>(sample_rate)); + // Set the compression level. A higher level usually means more computation + // but higher compression. + FLAC__stream_encoder_set_compression_level(encoder_, kFLACCompressionLevel); + + // Initialize the encoder instance to encode native FLAC stream. + FLAC__StreamEncoderInitStatus encoder_status = + FLAC__stream_encoder_init_stream(encoder_, WriteCallback, NULL, NULL, + NULL, this); + DCHECK_EQ(encoder_status, FLAC__STREAM_ENCODER_INIT_STATUS_OK); +} + +AudioEncoderFlac::~AudioEncoderFlac() { + DCHECK(thread_checker_.CalledOnValidThread()); + FLAC__stream_encoder_delete(encoder_); +} + +void AudioEncoderFlac::Encode(const AudioBus* audio_bus) { + DCHECK(thread_checker_.CalledOnValidThread()); + + DCHECK_EQ(audio_bus->channels(), 1); + const float* audio_data = audio_bus->channel(0); + uint32 frames = static_cast<uint32>(audio_bus->frames()); + scoped_array<FLAC__int32> flac_samples(new FLAC__int32[frames]); + for (uint32 i = 0; i < frames; ++i) { + flac_samples[i] = + static_cast<FLAC__int32>(audio_data[i] * kMaxInt16AsFloat32); + } + + FLAC__int32* flac_samples_ptr = flac_samples.get(); + // Submit data for encoding. + FLAC__bool success = + FLAC__stream_encoder_process(encoder_, &flac_samples_ptr, frames); + DCHECK(success); +} + +void AudioEncoderFlac::Finish() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Finish the encoding. It causes the encoder to encode any data still in + // its input pipe, and finally reset the encoder to the unintialized state. + FLAC__stream_encoder_finish(encoder_); +} + +std::string AudioEncoderFlac::GetMimeType() const { + DCHECK(thread_checker_.CalledOnValidThread()); + + return std::string(kContentTypeFLAC) + + base::UintToString(FLAC__stream_encoder_get_sample_rate(encoder_)); +} + +std::string AudioEncoderFlac::GetAndClearAvailableEncodedData() { + DCHECK(thread_checker_.CalledOnValidThread()); + + std::string result = encoded_data_; + encoded_data_.clear(); + return result; +} + +// A write callback which will be called anytime there is a raw encoded data to +// write. The call to FLAC__stream_encoder_init_stream() currently will also +// immediately call the write callback several times, once with the FLAC +// signature, and once for each encoded metadata block. +FLAC__StreamEncoderWriteStatus AudioEncoderFlac::WriteCallback( + const FLAC__StreamEncoder* encoder, const FLAC__byte buffer[], size_t bytes, + unsigned int samples, unsigned int current_frame, void* client_data) { + UNREFERENCED_PARAMETER(encoder); + UNREFERENCED_PARAMETER(samples); + UNREFERENCED_PARAMETER(current_frame); + + AudioEncoderFlac* audio_encoder = + reinterpret_cast<AudioEncoderFlac*>(client_data); + DCHECK(audio_encoder); + DCHECK(audio_encoder->thread_checker_.CalledOnValidThread()); + + audio_encoder->encoded_data_.append(reinterpret_cast<const char*>(buffer), + bytes); + + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; +} + +} // namespace speech +} // namespace cobalt
diff --git a/src/cobalt/speech/audio_encoder_flac.h b/src/cobalt/speech/audio_encoder_flac.h new file mode 100644 index 0000000..be993ca --- /dev/null +++ b/src/cobalt/speech/audio_encoder_flac.h
@@ -0,0 +1,66 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COBALT_SPEECH_AUDIO_ENCODER_FLAC_H_ +#define COBALT_SPEECH_AUDIO_ENCODER_FLAC_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/threading/thread_checker.h" +#include "media/base/audio_bus.h" +#include "third_party/flac/include/FLAC/stream_encoder.h" + +namespace cobalt { +namespace speech { + +// Encode raw audio to using FLAC codec. +class AudioEncoderFlac { + public: + typedef ::media::AudioBus AudioBus; + + explicit AudioEncoderFlac(int sample_rate); + ~AudioEncoderFlac(); + + // Encode raw audio data. + void Encode(const AudioBus* audio_bus); + // Finish encoding. + void Finish(); + + // Returns mime Type of audio data. + std::string GetMimeType() const; + // Returns and clears the available encoded audio data. + std::string GetAndClearAvailableEncodedData(); + + private: + // FLAC encoder callback. + static FLAC__StreamEncoderWriteStatus WriteCallback( + const FLAC__StreamEncoder* encoder, const FLAC__byte buffer[], + size_t bytes, unsigned int samples, unsigned int current_frame, + void* client_data); + + base::ThreadChecker thread_checker_; + // FLAC encoder. + FLAC__StreamEncoder* encoder_; + // Cached encoded data. + std::string encoded_data_; +}; + +} // namespace speech +} // namespace cobalt + +#endif // COBALT_SPEECH_AUDIO_ENCODER_FLAC_H_
diff --git a/src/cobalt/speech/speech.gyp b/src/cobalt/speech/speech.gyp index 83960c6..5a43669 100644 --- a/src/cobalt/speech/speech.gyp +++ b/src/cobalt/speech/speech.gyp
@@ -21,6 +21,8 @@ 'target_name': 'speech', 'type': 'static_library', 'sources': [ + 'audio_encoder_flac.cc', + 'audio_encoder_flac.h', 'mic.h', 'speech_recognition.cc', 'speech_recognition.h', @@ -43,6 +45,7 @@ 'dependencies': [ '<(DEPTH)/cobalt/base/base.gyp:base', '<(DEPTH)/cobalt/dom/dom.gyp:dom', + '<(DEPTH)/third_party/flac/flac.gyp:libflac', ], 'conditions': [ ['OS=="starboard"', {
diff --git a/src/cobalt/speech/speech_recognition.cc b/src/cobalt/speech/speech_recognition.cc index 90383ae..e1155d6 100644 --- a/src/cobalt/speech/speech_recognition.cc +++ b/src/cobalt/speech/speech_recognition.cc
@@ -28,7 +28,10 @@ SpeechRecognition::SpeechRecognition(script::EnvironmentSettings* settings) : ALLOW_THIS_IN_INITIALIZER_LIST( manager_(base::polymorphic_downcast<dom::DOMSettings*>(settings) - ->fetcher_factory())), + ->fetcher_factory() + ->network_module(), + base::Bind(&SpeechRecognition::OnEventAvailable, + base::Unretained(this)))), config_("" /*lang*/, false /*continuous*/, false /*interim_results*/, 1 /*max alternatives*/) {} @@ -38,5 +41,10 @@ void SpeechRecognition::Abort() { NOTIMPLEMENTED(); } +bool SpeechRecognition::OnEventAvailable( + const scoped_refptr<dom::Event>& event) { + return DispatchEvent(event); +} + } // namespace speech } // namespace cobalt
diff --git a/src/cobalt/speech/speech_recognition.h b/src/cobalt/speech/speech_recognition.h index 4622a9c..9a24a85 100644 --- a/src/cobalt/speech/speech_recognition.h +++ b/src/cobalt/speech/speech_recognition.h
@@ -107,6 +107,9 @@ private: ~SpeechRecognition() OVERRIDE {} + // Callback from recognition manager. + bool OnEventAvailable(const scoped_refptr<dom::Event>& event); + // Handles main operations of speech recognition including audio encoding, // mic audio retrieving and audio data recognizing. SpeechRecognitionManager manager_;
diff --git a/src/cobalt/speech/speech_recognition_manager.cc b/src/cobalt/speech/speech_recognition_manager.cc index 61b50c7..6f6966e 100644 --- a/src/cobalt/speech/speech_recognition_manager.cc +++ b/src/cobalt/speech/speech_recognition_manager.cc
@@ -23,20 +23,27 @@ namespace { const int kSampleRate = 16000; +const float kAudioPacketDurationInSeconds = 0.1f; } // namespace SpeechRecognitionManager::SpeechRecognitionManager( - loader::FetcherFactory* fetcher_factory) + network::NetworkModule* network_module, const EventCallback& event_callback) : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)), weak_this_(weak_ptr_factory_.GetWeakPtr()), main_message_loop_(base::MessageLoopProxy::current()), - recognizer_(fetcher_factory), + event_callback_(event_callback), + ALLOW_THIS_IN_INITIALIZER_LIST( + recognizer_(network_module, + base::Bind(&SpeechRecognitionManager::OnRecognizerResult, + base::Unretained(this)), + base::Bind(&SpeechRecognitionManager::OnRecognizerError, + base::Unretained(this)))), ALLOW_THIS_IN_INITIALIZER_LIST(mic_(Mic::Create( kSampleRate, base::Bind(&SpeechRecognitionManager::OnDataReceived, base::Unretained(this)), base::Bind(&SpeechRecognitionManager::OnDataCompletion, base::Unretained(this)), - base::Bind(&SpeechRecognitionManager::OnError, + base::Bind(&SpeechRecognitionManager::OnMicError, base::Unretained(this))))) {} SpeechRecognitionManager::~SpeechRecognitionManager() { Stop(); } @@ -44,7 +51,7 @@ void SpeechRecognitionManager::Start(const SpeechRecognitionConfig& config) { DCHECK(main_message_loop_->BelongsToCurrentThread()); - recognizer_.Start(config); + recognizer_.Start(config, kSampleRate); mic_->Start(); } @@ -64,9 +71,7 @@ return; } - // TODO: Encode audio data, and then send it to recognizer. After - // receiving the recognition result from recognizer, fire a speech recognition - // event. + recognizer_.RecognizeAudio(audio_bus.Pass(), false); } void SpeechRecognitionManager::OnDataCompletion() { @@ -78,19 +83,56 @@ return; } - // TODO: Handle the case that no audio data would be received - // afterwards. + // The encoder requires a non-empty final buffer, so encoding a packet of + // silence at the end in case encoder had no data already. + size_t dummy_frames = + static_cast<size_t>(kSampleRate * kAudioPacketDurationInSeconds); + scoped_ptr<AudioBus> dummy_audio_bus = + AudioBus::Create(1, static_cast<int>(dummy_frames)); + memset(dummy_audio_bus->channel(0), 0, dummy_frames); + recognizer_.RecognizeAudio(dummy_audio_bus.Pass(), true); } -void SpeechRecognitionManager::OnError() { +void SpeechRecognitionManager::OnRecognizerResult( + const scoped_refptr<SpeechRecognitionEvent>& event) { if (!main_message_loop_->BelongsToCurrentThread()) { - // Called from mic thread. + // Called from recognizer thread. main_message_loop_->PostTask( - FROM_HERE, base::Bind(&SpeechRecognitionManager::OnError, weak_this_)); + FROM_HERE, base::Bind(&SpeechRecognitionManager::OnRecognizerResult, + weak_this_, event)); return; } - // TODO: Handle the case that an error occurred. + event_callback_.Run(event); +} + +void SpeechRecognitionManager::OnRecognizerError() { + if (!main_message_loop_->BelongsToCurrentThread()) { + // Called from recognizer thread. + main_message_loop_->PostTask( + FROM_HERE, + base::Bind(&SpeechRecognitionManager::OnRecognizerError, weak_this_)); + return; + } + + // TODO: Could be other error types based on the recognizer response. + event_callback_.Run( + scoped_refptr<SpeechRecognitionError>(new SpeechRecognitionError( + SpeechRecognitionError::kNetwork, "Recognition Failed."))); +} + +void SpeechRecognitionManager::OnMicError() { + if (!main_message_loop_->BelongsToCurrentThread()) { + // Called from mic thread. + main_message_loop_->PostTask( + FROM_HERE, + base::Bind(&SpeechRecognitionManager::OnMicError, weak_this_)); + return; + } + + event_callback_.Run( + scoped_refptr<SpeechRecognitionError>(new SpeechRecognitionError( + SpeechRecognitionError::kAborted, "Mic Disconnected."))); } } // namespace speech
diff --git a/src/cobalt/speech/speech_recognition_manager.h b/src/cobalt/speech/speech_recognition_manager.h index 022d82c..dda56b2 100644 --- a/src/cobalt/speech/speech_recognition_manager.h +++ b/src/cobalt/speech/speech_recognition_manager.h
@@ -19,9 +19,11 @@ #include <string> -#include "cobalt/loader/fetcher_factory.h" +#include "cobalt/network/network_module.h" #include "cobalt/speech/mic.h" #include "cobalt/speech/speech_recognition_config.h" +#include "cobalt/speech/speech_recognition_error.h" +#include "cobalt/speech/speech_recognition_event.h" #include "cobalt/speech/speech_recognizer.h" namespace cobalt { @@ -35,8 +37,10 @@ class SpeechRecognitionManager { public: typedef ::media::AudioBus AudioBus; + typedef base::Callback<bool(const scoped_refptr<dom::Event>&)> EventCallback; - explicit SpeechRecognitionManager(loader::FetcherFactory* fetcher_factory); + SpeechRecognitionManager(network::NetworkModule* network_module, + const EventCallback& event_callback); ~SpeechRecognitionManager(); // Start/Stop speech recognizer and microphone. Multiple calls would be @@ -48,14 +52,20 @@ // Callbacks from mic. void OnDataReceived(scoped_ptr<AudioBus> audio_bus); void OnDataCompletion(); - void OnError(); + void OnMicError(); + + // Callbacks from recognizer. + void OnRecognizerResult(const scoped_refptr<SpeechRecognitionEvent>& event); + void OnRecognizerError(); base::WeakPtrFactory<SpeechRecognitionManager> weak_ptr_factory_; // We construct a WeakPtr upon SpeechRecognitionManager's construction in // order to associate the WeakPtr with the constructing thread. base::WeakPtr<SpeechRecognitionManager> weak_this_; - scoped_refptr<base::MessageLoopProxy> const main_message_loop_; + + // Callback for sending dom events if available. + EventCallback event_callback_; SpeechRecognizer recognizer_; scoped_ptr<Mic> mic_; };
diff --git a/src/cobalt/speech/speech_recognizer.cc b/src/cobalt/speech/speech_recognizer.cc index 6355ae7..f46df90 100644 --- a/src/cobalt/speech/speech_recognizer.cc +++ b/src/cobalt/speech/speech_recognizer.cc
@@ -17,14 +17,65 @@ #include "cobalt/speech/speech_recognizer.h" #include "base/bind.h" +#include "base/rand_util.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" +#include "base/utf_string_conversions.h" +#include "cobalt/loader/fetcher_factory.h" +#include "cobalt/network/network_module.h" +#include "net/base/escape.h" +#include "net/url_request/url_fetcher.h" namespace cobalt { namespace speech { -SpeechRecognizer::SpeechRecognizer(loader::FetcherFactory* fetcher_factory) - : fetcher_factory_(fetcher_factory), +namespace { +const char kBaseStreamURL[] = + "https://www.google.com/speech-api/full-duplex/v1"; +// TODO: hide this key to somewhere else. +const char kSpeechAPIKey[] = ""; +const char kUp[] = "up"; +const char kDown[] = "down"; +const char kClient[] = "com.speech.tv"; + +GURL AppendPath(const GURL& url, const std::string& value) { + std::string path(url.path()); + + if (!path.empty()) path += "/"; + + path += net::EscapePath(value); + GURL::Replacements replacements; + replacements.SetPathStr(path); + return url.ReplaceComponents(replacements); +} + +GURL AppendQueryParameter(const GURL& url, const std::string& new_query, + const std::string& value) { + std::string query(url.query()); + + if (!query.empty()) query += "&"; + + query += net::EscapeQueryParamValue(new_query, true); + + if (!value.empty()) { + query += "=" + net::EscapeQueryParamValue(value, true); + } + + GURL::Replacements replacements; + replacements.SetQueryStr(query); + return url.ReplaceComponents(replacements); +} + +} // namespace + +SpeechRecognizer::SpeechRecognizer(network::NetworkModule* network_module, + const ResultCallback& result_callback, + const ErrorCallback& error_callback) + : network_module_(network_module), thread_("speech_recognizer"), - started_(false) { + started_(false), + result_callback_(result_callback), + error_callback_(error_callback) { thread_.StartWithOptions(base::Thread::Options(MessageLoop::TYPE_IO, 0)); } @@ -32,11 +83,12 @@ Stop(); } -void SpeechRecognizer::Start(const SpeechRecognitionConfig& config) { +void SpeechRecognizer::Start(const SpeechRecognitionConfig& config, + int sample_rate) { // Called by the speech recognition manager thread. - thread_.message_loop()->PostTask(FROM_HERE, - base::Bind(&SpeechRecognizer::StartInternal, - base::Unretained(this), config)); + thread_.message_loop()->PostTask( + FROM_HERE, base::Bind(&SpeechRecognizer::StartInternal, + base::Unretained(this), config, sample_rate)); } void SpeechRecognizer::Stop() { @@ -46,21 +98,19 @@ base::Bind(&SpeechRecognizer::StopInternal, base::Unretained(this))); } -void SpeechRecognizer::RecognizeAudio(scoped_array<uint8> encoded_audio_data, - size_t size, bool is_last_chunk) { +void SpeechRecognizer::RecognizeAudio(scoped_ptr<AudioBus> audio_bus, + bool is_last_chunk) { // Called by the speech recognition manager thread. thread_.message_loop()->PostTask( - FROM_HERE, - base::Bind(&SpeechRecognizer::UploadAudioDataInternal, - base::Unretained(this), base::Passed(&encoded_audio_data), - size, is_last_chunk)); + FROM_HERE, base::Bind(&SpeechRecognizer::UploadAudioDataInternal, + base::Unretained(this), base::Passed(&audio_bus), + is_last_chunk)); } void SpeechRecognizer::OnURLFetchDownloadData( const net::URLFetcher* source, scoped_ptr<std::string> download_data) { DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); - - // TODO: process the download data. + // TODO: Parse the serialized protocol buffers data. NOTIMPLEMENTED(); UNREFERENCED_PARAMETER(source); @@ -70,45 +120,100 @@ void SpeechRecognizer::OnURLFetchComplete(const net::URLFetcher* source) { DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); UNREFERENCED_PARAMETER(source); + started_ = false; } -void SpeechRecognizer::StartInternal(const SpeechRecognitionConfig& config) { +void SpeechRecognizer::StartInternal(const SpeechRecognitionConfig& config, + int sample_rate) { DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); + if (started_) { // Recognizer is already started. return; } started_ = true; - // TODO: set up url fetchers with this URLFetcherDelegate. - NOTIMPLEMENTED(); + encoder_.reset(new AudioEncoderFlac(sample_rate)); - UNREFERENCED_PARAMETER(config); - UNREFERENCED_PARAMETER(fetcher_factory_); + // Required for streaming on both up and down connections. + std::string pair = base::Uint64ToString(base::RandUint64()); + + // Set up down stream first. + GURL down_url(kBaseStreamURL); + down_url = AppendPath(down_url, kDown); + down_url = AppendQueryParameter(down_url, "pair", pair); + // Use protobuffer as the output format. + down_url = AppendQueryParameter(down_url, "output", "pb"); + + downstream_fetcher_.reset( + net::URLFetcher::Create(down_url, net::URLFetcher::GET, this)); + downstream_fetcher_->SetRequestContext( + network_module_->url_request_context_getter()); + downstream_fetcher_->Start(); + + // Up stream. + GURL up_url(kBaseStreamURL); + up_url = AppendPath(up_url, kUp); + up_url = AppendQueryParameter(up_url, "client", kClient); + up_url = AppendQueryParameter(up_url, "pair", pair); + up_url = AppendQueryParameter(up_url, "output", "pb"); + up_url = AppendQueryParameter(up_url, "key", kSpeechAPIKey); + + if (!config.lang.empty()) { + up_url = AppendQueryParameter(up_url, "lang", config.lang); + } + + if (config.max_alternatives) { + up_url = AppendQueryParameter(up_url, "maxAlternatives", + base::UintToString(config.max_alternatives)); + } + + if (config.continuous) { + up_url = AppendQueryParameter(up_url, "continuous", ""); + } + if (config.interim_results) { + up_url = AppendQueryParameter(up_url, "interim", ""); + } + + upstream_fetcher_.reset( + net::URLFetcher::Create(up_url, net::URLFetcher::POST, this)); + upstream_fetcher_->SetRequestContext( + network_module_->url_request_context_getter()); + upstream_fetcher_->SetChunkedUpload(encoder_->GetMimeType()); + upstream_fetcher_->Start(); } void SpeechRecognizer::StopInternal() { DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); + if (!started_) { // Recognizer is not started. return; } started_ = false; - // TODO: terminate url fetchers. - NOTIMPLEMENTED(); + upstream_fetcher_.reset(); + downstream_fetcher_.reset(); + encoder_.reset(); } -void SpeechRecognizer::UploadAudioDataInternal( - scoped_array<uint8> encoded_audio_data, size_t size, bool is_last_chunk) { +void SpeechRecognizer::UploadAudioDataInternal(scoped_ptr<AudioBus> audio_bus, + bool is_last_chunk) { DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); + DCHECK(audio_bus); - // TODO: upload encoded audio data chunk. - NOTIMPLEMENTED(); + std::string encoded_audio_data; + if (encoder_) { + encoder_->Encode(audio_bus.get()); + if (is_last_chunk) { + encoder_->Finish(); + } + encoded_audio_data = encoder_->GetAndClearAvailableEncodedData(); + } - UNREFERENCED_PARAMETER(encoded_audio_data); - UNREFERENCED_PARAMETER(size); - UNREFERENCED_PARAMETER(is_last_chunk); + if (upstream_fetcher_ && !encoded_audio_data.empty()) { + upstream_fetcher_->AppendChunkToUpload(encoded_audio_data, is_last_chunk); + } } } // namespace speech
diff --git a/src/cobalt/speech/speech_recognizer.h b/src/cobalt/speech/speech_recognizer.h index c1900c7..7b54584 100644 --- a/src/cobalt/speech/speech_recognizer.h +++ b/src/cobalt/speech/speech_recognizer.h
@@ -18,17 +18,22 @@ #define COBALT_SPEECH_SPEECH_RECOGNIZER_H_ #include <string> +#include <vector> #include "base/threading/thread.h" -#include "cobalt/loader/fetcher_factory.h" +#include "cobalt/network/network_module.h" +#include "cobalt/speech/audio_encoder_flac.h" #include "cobalt/speech/speech_recognition_config.h" +#include "cobalt/speech/speech_recognition_event.h" +#include "media/base/audio_bus.h" +#include "net/url_request/url_fetcher.h" #include "net/url_request/url_fetcher_delegate.h" namespace cobalt { namespace speech { -// Interacts with Google speech recogniton service, and then parses recognition -// results and forms speech recogniton event. +// Interacts with Google speech recognition service, and then parses recognition +// results and forms speech recognition event. // It creates an upstream fetcher to upload the encoded audio and a downstream // fetcher to fetch the speech recognition result. The fetched speech // recognition result is parsed by JSON parser and a SpeechRecognitionEvent, @@ -36,18 +41,24 @@ // manager. class SpeechRecognizer : public net::URLFetcherDelegate { public: - explicit SpeechRecognizer(loader::FetcherFactory* fetcher_factory); + typedef ::media::AudioBus AudioBus; + typedef base::Callback<void(const scoped_refptr<SpeechRecognitionEvent>&)> + ResultCallback; + typedef base::Callback<void(void)> ErrorCallback; + + SpeechRecognizer(network::NetworkModule* network_module, + const ResultCallback& result_callback, + const ErrorCallback& error_callback); ~SpeechRecognizer() OVERRIDE; // Multiple calls to Start/Stop are allowed, the implementation should take // care of multiple calls. // Start speech recognizer. - void Start(const SpeechRecognitionConfig& config); + void Start(const SpeechRecognitionConfig& config, int sample_rate); // Stop speech recognizer. void Stop(); // An encoded audio data is available and ready to be recognized. - void RecognizeAudio(scoped_array<uint8> encoded_audio_data, size_t size, - bool is_last_chunk); + void RecognizeAudio(scoped_ptr<AudioBus> audio_bus, bool is_last_chunk); // net::URLFetcherDelegate interface void OnURLFetchDownloadData(const net::URLFetcher* source, @@ -58,18 +69,26 @@ int64 /*current*/, int64 /*total*/) OVERRIDE {} private: - void StartInternal(const SpeechRecognitionConfig& config); + void StartInternal(const SpeechRecognitionConfig& config, int sample_rate); void StopInternal(); - - void UploadAudioDataInternal(scoped_array<uint8> encoded_audio_data, - size_t size, bool is_last_chunk); + void UploadAudioDataInternal(scoped_ptr<AudioBus> audio_bus, + bool is_last_chunk); // This is used for creating fetchers. - loader::FetcherFactory* fetcher_factory_; + network::NetworkModule* network_module_; // Speech recognizer is operating in its own thread. base::Thread thread_; // Track the start/stop state of speech recognizer. bool started_; + + // Encoder for encoding raw audio data to flac codec. + scoped_ptr<AudioEncoderFlac> encoder_; + // Fetcher for posting the audio data. + scoped_ptr<net::URLFetcher> upstream_fetcher_; + // Fetcher for receiving the streaming results. + scoped_ptr<net::URLFetcher> downstream_fetcher_; + ResultCallback result_callback_; + ErrorCallback error_callback_; }; } // namespace speech
diff --git a/src/cobalt/storage/savegame.h b/src/cobalt/storage/savegame.h index efb4adc..35005e4 100644 --- a/src/cobalt/storage/savegame.h +++ b/src/cobalt/storage/savegame.h
@@ -52,6 +52,8 @@ // Delete the savegame file when the Savegame object goes out of scope. // This should only be used by tests. bool delete_on_destruction; + // Initial data to return from Read. Only for tests. + ByteVector test_initial_data; }; static scoped_ptr<Savegame> Create(const Options& options); @@ -83,7 +85,6 @@ DISALLOW_COPY_AND_ASSIGN(Savegame); }; - } // namespace storage } // namespace cobalt
diff --git a/src/cobalt/storage/savegame_fake.cc b/src/cobalt/storage/savegame_fake.cc index 482e200..9866e6d 100644 --- a/src/cobalt/storage/savegame_fake.cc +++ b/src/cobalt/storage/savegame_fake.cc
@@ -23,6 +23,13 @@ Savegame::ByteVector* SavegameFake::s_persistent_data_; +SavegameFake::SavegameFake(const Options& options) : Savegame(options) { + if (options.test_initial_data.size() > 0 && !s_persistent_data_) { + s_persistent_data_ = new ByteVector(); + *s_persistent_data_ = options.test_initial_data; + } +} + bool SavegameFake::PlatformRead(ByteVector* bytes_ptr) { if (s_persistent_data_) { *bytes_ptr = *s_persistent_data_;
diff --git a/src/cobalt/storage/savegame_fake.h b/src/cobalt/storage/savegame_fake.h index 7350c5f..a7e0a4a 100644 --- a/src/cobalt/storage/savegame_fake.h +++ b/src/cobalt/storage/savegame_fake.h
@@ -30,7 +30,7 @@ // We only support a single "file". class SavegameFake : public Savegame { public: - explicit SavegameFake(const Options& options) : Savegame(options) {} + explicit SavegameFake(const Options& options); ~SavegameFake() OVERRIDE { if (options_.delete_on_destruction) {
diff --git a/src/cobalt/storage/storage_manager.cc b/src/cobalt/storage/storage_manager.cc index d7d2e9c..decba88 100644 --- a/src/cobalt/storage/storage_manager.cc +++ b/src/cobalt/storage/storage_manager.cc
@@ -23,6 +23,7 @@ #include "base/debug/trace_event.h" #include "base/stringprintf.h" #include "cobalt/storage/savegame_thread.h" +#include "cobalt/storage/upgrade/upgrade_reader.h" #include "cobalt/storage/virtual_file.h" #include "cobalt/storage/virtual_file_system.h" #include "sql/statement.h" @@ -134,8 +135,10 @@ } // namespace -StorageManager::StorageManager(const Options& options) - : options_(options), +StorageManager::StorageManager(scoped_ptr<UpgradeHandler> upgrade_handler, + const Options& options) + : upgrade_handler_(upgrade_handler.Pass()), + options_(options), sql_thread_(new base::Thread("StorageManager SQL")), ALLOW_THIS_IN_INITIALIZER_LIST(sql_context_(new SqlContext(this))), connection_(new sql::Connection()), @@ -145,6 +148,7 @@ flush_requested_(false), no_flushes_pending_(true /* manual reset */, true /* initially signalled */) { + DCHECK(upgrade_handler_); TRACE_EVENT0("cobalt::storage", __FUNCTION__); savegame_thread_.reset(new SavegameThread(options_.savegame_options)); // Start the savegame load immediately. @@ -249,6 +253,7 @@ sql_vfs_.reset(new SqlVfs("cobalt_vfs", vfs_.get())); flush_timer_.reset(new base::OneShotTimer<StorageManager>()); // Savegame has finished loading. Now initialize the database connection. + // Check if this is upgrade data, if so, handle it, otherwise: // Check if the savegame data contains a VFS header. // If so, proceed to deserialize it. // If not, load the file into the VFS directly. @@ -257,18 +262,25 @@ DCHECK(loaded_raw_bytes); Savegame::ByteVector& raw_bytes = *loaded_raw_bytes; VirtualFileSystem::SerializedHeader header = {}; - if (raw_bytes.size() > 0) { - if (raw_bytes.size() >= sizeof(VirtualFileSystem::SerializedHeader)) { - memcpy(&header, &raw_bytes[0], - sizeof(VirtualFileSystem::SerializedHeader)); - } - if (VirtualFileSystem::GetHeaderVersion(header) == -1) { - VirtualFile* vf = vfs_->Open(kDefaultSaveFile); - vf->Write(&raw_bytes[0], static_cast<int>(raw_bytes.size()), - 0 /* offset */); + if (raw_bytes.size() > 0) { + const char* buffer = reinterpret_cast<char*>(&raw_bytes[0]); + int buffer_size = static_cast<int>(raw_bytes.size()); + // Is this upgrade data? + if (upgrade::UpgradeReader::IsUpgradeData(buffer, buffer_size)) { + upgrade_handler_->OnUpgrade(this, buffer, buffer_size); } else { - vfs_->Deserialize(&raw_bytes[0], static_cast<int>(raw_bytes.size())); + if (raw_bytes.size() >= sizeof(VirtualFileSystem::SerializedHeader)) { + memcpy(&header, &raw_bytes[0], + sizeof(VirtualFileSystem::SerializedHeader)); + } + + if (VirtualFileSystem::GetHeaderVersion(header) == -1) { + VirtualFile* vf = vfs_->Open(kDefaultSaveFile); + vf->Write(&raw_bytes[0], buffer_size, 0 /* offset */); + } else { + vfs_->Deserialize(&raw_bytes[0], buffer_size); + } } }
diff --git a/src/cobalt/storage/storage_manager.h b/src/cobalt/storage/storage_manager.h index f2a2ad7..021e0f8 100644 --- a/src/cobalt/storage/storage_manager.h +++ b/src/cobalt/storage/storage_manager.h
@@ -28,6 +28,7 @@ #include "base/timer.h" #include "cobalt/storage/savegame_thread.h" #include "cobalt/storage/sql_vfs.h" +#include "cobalt/storage/upgrade/upgrade_reader.h" #include "cobalt/storage/virtual_file_system.h" #include "sql/connection.h" @@ -46,6 +47,16 @@ // is loaded. class StorageManager { public: + // Support for "upgrade" of legacy save data that may have been generated by + // a platform other than Steel/Cobalt. If save data in the upgrade format is + // detected, the |OnUpgrade| method will be called on |upgrade_handler_|. + class UpgradeHandler { + public: + virtual ~UpgradeHandler() {} + virtual void OnUpgrade(StorageManager* storage, const char* data, + int size) = 0; + }; + struct Options { Savegame::Options savegame_options; }; @@ -66,7 +77,8 @@ kSchemaVersionLost = -2, }; - explicit StorageManager(const Options& options); + StorageManager(scoped_ptr<UpgradeHandler> upgrade_handler, + const Options& options); virtual ~StorageManager(); // Obtain the SqlContext for our database. @@ -89,6 +101,8 @@ const Options& options() const { return options_; } + UpgradeHandler* upgrade_handler() const { return upgrade_handler_.get(); } + protected: // Queues a flush to be executed as soon as possible. As soon as possible // will be as soon as any existing flush completes, or right away if no @@ -131,6 +145,9 @@ bool GetSchemaVersion(const char* table_name, int* schema_version); void UpdateSchemaVersion(const char* table_name, int version); + // Upgrade handler used if upgrade save data is detected. + scoped_ptr<UpgradeHandler> upgrade_handler_; + // Configuration options for the Storage Manager. Options options_; @@ -233,7 +250,8 @@ explicit SqlContext(StorageManager* storage_manager) : storage_manager_(storage_manager) {} - friend StorageManager::StorageManager(const Options& options); + friend StorageManager::StorageManager( + scoped_ptr<UpgradeHandler> upgrade_handler, const Options& options); DISALLOW_COPY_AND_ASSIGN(SqlContext); };
diff --git a/src/cobalt/storage/storage_manager_test.cc b/src/cobalt/storage/storage_manager_test.cc index fbd0c9c..7160854 100644 --- a/src/cobalt/storage/storage_manager_test.cc +++ b/src/cobalt/storage/storage_manager_test.cc
@@ -31,6 +31,8 @@ using ::testing::_; using ::testing::InvokeWithoutArgs; +using ::testing::NotNull; +using ::testing::Eq; namespace cobalt { namespace storage { @@ -40,11 +42,17 @@ // Used to be able to intercept QueueFlush(). class MockStorageManager : public StorageManager { public: - explicit MockStorageManager(const Options& options) - : StorageManager(options) {} + MockStorageManager(scoped_ptr<StorageManager::UpgradeHandler> upgrade_handler, + const Options& options) + : StorageManager(upgrade_handler.Pass(), options) {} MOCK_METHOD1(QueueFlush, void(const base::Closure& callback)); }; +class MockUpgradeHandler : public StorageManager::UpgradeHandler { + public: + MOCK_METHOD3(OnUpgrade, void(StorageManager*, const char*, int)); +}; + class CallbackWaiter { public: CallbackWaiter() : was_called_event_(true, false) {} @@ -120,15 +128,22 @@ ~StorageManagerTest() { storage_manager_.reset(NULL); } template <typename StorageManagerType> - void Init(bool delete_savegame = true) { + void Init(bool delete_savegame = true, + const Savegame::ByteVector* initial_data = NULL) { // Destroy the current one first. We can't have two VFSs with the same name // concurrently. storage_manager_.reset(NULL); - StorageManager::Options options; + scoped_ptr<StorageManager::UpgradeHandler> upgrade_handler( + new MockUpgradeHandler()); + StorageManager::Options options; options.savegame_options.delete_on_destruction = delete_savegame; options.savegame_options.factory = &SavegameFake::Create; - storage_manager_.reset(new StorageManagerType(options)); + if (initial_data) { + options.savegame_options.test_initial_data = *initial_data; + } + storage_manager_.reset( + new StorageManagerType(upgrade_handler.Pass(), options)); } MessageLoop message_loop_; @@ -196,5 +211,27 @@ EXPECT_EQ(true, waiter.TimedWait()); } +TEST_F(StorageManagerTest, Upgrade) { + Savegame::ByteVector initial_data; + initial_data.push_back('U'); + initial_data.push_back('P'); + initial_data.push_back('G'); + initial_data.push_back('0'); + Init<StorageManager>(true, &initial_data); + + // We expect a call to the upgrade handler when it reads this data. + MockUpgradeHandler& upgrade_handler = + *dynamic_cast<MockUpgradeHandler*>(storage_manager_->upgrade_handler()); + EXPECT_CALL(upgrade_handler, + OnUpgrade(Eq(storage_manager_.get()), NotNull(), Eq(4))) + .Times(1); + + FlushWaiter waiter; + storage_manager_->FlushNow( + base::Bind(&FlushWaiter::OnFlushDone, base::Unretained(&waiter))); + EXPECT_EQ(true, waiter.TimedWait()); + message_loop_.RunUntilIdle(); +} + } // namespace storage } // namespace cobalt
diff --git a/src/cobalt/storage/upgrade/schema_v1.proto b/src/cobalt/storage/upgrade/schema_v1.proto index 6907806..c2516a5 100644 --- a/src/cobalt/storage/upgrade/schema_v1.proto +++ b/src/cobalt/storage/upgrade/schema_v1.proto
@@ -37,7 +37,7 @@ // A single cookie. mesage Cookie { - // URL in canonical form, e.g. "https://www.youtube.com/". Must be provided + // URL in canonical form, e.g. "https://www.example.com/". Must be provided // or the cookie will be ignored. string url = 1; @@ -48,7 +48,7 @@ // or the cookie will be ignored. string value = 3; - // Domain, e.g. ".youtube.com". Defaults to the domain of the "url" field. + // Domain, e.g. ".example.com". Defaults to the domain of the "url" field. string domain = 4; // Optional virtual path, defaults to "/".
diff --git a/src/cobalt/storage/upgrade/storage_upgrade_test.cc b/src/cobalt/storage/upgrade/storage_upgrade_test.cc index e7b64f0..8728b7e 100644 --- a/src/cobalt/storage/upgrade/storage_upgrade_test.cc +++ b/src/cobalt/storage/upgrade/storage_upgrade_test.cc
@@ -34,9 +34,6 @@ namespace { -const int kHeaderSize = 4; -const char kHeader[] = "UPG0"; - void ReadFileToString(const char* pathname, std::string* string_out) { EXPECT_TRUE(pathname); EXPECT_TRUE(string_out); @@ -44,9 +41,11 @@ EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &file_path)); file_path = file_path.Append(pathname); EXPECT_TRUE(file_util::ReadFileToString(file_path, string_out)); - EXPECT_GT(string_out->length(), static_cast<size_t>(kHeaderSize)); - EXPECT_LE(string_out->length(), static_cast<size_t>(10 * 1024 * 1024)); - EXPECT_EQ(string_out->find(kHeader), 0); + const char* data = string_out->c_str(); + const int size = static_cast<int>(string_out->length()); + EXPECT_GT(size, 0); + EXPECT_LE(size, 10 * 1024 * 1024); + EXPECT_TRUE(UpgradeReader::IsUpgradeData(data, size)); } void ValidateCookie(const net::CanonicalCookie* cookie, const std::string& url, @@ -94,14 +93,13 @@ std::string file_contents; ReadFileToString("cobalt/storage/upgrade/testdata/minimal_cookie_v1.json", &file_contents); - UpgradeReader upgrade_reader( - file_contents.c_str() + kHeaderSize, - static_cast<int>(file_contents.length()) - kHeaderSize); + UpgradeReader upgrade_reader(file_contents.c_str(), + static_cast<int>(file_contents.length())); // 1 cookie. EXPECT_EQ(upgrade_reader.GetNumCookies(), 1); const net::CanonicalCookie* cookie = upgrade_reader.GetCookie(0); - ValidateCookie(cookie, "https://www.youtube.com/", "cookie_name", + ValidateCookie(cookie, "https://www.example.com/", "cookie_name", "cookie_value"); EXPECT_FALSE(upgrade_reader.GetCookie(1)); @@ -115,9 +113,8 @@ ReadFileToString( "cobalt/storage/upgrade/testdata/minimal_local_storage_entry_v1.json", &file_contents); - UpgradeReader upgrade_reader( - file_contents.c_str() + kHeaderSize, - static_cast<int>(file_contents.length()) - kHeaderSize); + UpgradeReader upgrade_reader(file_contents.c_str(), + static_cast<int>(file_contents.length())); // 0 cookies. EXPECT_EQ(upgrade_reader.GetNumCookies(), 0); @@ -135,24 +132,23 @@ std::string file_contents; ReadFileToString("cobalt/storage/upgrade/testdata/full_data_v1.json", &file_contents); - UpgradeReader upgrade_reader( - file_contents.c_str() + kHeaderSize, - static_cast<int>(file_contents.length()) - kHeaderSize); + UpgradeReader upgrade_reader(file_contents.c_str(), + static_cast<int>(file_contents.length())); // 2 cookies. EXPECT_EQ(upgrade_reader.GetNumCookies(), 2); const net::CanonicalCookie* cookie = upgrade_reader.GetCookie(0); base::Time creation = base::Time::FromInternalValue(13119668760000000L); base::Time expiration = base::Time::FromInternalValue(13120000000000000L); - ValidateCookie(cookie, "https://www.youtube.com/", "cookie_name", - "cookie_value", "cookie.domain", "cookie/path", creation, - expiration, true); + ValidateCookie(cookie, "https://www.example.com/1", "cookie_name", + "cookie_value", ".example.com", "/1", creation, expiration, + true); cookie = upgrade_reader.GetCookie(1); creation = base::Time::FromInternalValue(13109668760000000L); expiration = base::Time::FromInternalValue(13110000000000000L); - ValidateCookie(cookie, "https://www.somewhere.com/", "cookie_name_2", - "cookie_value_2", "cookie.domain2", "cookie/path/2", creation, - expiration, true); + ValidateCookie(cookie, "https://www.somewhere.com/2", "cookie_name_2", + "cookie_value_2", ".somewhere.com", "/2", creation, expiration, + true); EXPECT_FALSE(upgrade_reader.GetCookie(2)); // 2 local storage entries. @@ -169,9 +165,8 @@ std::string file_contents; ReadFileToString("cobalt/storage/upgrade/testdata/missing_fields_v1.json", &file_contents); - UpgradeReader upgrade_reader( - file_contents.c_str() + kHeaderSize, - static_cast<int>(file_contents.length()) - kHeaderSize); + UpgradeReader upgrade_reader(file_contents.c_str(), + static_cast<int>(file_contents.length())); // 1 cookie with missing fields, 2 local storage entries with missing fields, // 1 valid local storage entry. @@ -188,9 +183,8 @@ std::string file_contents; ReadFileToString("cobalt/storage/upgrade/testdata/malformed_v1.json", &file_contents); - UpgradeReader upgrade_reader( - file_contents.c_str() + kHeaderSize, - static_cast<int>(file_contents.length()) - kHeaderSize); + UpgradeReader upgrade_reader(file_contents.c_str(), + static_cast<int>(file_contents.length())); // No cookies or local storage entries available in malformed data. EXPECT_EQ(upgrade_reader.GetNumCookies(), 0); @@ -203,14 +197,13 @@ std::string file_contents; ReadFileToString("cobalt/storage/upgrade/testdata/extra_fields_v1.json", &file_contents); - UpgradeReader upgrade_reader( - file_contents.c_str() + kHeaderSize, - static_cast<int>(file_contents.length()) - kHeaderSize); + UpgradeReader upgrade_reader(file_contents.c_str(), + static_cast<int>(file_contents.length())); // 1 cookie, extra fields should be ignored. EXPECT_EQ(upgrade_reader.GetNumCookies(), 1); const net::CanonicalCookie* cookie = upgrade_reader.GetCookie(0); - ValidateCookie(cookie, "https://www.youtube.com/", "cookie_name", + ValidateCookie(cookie, "https://www.example.com/", "cookie_name", "cookie_value"); EXPECT_FALSE(upgrade_reader.GetCookie(1));
diff --git a/src/cobalt/storage/upgrade/testdata/extra_fields_v1.json b/src/cobalt/storage/upgrade/testdata/extra_fields_v1.json index 7709e50..ba7211e 100644 --- a/src/cobalt/storage/upgrade/testdata/extra_fields_v1.json +++ b/src/cobalt/storage/upgrade/testdata/extra_fields_v1.json
@@ -2,7 +2,7 @@ { "cookies": [ { - "url": "https://www.youtube.com", + "url": "https://www.example.com", "name": "cookie_name", "value": "cookie_value", "unexpected": "dummy_value"
diff --git a/src/cobalt/storage/upgrade/testdata/full_data_v1.json b/src/cobalt/storage/upgrade/testdata/full_data_v1.json index c5a039f..07b0296 100644 --- a/src/cobalt/storage/upgrade/testdata/full_data_v1.json +++ b/src/cobalt/storage/upgrade/testdata/full_data_v1.json
@@ -2,22 +2,22 @@ { "cookies": [ { - "url": "https://www.youtube.com", + "url": "https://www.example.com/1", "name": "cookie_name", "value": "cookie_value", - "domain": "cookie.domain", - "path": "cookie/path", + "domain": ".example.com", + "path": "/1", "creation": "13119668760000000", "expiration": "13120000000000000", "last_acess": "13119668770000000", "http_only": true }, { - "url": "http://www.somewhere.com", + "url": "http://www.somewhere.com/2", "name": "cookie_name_2", "value": "cookie_value_2", - "domain": "cookie.domain2", - "path": "cookie/path/2", + "domain": ".somewhere.com", + "path": "/2", "creation": "13109668760000000", "expiration": "13110000000000000", "last_acess": "13109668770000000",
diff --git a/src/cobalt/storage/upgrade/testdata/malformed_v1.json b/src/cobalt/storage/upgrade/testdata/malformed_v1.json index 6d87bf0..4b26940 100644 --- a/src/cobalt/storage/upgrade/testdata/malformed_v1.json +++ b/src/cobalt/storage/upgrade/testdata/malformed_v1.json
@@ -2,22 +2,22 @@ { "cookies": [ { - "url": "https://www.youtube.com", + "url": "https://www.example.com/1", "name": "cookie_name", "value": "cookie_value" - "domain": "cookie.domain", - "path": "cookie/path", + "domain": ".example.com", + "path": "/1", "creation": "13119668760000000", "expiration": "13120000000000000", "last_acess": "13119668770000000", "http_only": true }, { - "url": "http://www.somewhere.com", + "url": "http://www.somewhere.com/2", "name": "cookie_name_2", "value": "cookie_value_2", - "domain": "cookie.domain2", - "path": "cookie/path/2", + "domain": ".somewhere.com", + "path": "/2", "creation": "13109668760000000", "expiration": "13110000000000000", "last_acess": "13109668770000000",
diff --git a/src/cobalt/storage/upgrade/testdata/minimal_cookie_v1.json b/src/cobalt/storage/upgrade/testdata/minimal_cookie_v1.json index 7dbd209..a7f1983 100644 --- a/src/cobalt/storage/upgrade/testdata/minimal_cookie_v1.json +++ b/src/cobalt/storage/upgrade/testdata/minimal_cookie_v1.json
@@ -2,7 +2,7 @@ { "cookies": [ { - "url": "https://www.youtube.com", + "url": "https://www.example.com", "name": "cookie_name", "value": "cookie_value" }
diff --git a/src/cobalt/storage/upgrade/upgrade_reader.cc b/src/cobalt/storage/upgrade/upgrade_reader.cc index 35fdb53..64ef1aa 100644 --- a/src/cobalt/storage/upgrade/upgrade_reader.cc +++ b/src/cobalt/storage/upgrade/upgrade_reader.cc
@@ -30,6 +30,10 @@ namespace { +// The header at the start of upgrade data. +const int kHeaderSize = 4; +const char kHeader[] = "UPG0"; + // Used as a sanity check. const int kMaxUpgradeDataSize = 10 * 1024 * 1024; @@ -104,9 +108,14 @@ UpgradeReader::UpgradeReader(const char* data, int size) { DCHECK(data); - DCHECK_GT(size, 0); + DCHECK_GE(size, kHeaderSize); DCHECK_LE(size, kMaxUpgradeDataSize); + // Check the header and offset the data. + DCHECK(IsUpgradeData(data, size)); + data += kHeaderSize; + size -= kHeaderSize; + base::JSONReader json_reader; scoped_ptr<base::Value> parsed( json_reader.ReadToValue(std::string(data, static_cast<size_t>(size)))); @@ -200,6 +209,11 @@ local_storage_entries_.push_back(LocalStorageEntry(key, value)); } +// static +bool UpgradeReader::IsUpgradeData(const char* data, int size) { + return size >= kHeaderSize && memcmp(data, kHeader, kHeaderSize) == 0; +} + void UpgradeReader::ProcessValues(const base::DictionaryValue* dictionary) { DCHECK(dictionary);
diff --git a/src/cobalt/storage/upgrade/upgrade_reader.h b/src/cobalt/storage/upgrade/upgrade_reader.h index 00d1462..83f0953 100644 --- a/src/cobalt/storage/upgrade/upgrade_reader.h +++ b/src/cobalt/storage/upgrade/upgrade_reader.h
@@ -57,6 +57,8 @@ // storage entry doesn't exist, return NULL. const LocalStorageEntry* GetLocalStorageEntry(int index) const; + static bool IsUpgradeData(const char* data, int size); + private: // Process the parsed values and populate |cookies_| and // |local_storage_entries_|.
diff --git a/src/cobalt/system_window/application_event.h b/src/cobalt/system_window/application_event.h index 1387528..248cd9b 100644 --- a/src/cobalt/system_window/application_event.h +++ b/src/cobalt/system_window/application_event.h
@@ -26,6 +26,8 @@ public: enum Type { kQuit, + kPause, + kUnpause, kSuspend, kResume, };
diff --git a/src/glimp/egl/surface_impl.h b/src/glimp/egl/surface_impl.h index ead72c4..fc026c3 100644 --- a/src/glimp/egl/surface_impl.h +++ b/src/glimp/egl/surface_impl.h
@@ -32,6 +32,10 @@ virtual int GetWidth() const = 0; virtual int GetHeight() const = 0; + // Returns true if the surface is a window surface, false if the surface is a + // pixel buffer or a pixmap. + virtual bool IsWindowSurface() const = 0; + private: };
diff --git a/src/glimp/gles/context.cc b/src/glimp/gles/context.cc index 2057fcb..608344f 100644 --- a/src/glimp/gles/context.cc +++ b/src/glimp/gles/context.cc
@@ -2116,8 +2116,14 @@ void Context::SwapBuffers() { GLIMP_TRACE_EVENT0(__FUNCTION__); - Flush(); - impl_->SwapBuffers(default_draw_framebuffer_->color_attachment_surface()); + egl::Surface* surface = default_draw_framebuffer_->color_attachment_surface(); + // If surface is a pixel buffer or a pixmap, eglSwapBuffers has no effect, and + // no error is generated. + // https://www.khronos.org/registry/egl/sdk/docs/man/html/eglSwapBuffers.xhtml + if (surface->impl()->IsWindowSurface()) { + Flush(); + impl_->SwapBuffers(surface); + } } bool Context::BindTextureToEGLSurface(egl::Surface* surface) {
diff --git a/src/starboard/client_porting/poem/assert_poem.h b/src/starboard/client_porting/poem/assert_poem.h new file mode 100644 index 0000000..9d01a43 --- /dev/null +++ b/src/starboard/client_porting/poem/assert_poem.h
@@ -0,0 +1,40 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// A poem (POsix EMulation) for functions in string.h + +#ifndef STARBOARD_CLIENT_PORTING_POEM_ASSERT_POEM_H_ +#define STARBOARD_CLIENT_PORTING_POEM_ASSERT_POEM_H_ + +#if defined(STARBOARD) + +#include "starboard/log.h" + +#if !defined(POEM_NO_EMULATION) + +// On one line so that the assert macros do not interfere with reporting of line +// numbers in compiler error messages. +#define assert(x) \ + do { \ + if (!(x)) { \ + SbLogFormatF("expression %s failed at %s:%d", #x, __FILE__, __LINE__); \ + SbSystemBreakIntoDebugger(); \ + } \ + } while (false); + +#endif // POEM_NO_EMULATION + +#endif // STARBOARD + +#endif // STARBOARD_CLIENT_PORTING_POEM_ASSERT_POEM_H_
diff --git a/src/starboard/client_porting/poem/math_poem.h b/src/starboard/client_porting/poem/math_poem.h new file mode 100644 index 0000000..7f406d2 --- /dev/null +++ b/src/starboard/client_porting/poem/math_poem.h
@@ -0,0 +1,62 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// A poem (POsix EMulation) for functions in string.h + +#ifndef STARBOARD_CLIENT_PORTING_POEM_MATH_POEM_H_ +#define STARBOARD_CLIENT_PORTING_POEM_MATH_POEM_H_ + +#if defined(STARBOARD) + +#include "starboard/double.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Takes floor of a float |f|. Meant to be a drop-in replacement for |floorf| +static SB_C_INLINE float PoemSingleFloor(const float f) { + double d(f); + return SbDoubleFloor(d); +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#if !defined(POEM_NO_EMULATION) + +#define fabs(x) SbDoubleAbsolute(x) +#define floor(x) SbDoubleFloor(x) +#define floorf(x) PoemSingleFloor(x) +#define pow(x, y) SbDoubleExponent(x, y) + +#include <math.h> +#define ceil(x) ceil(x) +#define fmod(x, y) fmod(x, y) +#define modf(x, y) modf(x, y) +#define log(x) log(x) +#define sqrt(x) sqrt(x) +#define sin(x) sin(x) +#define cos(x) cos(x) +#define tan(x) tan(x) +#define atan(x) atan(x) +#define atan2(x, y) atan2(x, y) +#define asin(x) asin(x) +#define acos(x) acos(x) +#endif // POEM_NO_EMULATION + +#endif // STARBOARD + +#endif // STARBOARD_CLIENT_PORTING_POEM_MATH_POEM_H_
diff --git a/src/starboard/client_porting/poem/stdio_poem.h b/src/starboard/client_porting/poem/stdio_poem.h index 2500568..d68c688 100644 --- a/src/starboard/client_porting/poem/stdio_poem.h +++ b/src/starboard/client_porting/poem/stdio_poem.h
@@ -21,8 +21,8 @@ #if !defined(POEM_NO_EMULATION) -#include "starboard/string.h" #include "starboard/memory.h" +#include "starboard/string.h" #define wcsncmp(s1, s2, c) SbStringCompareWide(s1, s2, c) @@ -35,6 +35,7 @@ #define vsscanf SbStringScan #define sscanf SbStringScanF #define malloc(sz) SbMemoryAllocateUnchecked(sz) +#define calloc(c, s) SbMemoryCalloc(c, s) #define free(a) SbMemoryFree(a) #define realloc(m, sz) SbMemoryReallocateUnchecked(m, sz)
diff --git a/src/starboard/linux/shared/configuration_public.h b/src/starboard/linux/shared/configuration_public.h index 9859ced..7d84890 100644 --- a/src/starboard/linux/shared/configuration_public.h +++ b/src/starboard/linux/shared/configuration_public.h
@@ -52,6 +52,9 @@ // Whether the current platform provides the standard header limits.h. #define SB_HAS_LIMITS_H 1 +// Whether the current platform provides the standard header float.h. +#define SB_HAS_FLOAT_H 1 + // Type detection for wchar_t. #if defined(__WCHAR_MAX__) && \ (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
diff --git a/src/starboard/linux/x64directfb/starboard_platform.gyp b/src/starboard/linux/x64directfb/starboard_platform.gyp index cca05bb..331fd6c 100644 --- a/src/starboard/linux/x64directfb/starboard_platform.gyp +++ b/src/starboard/linux/x64directfb/starboard_platform.gyp
@@ -170,6 +170,7 @@ '<(DEPTH)/starboard/shared/posix/log_format.cc', '<(DEPTH)/starboard/shared/posix/log_is_tty.cc', '<(DEPTH)/starboard/shared/posix/log_raw.cc', + '<(DEPTH)/starboard/shared/posix/memory_flush.cc', '<(DEPTH)/starboard/shared/posix/set_non_blocking_internal.cc', '<(DEPTH)/starboard/shared/posix/socket_accept.cc', '<(DEPTH)/starboard/shared/posix/socket_bind.cc',
diff --git a/src/starboard/linux/x64x11/starboard_platform.gyp b/src/starboard/linux/x64x11/starboard_platform.gyp index 6276a96..761a55d 100644 --- a/src/starboard/linux/x64x11/starboard_platform.gyp +++ b/src/starboard/linux/x64x11/starboard_platform.gyp
@@ -133,6 +133,7 @@ '<(DEPTH)/starboard/shared/posix/log_format.cc', '<(DEPTH)/starboard/shared/posix/log_is_tty.cc', '<(DEPTH)/starboard/shared/posix/log_raw.cc', + '<(DEPTH)/starboard/shared/posix/memory_flush.cc', '<(DEPTH)/starboard/shared/posix/set_non_blocking_internal.cc', '<(DEPTH)/starboard/shared/posix/socket_accept.cc', '<(DEPTH)/starboard/shared/posix/socket_bind.cc',
diff --git a/src/starboard/memory.h b/src/starboard/memory.h index 81fd4f3..f418c30 100644 --- a/src/starboard/memory.h +++ b/src/starboard/memory.h
@@ -142,6 +142,14 @@ // and another call to SbMemoryMap(0x1000) returns (void*)0xB000, // SbMemoryUnmap(0xA000, 0x2000) should free both. SB_EXPORT bool SbMemoryUnmap(void* virtual_address, int64_t size_bytes); + +#if SB_CAN(MAP_EXECUTABLE_MEMORY) +// Flushes any data in the given virtual address range that is cached locally in +// the current processor core to physical memory, ensuring that data and +// instruction caches are cleared. This is required to be called on executable +// memory that has been written to and might be executed in the future. +SB_EXPORT void SbMemoryFlush(void* virtual_address, int64_t size_bytes); +#endif #endif // SB_HAS(MMAP) // Gets the stack bounds for the current thread, placing the highest addressable
diff --git a/src/starboard/nplb/blitter_flush_context_test.cc b/src/starboard/nplb/blitter_flush_context_test.cc index 1095b90..4707e10 100644 --- a/src/starboard/nplb/blitter_flush_context_test.cc +++ b/src/starboard/nplb/blitter_flush_context_test.cc
@@ -67,6 +67,7 @@ // Check that flush succeeds after some commands have been submitted. EXPECT_TRUE(SbBlitterFlushContext(context)); + EXPECT_TRUE(SbBlitterDestroySurface(surface)); EXPECT_TRUE(SbBlitterDestroyContext(context)); EXPECT_TRUE(SbBlitterDestroyDevice(device)); }
diff --git a/src/starboard/nplb/blitter_pixel_tests/tests.cc b/src/starboard/nplb/blitter_pixel_tests/tests.cc index 30c8a8b..9554da7 100644 --- a/src/starboard/nplb/blitter_pixel_tests/tests.cc +++ b/src/starboard/nplb/blitter_pixel_tests/tests.cc
@@ -99,6 +99,8 @@ SbBlitterBlitRectToRect(context_, checker_image, SbBlitterMakeRect(0, 0, GetWidth(), GetHeight()), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } #if SB_HAS(BILINEAR_FILTERING_SUPPORT) @@ -116,6 +118,8 @@ SbBlitterBlitRectToRect(context_, checker_image, SbBlitterMakeRect(0, 0, 2, 2), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, MinifyBlitRectToRect) { @@ -128,6 +132,8 @@ SbBlitterBlitRectToRect( context_, checker_image, SbBlitterMakeRect(0, 0, GetWidth(), GetHeight()), SbBlitterMakeRect(0, 0, GetWidth() / 8, GetHeight() / 8)); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, BlitRectToRectPartialSourceRect) { @@ -141,6 +147,8 @@ context_, checker_image, SbBlitterMakeRect(GetWidth() / 2, 0, GetWidth() / 2, GetHeight()), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, BlitRectToRectTiled) { @@ -153,6 +161,8 @@ SbBlitterBlitRectToRectTiled( context_, checker_image, SbBlitterMakeRect(0, 0, GetWidth(), GetHeight()), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, BlitRectToRectTiledWithNoTiling) { @@ -165,6 +175,8 @@ SbBlitterBlitRectToRectTiled( context_, checker_image, SbBlitterMakeRect(0, 0, GetWidth(), GetHeight()), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, BlitRectToRectTiledNegativeOffset) { @@ -178,6 +190,8 @@ context_, checker_image, SbBlitterMakeRect(-GetWidth(), -GetHeight(), GetWidth(), GetHeight()), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, BlitRectToRectTiledOffCenter) { @@ -192,6 +206,8 @@ SbBlitterMakeRect(-GetWidth() / 2, -GetHeight() / 2, GetWidth(), GetHeight()), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } void DrawRectsWithBatchBlit(SbBlitterSurface texture, @@ -225,6 +241,8 @@ SbBlitterSetRenderTarget(context_, render_target_); DrawRectsWithBatchBlit(checker_image, context_, GetWidth(), GetHeight()); + + SbBlitterDestroySurface(checker_image); } SbBlitterSurface CreateCheckerImageWithPixelData(SbBlitterDevice device, @@ -333,6 +351,8 @@ SbBlitterBlitRectToRect(context_, checker_image, SbBlitterMakeRect(0, 0, GetWidth(), GetHeight()), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, BlitRedGreenRectToRectFromPixelData) { @@ -343,6 +363,8 @@ SbBlitterBlitRectToRect(context_, checker_image, SbBlitterMakeRect(0, 0, GetWidth(), GetHeight()), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, BlitHalfTransparentRectToRectFromPixelData) { @@ -353,6 +375,8 @@ SbBlitterBlitRectToRect(context_, checker_image, SbBlitterMakeRect(0, 0, GetWidth(), GetHeight()), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, BlitCanPunchThrough) { @@ -368,6 +392,8 @@ SbBlitterBlitRectToRect(context_, checker_image, SbBlitterMakeRect(0, 0, GetWidth(), GetHeight()), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, BlitCanBlend) { @@ -383,6 +409,8 @@ SbBlitterBlitRectToRect(context_, checker_image, SbBlitterMakeRect(0, 0, GetWidth(), GetHeight()), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, SimpleAlphaBlitWithNoColorModulation) { @@ -397,6 +425,8 @@ SbBlitterBlitRectToRect(context_, checker_image, SbBlitterMakeRect(0, 0, GetWidth(), GetHeight()), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, BlendedAlphaBlitWithNoColorModulationOnRed) { @@ -410,6 +440,8 @@ SbBlitterBlitRectToRect(context_, checker_image, SbBlitterMakeRect(0, 0, GetWidth(), GetHeight()), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, AlphaBlitWithBlueColorModulation) { @@ -422,6 +454,8 @@ SbBlitterBlitRectToRect(context_, checker_image, SbBlitterMakeRect(0, 0, GetWidth(), GetHeight()), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, BlendedAlphaBlitWithBlueColorModulationOnRed) { @@ -436,6 +470,8 @@ SbBlitterBlitRectToRect(context_, checker_image, SbBlitterMakeRect(0, 0, GetWidth(), GetHeight()), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, BlendedAlphaBlitWithAlphaColorModulationOnRed) { @@ -450,6 +486,8 @@ SbBlitterBlitRectToRect(context_, checker_image, SbBlitterMakeRect(0, 0, GetWidth(), GetHeight()), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, BlendedColorBlitWithAlphaColorModulationOnRed) { @@ -465,6 +503,8 @@ SbBlitterBlitRectToRect(context_, checker_image, SbBlitterMakeRect(0, 0, GetWidth(), GetHeight()), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, BlendedColorBlitWithAlphaColorModulation) { @@ -478,6 +518,8 @@ SbBlitterBlitRectToRect(context_, checker_image, SbBlitterMakeRect(0, 0, GetWidth(), GetHeight()), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, FillRectColorIsNotPremultiplied) { @@ -512,6 +554,8 @@ SbBlitterBlitRectToRect(context_, checker_image, SbBlitterMakeRect(0, 0, GetWidth(), GetHeight()), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, ScissoredBlitRectToRectTiled) { @@ -529,6 +573,8 @@ context_, checker_image, SbBlitterMakeRect(0, 0, GetWidth() * 2, GetHeight() * 2), SbBlitterMakeRect(0, 0, GetWidth(), GetHeight())); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, ScissoredBlitRectToRects) { @@ -543,6 +589,8 @@ SbBlitterSetScissor( context_, SbBlitterMakeRect(32, 32, GetWidth() - 48, GetHeight() - 48)); DrawRectsWithBatchBlit(checker_image, context_, GetWidth(), GetHeight()); + + SbBlitterDestroySurface(checker_image); } TEST_F(SbBlitterPixelTest, ScissorResetsWhenSetRenderTargetIsCalled) {
diff --git a/src/starboard/nplb/memory_map_test.cc b/src/starboard/nplb/memory_map_test.cc index 52cdc1c..96dd551 100644 --- a/src/starboard/nplb/memory_map_test.cc +++ b/src/starboard/nplb/memory_map_test.cc
@@ -54,7 +54,7 @@ // Map 4x the amount of system memory (sequentially, not at once). int64_t bytes_mapped = SbSystemGetTotalCPUMemory() / 4; for (int64_t total_bytes_mapped = 0; - total_bytes_mapped < SbSystemGetTotalCPUMemory() * 4; + total_bytes_mapped < SbSystemGetTotalCPUMemory() * 3; total_bytes_mapped += bytes_mapped) { void* memory = SbMemoryMap(bytes_mapped, kSbMemoryMapProtectWrite, "test"); ASSERT_NE(kFailed, memory); @@ -102,7 +102,8 @@ return x + y; } -// It's not clear how portable this test is. +// This test is known to run on x64, ARM, and MIPS32 with MIPS32 and MIPS16 +// instructions. TEST(SbMemoryMapTest, CanExecuteMappedMemoryWithExecFlag) { void* memory = SbMemoryMap( kSize, kSbMemoryMapProtectReadWrite | kSbMemoryMapProtectExec, "test"); @@ -120,9 +121,19 @@ // cast to a uint* which will be implicitly casted to a void* below. uint8_t* sum_function_start = reinterpret_cast<uint8_t*>(&sum); + // MIPS16 instruction are kept odd addresses to differentiate between that and + // MIPS32 instructions. Most other instructions are aligned to at least even + // addresses, so this code should do nothing for those architectures. + // https://www.linux-mips.org/wiki/MIPS16 + ptrdiff_t sum_function_offset = + sum_function_start - + reinterpret_cast<uint8_t*>( + reinterpret_cast<intptr_t>(sum_function_start) & ~0x1); + sum_function_start -= sum_function_offset; + // Get the last address of the page that |sum_function_start| is on. uint8_t* sum_function_page_end = reinterpret_cast<uint8_t*>( - (reinterpret_cast<intptr_t>(&sum) / SB_MEMORY_PAGE_SIZE) * + (reinterpret_cast<intptr_t>(sum_function_start) / SB_MEMORY_PAGE_SIZE) * SB_MEMORY_PAGE_SIZE + SB_MEMORY_PAGE_SIZE); ASSERT_TRUE(SbMemoryIsAligned(sum_function_page_end, SB_MEMORY_PAGE_SIZE)); @@ -132,8 +143,10 @@ ASSERT_LE(bytes_to_copy, SB_MEMORY_PAGE_SIZE); SbMemoryCopy(memory, sum_function_start, bytes_to_copy); - SumFunction mapped_function = - reinterpret_cast<SumFunction>(reinterpret_cast<char*>(memory)); + SbMemoryFlush(memory, bytes_to_copy); + + SumFunction mapped_function = reinterpret_cast<SumFunction>( + reinterpret_cast<uint8_t*>(memory) + sum_function_offset); EXPECT_EQ(4, (*mapped_function)(1, 3)); EXPECT_EQ(5, (*mapped_function)(10, -5));
diff --git a/src/starboard/nplb/system_symbolize_test.cc b/src/starboard/nplb/system_symbolize_test.cc index 57429af..a0de2e2 100644 --- a/src/starboard/nplb/system_symbolize_test.cc +++ b/src/starboard/nplb/system_symbolize_test.cc
@@ -26,8 +26,9 @@ EXPECT_NE(static_cast<void*>(NULL), stack[0]); char buffer[1024] = {0}; bool result = SbSystemSymbolize(stack[0], buffer, SB_ARRAY_SIZE_INT(buffer)); - EXPECT_TRUE(result); - EXPECT_LT(0, SbStringGetLength(buffer)); + if (result) { + EXPECT_LT(0, SbStringGetLength(buffer)); + } } TEST(SbSystemSymbolizeTest, RainyDay) {
diff --git a/src/starboard/player.h b/src/starboard/player.h index bc19c20..236f3f6 100644 --- a/src/starboard/player.h +++ b/src/starboard/player.h
@@ -272,10 +272,10 @@ // kSbMediaTypeVideo, and must be NULL otherwise. |sample_drm_info| must be // provided for encrypted samples, and must be NULL otherwise. // -// The lifetime of |video_sample_info| and |sample_drm_info| are not guaranteed -// past the call to SbPlayerWriteSample, so the implementation must copy any -// information it wants to retain from those structures synchronously, before it -// returns. +// The lifetime of |video_sample_info| and |sample_drm_info| (as well as member +// |subsample_mapping| contained inside it) are not guaranteed past the call to +// SbPlayerWriteSample, so the implementation must copy any information it wants +// to retain from those structures synchronously, before it returns. SB_EXPORT void SbPlayerWriteSample( SbPlayer player, SbMediaType sample_type,
diff --git a/src/starboard/raspi/1/starboard_platform.gyp b/src/starboard/raspi/1/starboard_platform.gyp index f22110b..6d8c257 100644 --- a/src/starboard/raspi/1/starboard_platform.gyp +++ b/src/starboard/raspi/1/starboard_platform.gyp
@@ -140,6 +140,7 @@ '<(DEPTH)/starboard/shared/posix/log_format.cc', '<(DEPTH)/starboard/shared/posix/log_is_tty.cc', '<(DEPTH)/starboard/shared/posix/log_raw.cc', + '<(DEPTH)/starboard/shared/posix/memory_flush.cc', '<(DEPTH)/starboard/shared/posix/set_non_blocking_internal.cc', '<(DEPTH)/starboard/shared/posix/socket_accept.cc', '<(DEPTH)/starboard/shared/posix/socket_bind.cc',
diff --git a/src/starboard/raspi/shared/configuration_public.h b/src/starboard/raspi/shared/configuration_public.h index 03cb289..942feef 100644 --- a/src/starboard/raspi/shared/configuration_public.h +++ b/src/starboard/raspi/shared/configuration_public.h
@@ -46,6 +46,9 @@ // Whether the current platform provides the standard header limits.h. #define SB_HAS_LIMITS_H 1 +// Whether the current platform provides the standard header float.h. +#define SB_HAS_FLOAT_H 1 + // Type detection for wchar_t. #if defined(__WCHAR_MAX__) && \ (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
diff --git a/src/starboard/shared/posix/memory_flush.cc b/src/starboard/shared/posix/memory_flush.cc new file mode 100644 index 0000000..e45ea16 --- /dev/null +++ b/src/starboard/shared/posix/memory_flush.cc
@@ -0,0 +1,36 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "starboard/memory.h" + +#include <sys/mman.h> + +#include <iomanip> + +#include "starboard/log.h" + +#if !SB_CAN(MAP_EXECUTABLE_MEMORY) +#error "You shouldn't implement SbMemoryFlush unless you can map " \ + "memory pages as executable" +#endif + +void SbMemoryFlush(void* virtual_address, int64_t size_bytes) { + char* memory = reinterpret_cast<char*>(virtual_address); +#if !SB_IS(ARCH_ARM) + int result = msync(memory, size_bytes, MS_SYNC); + SB_DCHECK(result == 0) << "msync failed: 0x" << std::hex << result << " (" + << std::dec << result << "d)"; +#endif + __builtin___clear_cache(memory, memory + size_bytes); +}
diff --git a/src/starboard/shared/signal/suspend_signals.cc b/src/starboard/shared/signal/suspend_signals.cc index 0ea0f09..a3cbeea 100644 --- a/src/starboard/shared/signal/suspend_signals.cc +++ b/src/starboard/shared/signal/suspend_signals.cc
@@ -29,6 +29,15 @@ namespace { +int UnblockSignal(int signal_id) { + sigset_t mask; + ::sigemptyset(&mask); + ::sigaddset(&mask, signal_id); + + sigset_t previous_mask; + return ::sigprocmask(SIG_UNBLOCK, &mask, &previous_mask); +} + void SetSignalHandler(int signal_id, SignalHandlerFunction handler) { struct sigaction action = {0}; @@ -40,8 +49,7 @@ } void SuspendDone(void* /*context*/) { - SetSignalHandler(SIGTSTP, SIG_DFL); - raise(SIGTSTP); + raise(SIGSTOP); } void Suspend(int signal_id) { @@ -51,8 +59,7 @@ void Resume(int signal_id) { LogSignalCaught(signal_id); - SetSignalHandler(SIGTSTP, &Suspend); - // TODO: Resume or Unpause based on state. + // TODO: Resume or Unpause based on state before suspend? starboard::Application::Get()->Unpause(NULL, NULL); } @@ -60,12 +67,13 @@ void InstallSuspendSignalHandlers() { SetSignalHandler(SIGTSTP, &Suspend); + UnblockSignal(SIGTSTP); SetSignalHandler(SIGCONT, &Resume); } void UninstallSuspendSignalHandlers() { - SetSignalHandler(SIGTSTP, SIG_DFL); SetSignalHandler(SIGCONT, SIG_DFL); + SetSignalHandler(SIGTSTP, SIG_DFL); } } // namespace signal
diff --git a/src/starboard/shared/starboard/player/input_buffer_internal.cc b/src/starboard/shared/starboard/player/input_buffer_internal.cc index 45a65c9..8431fc2 100644 --- a/src/starboard/shared/starboard/player/input_buffer_internal.cc +++ b/src/starboard/shared/starboard/player/input_buffer_internal.cc
@@ -49,7 +49,14 @@ video_sample_info_ = *video_sample_info; } if (has_drm_info_) { + SB_DCHECK(sample_drm_info->subsample_count > 0); + + subsamples_.assign(sample_drm_info->subsample_mapping, + sample_drm_info->subsample_mapping + + sample_drm_info->subsample_count); drm_info_ = *sample_drm_info; + drm_info_.subsample_mapping = + subsamples_.empty() ? NULL : &subsamples_[0]; } } @@ -107,6 +114,7 @@ bool has_drm_info_; SbDrmSampleInfo drm_info_; std::vector<uint8_t> decrypted_data_; + std::vector<SbDrmSubSampleMapping> subsamples_; SB_DISALLOW_COPY_AND_ASSIGN(ReferenceCountedBuffer); };
diff --git a/src/starboard/shared/starboard/player/input_buffer_internal.h b/src/starboard/shared/starboard/player/input_buffer_internal.h index 273f873..5cc7af6 100644 --- a/src/starboard/shared/starboard/player/input_buffer_internal.h +++ b/src/starboard/shared/starboard/player/input_buffer_internal.h
@@ -15,6 +15,7 @@ #ifndef STARBOARD_SHARED_STARBOARD_PLAYER_INPUT_BUFFER_INTERNAL_H_ #define STARBOARD_SHARED_STARBOARD_PLAYER_INPUT_BUFFER_INTERNAL_H_ +#include "starboard/drm.h" #include "starboard/media.h" #include "starboard/player.h" #include "starboard/shared/internal_only.h"
diff --git a/src/starboard/shared/stub/memory_flush.cc b/src/starboard/shared/stub/memory_flush.cc new file mode 100644 index 0000000..0ac4dd2 --- /dev/null +++ b/src/starboard/shared/stub/memory_flush.cc
@@ -0,0 +1,17 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "starboard/memory.h" + +void SbMemoryFlush(void* /*virtual_address*/, int64_t /*size_bytes*/) {}
diff --git a/src/starboard/stub/configuration_public.h b/src/starboard/stub/configuration_public.h index 5af547f..9a8c64f 100644 --- a/src/starboard/stub/configuration_public.h +++ b/src/starboard/stub/configuration_public.h
@@ -122,6 +122,9 @@ // Whether the current platform provides the standard header limits.h. #define SB_HAS_LIMITS_H 1 +// Whether the current platform provides the standard header float.h. +#define SB_HAS_FLOAT_H 1 + // Type detection for wchar_t. #if defined(__WCHAR_MAX__) && \ (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
diff --git a/src/starboard/stub/starboard_platform.gyp b/src/starboard/stub/starboard_platform.gyp index 707fecd..ed61292 100644 --- a/src/starboard/stub/starboard_platform.gyp +++ b/src/starboard/stub/starboard_platform.gyp
@@ -89,6 +89,7 @@ '<(DEPTH)/starboard/shared/stub/memory_compare.cc', '<(DEPTH)/starboard/shared/stub/memory_copy.cc', '<(DEPTH)/starboard/shared/stub/memory_find_byte.cc', + '<(DEPTH)/starboard/shared/stub/memory_flush.cc', '<(DEPTH)/starboard/shared/stub/memory_free.cc', '<(DEPTH)/starboard/shared/stub/memory_free_aligned.cc', '<(DEPTH)/starboard/shared/stub/memory_get_stack_bounds.cc',
diff --git a/src/starboard/types.h b/src/starboard/types.h index 16418bb..746689a 100644 --- a/src/starboard/types.h +++ b/src/starboard/types.h
@@ -47,6 +47,10 @@ #include <stdarg.h> #endif +#if SB_HAS(FLOAT_H) +#include <float.h> +#endif + #ifdef __cplusplus extern "C" { #endif @@ -164,6 +168,10 @@ // A value that represents an int that is probably invalid. #define kSbInvalidInt kSbInt32Min +#if !SB_HAS(FLOAT_H) +#define DBL_MANT_DIG 53 +#endif + // --- Standard Include Emulation Audits --------------------------------------- #if !defined(UINT_MAX) || !defined(INT_MIN) || !defined(INT_MAX) || \
diff --git a/src/third_party/flac/flac.h b/src/third_party/flac/flac.h index 5070317..b10e4ab 100644 --- a/src/third_party/flac/flac.h +++ b/src/third_party/flac/flac.h
@@ -9,6 +9,15 @@ // This is a shim header to include the right flac headers. // Use this instead of referencing the flac headers directly. +#ifdef STARBOARD +#include "starboard/configuration.h" +# if SB_IS(BIG_ENDIAN) +# define WORDS_BIGENDIAN 1 +# else +# undef WORDS_BIGENDIAN +# endif +#endif // STARBOARD + #if defined(USE_SYSTEM_FLAC) #include <FLAC/stream_encoder.h> #else
diff --git a/src/third_party/flac/include/FLAC/callback.h b/src/third_party/flac/include/FLAC/callback.h index c954121..2ee135e 100644 --- a/src/third_party/flac/include/FLAC/callback.h +++ b/src/third_party/flac/include/FLAC/callback.h
@@ -33,7 +33,10 @@ #define FLAC__CALLBACK_H #include "ordinals.h" + +#ifndef STARBOARD #include <stdlib.h> /* for size_t */ +#endif // STARBOARD /** \file include/FLAC/callback.h *
diff --git a/src/third_party/flac/include/FLAC/metadata.h b/src/third_party/flac/include/FLAC/metadata.h index fff90b0..b3e43db 100644 --- a/src/third_party/flac/include/FLAC/metadata.h +++ b/src/third_party/flac/include/FLAC/metadata.h
@@ -32,7 +32,10 @@ #ifndef FLAC__METADATA_H #define FLAC__METADATA_H +#ifndef STARBOARD #include <sys/types.h> /* for off_t */ +#endif // STARBOARD + #include "export.h" #include "callback.h" #include "format.h"
diff --git a/src/third_party/flac/include/FLAC/stream_decoder.h b/src/third_party/flac/include/FLAC/stream_decoder.h index 9ac1594..00b5012 100644 --- a/src/third_party/flac/include/FLAC/stream_decoder.h +++ b/src/third_party/flac/include/FLAC/stream_decoder.h
@@ -32,7 +32,10 @@ #ifndef FLAC__STREAM_DECODER_H #define FLAC__STREAM_DECODER_H +#ifndef COBALT #include <stdio.h> /* for FILE */ +#endif // COBALT + #include "export.h" #include "format.h" @@ -1082,6 +1085,7 @@ void *client_data ); +#ifndef COBALT /** Initialize the decoder instance to decode Ogg FLAC streams. * * This flavor of initialization sets up the decoder to decode from a @@ -1346,6 +1350,7 @@ FLAC__StreamDecoderErrorCallback error_callback, void *client_data ); +#endif // COBALT /** Finish the decoding process. * Flushes the decoding buffer, releases resources, resets the decoder
diff --git a/src/third_party/flac/include/FLAC/stream_encoder.h b/src/third_party/flac/include/FLAC/stream_encoder.h index dbbbb23..b030bb2 100644 --- a/src/third_party/flac/include/FLAC/stream_encoder.h +++ b/src/third_party/flac/include/FLAC/stream_encoder.h
@@ -32,7 +32,10 @@ #ifndef FLAC__STREAM_ENCODER_H #define FLAC__STREAM_ENCODER_H +#ifndef COBALT #include <stdio.h> /* for FILE */ +#endif // COBALT + #include "export.h" #include "format.h" #include "stream_decoder.h" @@ -1470,6 +1473,7 @@ */ FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_stream(FLAC__StreamEncoder *encoder, FLAC__StreamEncoderWriteCallback write_callback, FLAC__StreamEncoderSeekCallback seek_callback, FLAC__StreamEncoderTellCallback tell_callback, FLAC__StreamEncoderMetadataCallback metadata_callback, void *client_data); +#ifndef COBALT /** Initialize the encoder instance to encode Ogg FLAC streams. * * This flavor of initialization sets up the encoder to encode to a FLAC @@ -1671,6 +1675,7 @@ * see FLAC__StreamEncoderInitStatus for the meanings of other return values. */ FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_file(FLAC__StreamEncoder *encoder, const char *filename, FLAC__StreamEncoderProgressCallback progress_callback, void *client_data); +#endif // COBALT /** Finish the encoding process. * Flushes the encoding buffer, releases resources, resets the encoder
diff --git a/src/third_party/flac/include/share/alloc.h b/src/third_party/flac/include/share/alloc.h index 2a6ac39..a6761c3 100644 --- a/src/third_party/flac/include/share/alloc.h +++ b/src/third_party/flac/include/share/alloc.h
@@ -5,6 +5,10 @@ #ifndef THIRD_PARTY_FLAC_INCLUDE_SHARE_ALLOC_H #define THIRD_PARTY_FLAC_INCLUDE_SHARE_ALLOC_H +#ifdef STARBOARD +#include "starboard/types.h" +#endif // STARBOARD + void *safe_malloc_(size_t size); void *safe_calloc_(size_t num_items, size_t size);
diff --git a/src/third_party/flac/src/libFLAC/alloc.c b/src/third_party/flac/src/libFLAC/alloc.c index c8c30d3..2297adf 100644 --- a/src/third_party/flac/src/libFLAC/alloc.c +++ b/src/third_party/flac/src/libFLAC/alloc.c
@@ -3,10 +3,15 @@ // found in the LICENSE file. #include <limits.h> + +#ifndef STARBOARD #if !defined _MSC_VER #include <stdint.h> #endif #include <stdlib.h> +#endif // STARBOARD + +#include "starboard/client_porting/poem/stdio_poem.h" #include "share/alloc.h" void *safe_malloc_(size_t size) {
diff --git a/src/third_party/flac/src/libFLAC/bitreader.c b/src/third_party/flac/src/libFLAC/bitreader.c index 5c47b1a..29d64a0 100644 --- a/src/third_party/flac/src/libFLAC/bitreader.c +++ b/src/third_party/flac/src/libFLAC/bitreader.c
@@ -33,6 +33,7 @@ # include <config.h> #endif +#ifndef STARBOARD #include <stdlib.h> /* for malloc() */ #include <string.h> /* for memcpy(), memset() */ #if defined(_MSC_VER) && defined(HAVE_WINSOCK_H) @@ -44,6 +45,11 @@ #else #include <netinet/in.h> /* for ntohl() */ #endif +#endif // STARBOARD + +#include "starboard/client_porting/poem/stdio_poem.h" +#include "starboard/client_porting/poem/string_poem.h" + #include "private/bitmath.h" #include "private/bitreader.h" #include "private/crc.h" @@ -61,8 +67,11 @@ #if WORDS_BIGENDIAN #define SWAP_BE_WORD_TO_HOST(x) (x) #else -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(COBALT) #define SWAP_BE_WORD_TO_HOST(x) local_swap32_(x) +#elif defined(STARBOARD) +#include "starboard/byte_swap.h" +#define SWAP_BE_WORD_TO_HOST(x) SB_NET_TO_HOST_U32(x) #else #define SWAP_BE_WORD_TO_HOST(x) ntohl(x) #endif @@ -149,7 +158,7 @@ FLAC__CPUInfo cpu_info; }; -#if defined(_MSC_VER) && defined(HAVE_WINSOCK_H) +#if defined(_MSC_VER) && defined(HAVE_WINSOCK_H) && !defined(COBALT) /* OPT: an MSVC built-in would be better */ static _inline FLAC__uint32 local_swap32_(FLAC__uint32 x) { @@ -263,7 +272,7 @@ #if WORDS_BIGENDIAN #else end = (br->words*FLAC__BYTES_PER_WORD + br->bytes + bytes + (FLAC__BYTES_PER_WORD-1)) / FLAC__BYTES_PER_WORD; -# if defined(_MSC_VER) && (FLAC__BYTES_PER_WORD == 4) +# if defined(_MSC_VER) && (FLAC__BYTES_PER_WORD == 4) && !defined(COBALT) if(br->cpu_info.type == FLAC__CPUINFO_TYPE_IA32 && br->cpu_info.data.ia32.bswap) { start = br->words; local_swap32_block_(br->buffer + start, end - start); @@ -361,6 +370,7 @@ return true; } +#ifndef COBALT void FLAC__bitreader_dump(const FLAC__BitReader *br, FILE *out) { unsigned i, j; @@ -390,6 +400,7 @@ } } } +#endif // COBALT void FLAC__bitreader_reset_read_crc16(FLAC__BitReader *br, FLAC__uint16 seed) {
diff --git a/src/third_party/flac/src/libFLAC/bitwriter.c b/src/third_party/flac/src/libFLAC/bitwriter.c index 3b8599c..f1c54cf 100644 --- a/src/third_party/flac/src/libFLAC/bitwriter.c +++ b/src/third_party/flac/src/libFLAC/bitwriter.c
@@ -33,6 +33,7 @@ # include <config.h> #endif +#ifndef STARBOARD #include <stdlib.h> /* for malloc() */ #include <string.h> /* for memcpy(), memset() */ #if defined(_MSC_VER) && defined(HAVE_WINSOCK_H) @@ -44,6 +45,11 @@ #else #include <netinet/in.h> /* for ntohl() */ #endif +#else // STARBOARD +#include "starboard/byte_swap.h" +#include "starboard/client_porting/poem/stdio_poem.h" +#endif // STARBOARD + #if 0 /* UNUSED */ #include "private/bitmath.h" #endif @@ -63,8 +69,11 @@ #if WORDS_BIGENDIAN #define SWAP_BE_WORD_TO_HOST(x) (x) #else -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(COBALT) #define SWAP_BE_WORD_TO_HOST(x) local_swap32_(x) +#elif defined(STARBOARD) +#include "starboard/byte_swap.h" +#define SWAP_BE_WORD_TO_HOST(x) SB_NET_TO_HOST_U32(x) #else #define SWAP_BE_WORD_TO_HOST(x) ntohl(x) #endif @@ -207,6 +216,7 @@ bw->words = bw->bits = 0; } +#ifndef COBALT void FLAC__bitwriter_dump(const FLAC__BitWriter *bw, FILE *out) { unsigned i, j; @@ -230,6 +240,7 @@ } } } +#endif // COBALT FLAC__bool FLAC__bitwriter_get_write_crc16(FLAC__BitWriter *bw, FLAC__uint16 *crc) {
diff --git a/src/third_party/flac/src/libFLAC/cpu.c b/src/third_party/flac/src/libFLAC/cpu.c index 60b73bf..904ebbd 100644 --- a/src/third_party/flac/src/libFLAC/cpu.c +++ b/src/third_party/flac/src/libFLAC/cpu.c
@@ -34,6 +34,8 @@ #endif #include "private/cpu.h" + +#ifndef STARBOARD #include <stdlib.h> #include <stdio.h> @@ -54,6 +56,7 @@ # include <signal.h> # include <setjmp.h> +#endif // STARBOARD static sigjmp_buf jmpbuf; static volatile sig_atomic_t canjump = 0; @@ -71,13 +74,13 @@ # endif /* FLAC__NO_ASM */ #endif /* FLAC__CPU_PPC */ -#if defined (__NetBSD__) || defined(__OpenBSD__) +#if (defined (__NetBSD__) || defined(__OpenBSD__)) && !defined(STARBOARD) #include <sys/param.h> #include <sys/sysctl.h> #include <machine/cpu.h> #endif -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) +#if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)) && !defined(STARBOARD) #include <sys/types.h> #include <sys/sysctl.h> #endif
diff --git a/src/third_party/flac/src/libFLAC/fixed.c b/src/third_party/flac/src/libFLAC/fixed.c index 1a3aac0..733e1e3 100644 --- a/src/third_party/flac/src/libFLAC/fixed.c +++ b/src/third_party/flac/src/libFLAC/fixed.c
@@ -33,8 +33,14 @@ # include <config.h> #endif -#include <math.h> +#include <math.h> /* for log() */ + +#ifndef STARBOARD #include <string.h> +#endif // STARBOARD + +#include "starboard/client_porting/poem/string_poem.h" + #include "private/bitmath.h" #include "private/fixed.h" #include "FLAC/assert.h"
diff --git a/src/third_party/flac/src/libFLAC/format.c b/src/third_party/flac/src/libFLAC/format.c index 749461d..faead3c 100644 --- a/src/third_party/flac/src/libFLAC/format.c +++ b/src/third_party/flac/src/libFLAC/format.c
@@ -33,9 +33,16 @@ # include <config.h> #endif +#ifndef STARBOARD #include <stdio.h> #include <stdlib.h> /* for qsort() */ #include <string.h> /* for memset() */ +#endif // STARBOARD + +#include "starboard/client_porting/poem/stdio_poem.h" +#include "starboard/client_porting/poem/stdlib_poem.h" +#include "starboard/client_porting/poem/string_poem.h" + #include "FLAC/assert.h" #include "FLAC/format.h" #include "private/format.h" @@ -581,11 +588,11 @@ FLAC__ASSERT(object->capacity_by_order > 0 || (0 == object->parameters && 0 == object->raw_bits)); if(object->capacity_by_order < max_partition_order) { - if(0 == (object->parameters = (unsigned*)realloc(object->parameters, sizeof(unsigned)*(1 << max_partition_order)))) + if(0 == (object->parameters = (unsigned*)realloc(object->parameters, sizeof(unsigned)*(1LL << max_partition_order)))) return false; - if(0 == (object->raw_bits = (unsigned*)realloc(object->raw_bits, sizeof(unsigned)*(1 << max_partition_order)))) + if(0 == (object->raw_bits = (unsigned*)realloc(object->raw_bits, sizeof(unsigned)*(1LL << max_partition_order)))) return false; - memset(object->raw_bits, 0, sizeof(unsigned)*(1 << max_partition_order)); + memset(object->raw_bits, 0, sizeof(unsigned)*(1LL << max_partition_order)); object->capacity_by_order = max_partition_order; }
diff --git a/src/third_party/flac/src/libFLAC/include/private/bitreader.h b/src/third_party/flac/src/libFLAC/include/private/bitreader.h index fd0f6aa..b91dc57 100644 --- a/src/third_party/flac/src/libFLAC/include/private/bitreader.h +++ b/src/third_party/flac/src/libFLAC/include/private/bitreader.h
@@ -32,10 +32,16 @@ #ifndef FLAC__PRIVATE__BITREADER_H #define FLAC__PRIVATE__BITREADER_H +#ifndef COBALT #include <stdio.h> /* for FILE */ +#endif // COBALT #include "FLAC/ordinals.h" #include "cpu.h" +#ifdef STARBOARD +#include "starboard/types.h" +#endif // STARBOARD + /* * opaque structure definition */ @@ -52,7 +58,9 @@ FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__CPUInfo cpu, FLAC__BitReaderReadCallback rcb, void *cd); void FLAC__bitreader_free(FLAC__BitReader *br); /* does not 'free(br)' */ FLAC__bool FLAC__bitreader_clear(FLAC__BitReader *br); +#ifndef COBALT void FLAC__bitreader_dump(const FLAC__BitReader *br, FILE *out); +#endif // COBALT /* * CRC functions
diff --git a/src/third_party/flac/src/libFLAC/include/private/bitwriter.h b/src/third_party/flac/src/libFLAC/include/private/bitwriter.h index aa5c4f7..6315f22 100644 --- a/src/third_party/flac/src/libFLAC/include/private/bitwriter.h +++ b/src/third_party/flac/src/libFLAC/include/private/bitwriter.h
@@ -32,9 +32,15 @@ #ifndef FLAC__PRIVATE__BITWRITER_H #define FLAC__PRIVATE__BITWRITER_H +#ifndef COBALT #include <stdio.h> /* for FILE */ +#endif // COBALT #include "FLAC/ordinals.h" +#ifdef STARBOARD +#include "starboard/types.h" +#endif // STARBOARD + /* * opaque structure definition */ @@ -49,7 +55,9 @@ FLAC__bool FLAC__bitwriter_init(FLAC__BitWriter *bw); void FLAC__bitwriter_free(FLAC__BitWriter *bw); /* does not 'free(buffer)' */ void FLAC__bitwriter_clear(FLAC__BitWriter *bw); +#ifndef COBALT void FLAC__bitwriter_dump(const FLAC__BitWriter *bw, FILE *out); +#endif // COBALT /* * CRC functions
diff --git a/src/third_party/flac/src/libFLAC/include/private/md5.h b/src/third_party/flac/src/libFLAC/include/private/md5.h index e5f675a..01fbc6b 100644 --- a/src/third_party/flac/src/libFLAC/include/private/md5.h +++ b/src/third_party/flac/src/libFLAC/include/private/md5.h
@@ -28,6 +28,10 @@ #include "FLAC/ordinals.h" +#ifdef STARBOARD +#include "starboard/types.h" +#endif // STARBOARD + typedef struct { FLAC__uint32 in[16]; FLAC__uint32 buf[4];
diff --git a/src/third_party/flac/src/libFLAC/include/private/memory.h b/src/third_party/flac/src/libFLAC/include/private/memory.h index 7852c81..a5e0afd 100644 --- a/src/third_party/flac/src/libFLAC/include/private/memory.h +++ b/src/third_party/flac/src/libFLAC/include/private/memory.h
@@ -36,7 +36,11 @@ #include <config.h> #endif +#ifndef STARBOARD #include <stdlib.h> /* for size_t */ +#else // STARBOARD +#include "starboard/types.h" +#endif // STARBOARD #include "private/float.h" #include "FLAC/ordinals.h" /* for FLAC__bool */
diff --git a/src/third_party/flac/src/libFLAC/md5.c b/src/third_party/flac/src/libFLAC/md5.c index 1a60bac..6daa923 100644 --- a/src/third_party/flac/src/libFLAC/md5.c +++ b/src/third_party/flac/src/libFLAC/md5.c
@@ -2,8 +2,13 @@ # include <config.h> #endif +#ifndef STARBOARD #include <stdlib.h> /* for malloc() */ #include <string.h> /* for memcpy() */ +#endif // STARBOARD + +#include "starboard/client_porting/poem/stdio_poem.h" +#include "starboard/client_porting/poem/string_poem.h" #include "private/md5.h" #include "share/alloc.h"
diff --git a/src/third_party/flac/src/libFLAC/memory.c b/src/third_party/flac/src/libFLAC/memory.c index 4d10097..682c215 100644 --- a/src/third_party/flac/src/libFLAC/memory.c +++ b/src/third_party/flac/src/libFLAC/memory.c
@@ -37,6 +37,8 @@ #include "FLAC/assert.h" #include "share/alloc.h" +#include "starboard/client_porting/poem/stdio_poem.h" + void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address) { void *x;
diff --git a/src/third_party/flac/src/libFLAC/stream_decoder.c b/src/third_party/flac/src/libFLAC/stream_decoder.c index 7759a70..814cb43 100644 --- a/src/third_party/flac/src/libFLAC/stream_decoder.c +++ b/src/third_party/flac/src/libFLAC/stream_decoder.c
@@ -33,6 +33,7 @@ # include <config.h> #endif +#ifndef STARBOARD #if defined _MSC_VER || defined __MINGW32__ #include <io.h> /* for _setmode() */ #include <fcntl.h> /* for _O_BINARY */ @@ -46,6 +47,11 @@ #include <string.h> /* for memset/memcpy() */ #include <sys/stat.h> /* for stat() */ #include <sys/types.h> /* for off_t */ +#endif // STARBOARD + +#include "starboard/client_porting/poem/stdio_poem.h" +#include "starboard/client_porting/poem/string_poem.h" + #if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ #if _MSC_VER <= 1600 || defined __BORLANDC__ /* @@@ [2G limit] */ #define fseeko fseek @@ -54,9 +60,6 @@ #define fseeko _fseeki64 #define ftello _ftelli64 #endif -#elif defined(__LB_SHELL__) -#define fseeko fseek -#define ftello ftell #endif #include "FLAC/assert.h" #include "share/alloc.h" @@ -109,7 +112,9 @@ ***********************************************************************/ static void set_defaults_(FLAC__StreamDecoder *decoder); +#ifndef COBALT static FILE *get_binary_stdin_(void); +#endif // COBALT static FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigned channels); static FLAC__bool has_id_filtered_(FLAC__StreamDecoder *decoder, FLAC__byte *id); static FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder); @@ -141,11 +146,13 @@ #if FLAC__HAS_OGG static FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample); #endif +#ifndef COBALT static FLAC__StreamDecoderReadStatus file_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); static FLAC__StreamDecoderSeekStatus file_seek_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data); static FLAC__StreamDecoderTellStatus file_tell_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); static FLAC__StreamDecoderLengthStatus file_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); static FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data); +#endif // COBALT /*********************************************************************** * @@ -175,7 +182,9 @@ void (*local_lpc_restore_signal_16bit_order8)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); FLAC__bool (*local_bitreader_read_rice_signed_block)(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter); void *client_data; +#ifndef COBALT FILE *file; /* only used if FLAC__stream_decoder_init_file()/FLAC__stream_decoder_init_file() called, else NULL */ +#endif // COBALT FLAC__BitReader *input; FLAC__int32 *output[FLAC__MAX_CHANNELS]; FLAC__int32 *residual[FLAC__MAX_CHANNELS]; /* WATCHOUT: these are the aligned pointers; the real pointers that should be free()'d are residual_unaligned[] below */ @@ -334,7 +343,9 @@ for(i = 0; i < FLAC__MAX_CHANNELS; i++) FLAC__format_entropy_coding_method_partitioned_rice_contents_init(&decoder->private_->partitioned_rice_contents[i]); +#ifndef COBALT decoder->private_->file = 0; +#endif // COBALT set_defaults_(decoder); @@ -513,6 +524,7 @@ ); } +#ifndef COBALT FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream( FLAC__StreamDecoder *decoder, FLAC__StreamDecoderReadCallback read_callback, @@ -665,6 +677,7 @@ { return init_file_internal_(decoder, filename, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/true); } +#endif // COBALT FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder) { @@ -713,11 +726,13 @@ FLAC__ogg_decoder_aspect_finish(&decoder->protected_->ogg_decoder_aspect); #endif +#ifndef COBALT if(0 != decoder->private_->file) { if(decoder->private_->file != stdin) fclose(decoder->private_->file); decoder->private_->file = 0; } +#endif // COBALT if(decoder->private_->do_md5_checking) { if(memcmp(decoder->private_->stream_info.data.stream_info.md5sum, decoder->private_->computed_md5sum, 16)) @@ -1004,8 +1019,10 @@ * not seekable. */ if(!decoder->private_->internal_reset_hack) { +#ifndef COBALT if(decoder->private_->file == stdin) return false; /* can't rewind stdin, reset fails */ +#endif // COBALT if(decoder->private_->seek_callback && decoder->private_->seek_callback(decoder, 0, decoder->private_->client_data) == FLAC__STREAM_DECODER_SEEK_STATUS_ERROR) return false; /* seekable and seek fails, reset fails */ } @@ -1283,6 +1300,7 @@ #endif } +#ifndef COBALT /* * This will forcibly set stdin to binary mode (for OSes that require it) */ @@ -1303,6 +1321,7 @@ return stdin; } +#endif // COBALT FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigned channels) { @@ -3326,6 +3345,7 @@ } #endif +#ifndef COBALT FLAC__StreamDecoderReadStatus file_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) { (void)client_data; @@ -3391,3 +3411,4 @@ return feof(decoder->private_->file)? true : false; } +#endif // COBALT
diff --git a/src/third_party/flac/src/libFLAC/stream_encoder.c b/src/third_party/flac/src/libFLAC/stream_encoder.c index b908660..adad610 100644 --- a/src/third_party/flac/src/libFLAC/stream_encoder.c +++ b/src/third_party/flac/src/libFLAC/stream_encoder.c
@@ -33,6 +33,7 @@ # include <config.h> #endif +#ifndef STARBOARD #if defined _MSC_VER || defined __MINGW32__ #include <io.h> /* for _setmode() */ #include <fcntl.h> /* for _O_BINARY */ @@ -46,6 +47,12 @@ #include <stdlib.h> /* for malloc() */ #include <string.h> /* for memcpy() */ #include <sys/types.h> /* for off_t */ +#endif // STARBOARD + +#include "starboard/client_porting/poem/stdlib_poem.h" +#include "starboard/client_porting/poem/stdio_poem.h" +#include "starboard/client_porting/poem/string_poem.h" + #if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ #if _MSC_VER <= 1600 || defined __BORLANDC__ /* @@@ [2G limit] */ #define fseeko fseek @@ -54,9 +61,6 @@ #define fseeko _fseeki64 #define ftello _ftelli64 #endif -#elif defined(__LB_SHELL__) -#define fseeko fseek -#define ftello ftell #endif #include "FLAC/assert.h" #include "FLAC/stream_decoder.h" @@ -322,12 +326,13 @@ static void verify_metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); static void verify_error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); +#ifndef COBALT static FLAC__StreamEncoderReadStatus file_read_callback_(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data); static FLAC__StreamEncoderSeekStatus file_seek_callback_(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data); static FLAC__StreamEncoderTellStatus file_tell_callback_(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data); static FLAC__StreamEncoderWriteStatus file_write_callback_(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data); static FILE *get_binary_stdout_(void); - +#endif // COBALT /*********************************************************************** * @@ -401,7 +406,9 @@ FLAC__StreamEncoderProgressCallback progress_callback; void *client_data; unsigned first_seekpoint_to_check; +#ifndef COBALT FILE *file; /* only used when encoding to a file */ +#endif // COBALT FLAC__uint64 bytes_written; FLAC__uint64 samples_written; unsigned frames_written; @@ -558,7 +565,9 @@ return 0; } +#ifndef COBALT encoder->private_->file = 0; +#endif // COBALT set_defaults_(encoder); @@ -1165,6 +1174,7 @@ ); } +#ifndef COBALT FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_stream( FLAC__StreamEncoder *encoder, FLAC__StreamEncoderReadCallback read_callback, @@ -1318,6 +1328,7 @@ { return init_file_internal_(encoder, filename, progress_callback, client_data, /*is_ogg=*/true); } +#endif // COBALT FLAC_API FLAC__bool FLAC__stream_encoder_finish(FLAC__StreamEncoder *encoder) { @@ -1367,11 +1378,13 @@ } } +#ifndef COBALT if(0 != encoder->private_->file) { if(encoder->private_->file != stdout) fclose(encoder->private_->file); encoder->private_->file = 0; } +#endif // COBALT #if FLAC__HAS_OGG if(encoder->private_->is_ogg) @@ -3756,9 +3769,9 @@ /* save best parameters and raw_bits */ FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(prc, max(6, best_partition_order)); - memcpy(prc->parameters, private_->partitioned_rice_contents_extra[best_parameters_index].parameters, sizeof(unsigned)*(1<<(best_partition_order))); + memcpy(prc->parameters, private_->partitioned_rice_contents_extra[best_parameters_index].parameters, sizeof(unsigned)*(1LL<<(best_partition_order))); if(do_escape_coding) - memcpy(prc->raw_bits, private_->partitioned_rice_contents_extra[best_parameters_index].raw_bits, sizeof(unsigned)*(1<<(best_partition_order))); + memcpy(prc->raw_bits, private_->partitioned_rice_contents_extra[best_parameters_index].raw_bits, sizeof(unsigned)*(1LL<<(best_partition_order))); /* * Now need to check if the type should be changed to * FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 based on the @@ -4259,6 +4272,7 @@ encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR; } +#ifndef COBALT FLAC__StreamEncoderReadStatus file_read_callback_(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data) { (void)client_data; @@ -4363,3 +4377,4 @@ return stdout; } +#endif // COBALT
diff --git a/src/third_party/flac/src/libFLAC/stream_encoder_framing.c b/src/third_party/flac/src/libFLAC/stream_encoder_framing.c index 939955b..4ab29fe 100644 --- a/src/third_party/flac/src/libFLAC/stream_encoder_framing.c +++ b/src/third_party/flac/src/libFLAC/stream_encoder_framing.c
@@ -33,8 +33,13 @@ # include <config.h> #endif +#ifndef STARBOARD #include <stdio.h> #include <string.h> /* for strlen() */ +#endif // STARBOARD + +#include "starboard/client_porting/poem/string_poem.h" + #include "private/stream_encoder_framing.h" #include "private/crc.h" #include "FLAC/assert.h"
diff --git a/src/third_party/freetype2/freetype2_cobalt.gyp b/src/third_party/freetype2/freetype2_cobalt.gyp index 3da71b5..1ec9f02 100644 --- a/src/third_party/freetype2/freetype2_cobalt.gyp +++ b/src/third_party/freetype2/freetype2_cobalt.gyp
@@ -1,61 +1,68 @@ -# Copyright (c) 2013 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'variables': { - 'ft2_dir': '<(DEPTH)/third_party/freetype2', - }, - 'targets': [ - { - 'target_name': 'freetype2', - 'type': 'static_library', - 'toolsets': ['target'], - 'sources': [ - '<(ft2_dir)/src/autofit/autofit.c', - '<(ft2_dir)/src/base/ftbase.c', - '<(ft2_dir)/src/base/ftbbox.c', - '<(ft2_dir)/src/base/ftbitmap.c', - '<(ft2_dir)/src/base/ftfntfmt.c', - '<(ft2_dir)/src/base/ftfstype.c', - '<(ft2_dir)/src/base/ftgasp.c', - '<(ft2_dir)/src/base/ftglyph.c', - '<(ft2_dir)/src/base/ftinit.c', - '<(ft2_dir)/src/base/ftlcdfil.c', - '<(ft2_dir)/src/base/ftmm.c', - '<(ft2_dir)/src/base/ftstroke.c', - '<(ft2_dir)/src/base/ftsystem.c', - '<(ft2_dir)/src/base/fttype1.c', - '<(ft2_dir)/src/cff/cff.c', - '<(ft2_dir)/src/gzip/ftgzip.c', - '<(ft2_dir)/src/pshinter/pshinter.c', - '<(ft2_dir)/src/psnames/psnames.c', - '<(ft2_dir)/src/raster/raster.c', - '<(ft2_dir)/src/sfnt/sfnt.c', - '<(ft2_dir)/src/smooth/smooth.c', - '<(ft2_dir)/src/truetype/truetype.c', - ], - 'defines': [ - 'FT_CONFIG_OPTION_SYSTEM_ZLIB', - 'FT2_BUILD_LIBRARY', - 'FT_CONFIG_MODULES_H="ftmodule.h"', - 'FT_CONFIG_OPTIONS_H="ftoption.h"', - ], - 'include_dirs': [ - '<(ft2_dir)/include_cobalt', - '<(ft2_dir)/include', - ], - 'dependencies': [ - '<(DEPTH)/third_party/libpng/libpng.gyp:libpng', - '<(DEPTH)/third_party/zlib/zlib.gyp:zlib', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '<(ft2_dir)/include_cobalt', - '<(ft2_dir)/include', - ], - }, - 'msvs_disabled_warnings': [4146], - }, - ], # targets -} +# Copyright (c) 2013 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'ft2_dir': '<(DEPTH)/third_party/freetype2', + }, + 'targets': [ + { + 'target_name': 'freetype2', + 'type': 'static_library', + 'toolsets': ['target'], + 'sources': [ + '<(ft2_dir)/src/autofit/autofit.c', + '<(ft2_dir)/src/base/ftbase.c', + '<(ft2_dir)/src/base/ftbbox.c', + '<(ft2_dir)/src/base/ftbitmap.c', + '<(ft2_dir)/src/base/ftfntfmt.c', + '<(ft2_dir)/src/base/ftfstype.c', + '<(ft2_dir)/src/base/ftgasp.c', + '<(ft2_dir)/src/base/ftglyph.c', + '<(ft2_dir)/src/base/ftinit.c', + '<(ft2_dir)/src/base/ftlcdfil.c', + '<(ft2_dir)/src/base/ftmm.c', + '<(ft2_dir)/src/base/ftstroke.c', + '<(ft2_dir)/src/base/ftsystem.c', + '<(ft2_dir)/src/base/fttype1.c', + '<(ft2_dir)/src/cff/cff.c', + '<(ft2_dir)/src/gzip/ftgzip.c', + '<(ft2_dir)/src/pshinter/pshinter.c', + '<(ft2_dir)/src/psnames/psnames.c', + '<(ft2_dir)/src/raster/raster.c', + '<(ft2_dir)/src/sfnt/sfnt.c', + '<(ft2_dir)/src/smooth/smooth.c', + '<(ft2_dir)/src/truetype/truetype.c', + ], + 'defines': [ + 'FT_CONFIG_OPTION_SYSTEM_ZLIB', + 'FT2_BUILD_LIBRARY', + 'FT_CONFIG_CONFIG_H="ftconfig.h"', + 'FT_CONFIG_MODULES_H="ftmodule.h"', + 'FT_CONFIG_OPTIONS_H="ftoption.h"', + ], + 'include_dirs': [ + '<(ft2_dir)/include_cobalt', + '<(ft2_dir)/include', + ], + 'dependencies': [ + '<(DEPTH)/third_party/libpng/libpng.gyp:libpng', + '<(DEPTH)/third_party/zlib/zlib.gyp:zlib', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(ft2_dir)/include_cobalt', + '<(ft2_dir)/include', + ], + 'defines': [ + 'FT_CONFIG_OPTION_SYSTEM_ZLIB', + 'FT_CONFIG_CONFIG_H="ftconfig.h"', + 'FT_CONFIG_MODULES_H="ftmodule.h"', + 'FT_CONFIG_OPTIONS_H="ftoption.h"', + ], + }, + 'msvs_disabled_warnings': [4146], + }, + ], # targets +}
diff --git a/src/third_party/freetype2/include/freetype/config/ftconfig.h b/src/third_party/freetype2/include/freetype/config/ftconfig.h index 473272a..e01ee9f 100644 --- a/src/third_party/freetype2/include/freetype/config/ftconfig.h +++ b/src/third_party/freetype2/include/freetype/config/ftconfig.h
@@ -38,6 +38,10 @@ #ifndef __FTCONFIG_H__ #define __FTCONFIG_H__ +// This file should not be included, as we override ftconfig.h by defining +// FT_CONFIG_OPTIONS_H via GYP, and changing the include order. +#error "Do not include this file in Cobalt." + #include <ft2build.h> #include FT_CONFIG_OPTIONS_H #include FT_CONFIG_STANDARD_LIBRARY_H
diff --git a/src/third_party/freetype2/include/freetype/config/ftmodule.h b/src/third_party/freetype2/include/freetype/config/ftmodule.h index cea4569..719f03a 100644 --- a/src/third_party/freetype2/include/freetype/config/ftmodule.h +++ b/src/third_party/freetype2/include/freetype/config/ftmodule.h
@@ -10,6 +10,11 @@ * */ +// This file should not be included, as we override ftmodule.h by defining +// FT_CONFIG_MODULES_H via GYP, and changing the include order. + +#error "Do not include this file in Cobalt." + FT_USE_MODULE( FT_Module_Class, autofit_module_class ) FT_USE_MODULE( FT_Driver_ClassRec, tt_driver_class ) FT_USE_MODULE( FT_Driver_ClassRec, t1_driver_class )
diff --git a/src/third_party/freetype2/include/freetype/config/ftoption.h b/src/third_party/freetype2/include/freetype/config/ftoption.h index bbfd896..dc6fa85 100644 --- a/src/third_party/freetype2/include/freetype/config/ftoption.h +++ b/src/third_party/freetype2/include/freetype/config/ftoption.h
@@ -15,6 +15,9 @@ /* */ /***************************************************************************/ +// This file should not be included, as we override ftoption.h by defining +// FT_CONFIG_OPTIONS_H via GYP, and changing the include order. +#error "Do not include this file in Cobalt." #ifndef __FTOPTION_H__ #define __FTOPTION_H__
diff --git a/src/third_party/freetype2/include_cobalt/ftconfig.h b/src/third_party/freetype2/include_cobalt/ftconfig.h new file mode 100644 index 0000000..bc624e9 --- /dev/null +++ b/src/third_party/freetype2/include_cobalt/ftconfig.h
@@ -0,0 +1,447 @@ +/***************************************************************************/ +/* */ +/* ftconfig.h */ +/* */ +/* ANSI-specific configuration file (specification only). */ +/* */ +/* Copyright 1996-2015 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/*************************************************************************/ +/* */ +/* This header file contains a number of macro definitions that are used */ +/* by the rest of the engine. Most of the macros here are automatically */ +/* determined at compile time, and you should not need to change it to */ +/* port FreeType, except to compile the library with a non-ANSI */ +/* compiler. */ +/* */ +/* Note however that if some specific modifications are needed, we */ +/* advise you to place a modified copy in your build directory. */ +/* */ +/* The build directory is usually `builds/<system>', and contains */ +/* system-specific files that are always included first when building */ +/* the library. */ +/* */ +/* This ANSI version should stay in `include/config/'. */ +/* */ +/*************************************************************************/ + +// This file is mostly unchanged, but it is specified here so that we can +// set FT_CONFIG_OPTIONS_H to "ftoption.h" and the right file will be included. + +#ifndef __FTCONFIG_H__ +#define __FTCONFIG_H__ + +#include <ft2build.h> +#include FT_CONFIG_OPTIONS_H +#include FT_CONFIG_STANDARD_LIBRARY_H + +FT_BEGIN_HEADER + +/*************************************************************************/ +/* */ +/* PLATFORM-SPECIFIC CONFIGURATION MACROS */ +/* */ +/* These macros can be toggled to suit a specific system. The current */ +/* ones are defaults used to compile FreeType in an ANSI C environment */ +/* (16bit compilers are also supported). Copy this file to your own */ +/* `builds/<system>' directory, and edit it to port the engine. */ +/* */ +/*************************************************************************/ + +/* There are systems (like the Texas Instruments 'C54x) where a `char' */ +/* has 16 bits. ANSI C says that sizeof(char) is always 1. Since an */ +/* `int' has 16 bits also for this system, sizeof(int) gives 1 which */ +/* is probably unexpected. */ +/* */ +/* `CHAR_BIT' (defined in limits.h) gives the number of bits in a */ +/* `char' type. */ + +#ifndef FT_CHAR_BIT +#define FT_CHAR_BIT CHAR_BIT +#endif + +/* The size of an `int' type. */ +#if FT_UINT_MAX == 0xFFFFUL +#define FT_SIZEOF_INT (16 / FT_CHAR_BIT) +#elif FT_UINT_MAX == 0xFFFFFFFFUL +#define FT_SIZEOF_INT (32 / FT_CHAR_BIT) +#elif FT_UINT_MAX > 0xFFFFFFFFUL && FT_UINT_MAX == 0xFFFFFFFFFFFFFFFFUL +#define FT_SIZEOF_INT (64 / FT_CHAR_BIT) +#else +#error "Unsupported size of `int' type!" +#endif + +/* The size of a `long' type. A five-byte `long' (as used e.g. on the */ +/* DM642) is recognized but avoided. */ +#if FT_ULONG_MAX == 0xFFFFFFFFUL +#define FT_SIZEOF_LONG (32 / FT_CHAR_BIT) +#elif FT_ULONG_MAX > 0xFFFFFFFFUL && FT_ULONG_MAX == 0xFFFFFFFFFFUL +#define FT_SIZEOF_LONG (32 / FT_CHAR_BIT) +#elif FT_ULONG_MAX > 0xFFFFFFFFUL && FT_ULONG_MAX == 0xFFFFFFFFFFFFFFFFUL +#define FT_SIZEOF_LONG (64 / FT_CHAR_BIT) +#else +#error "Unsupported size of `long' type!" +#endif + +/* FT_UNUSED is a macro used to indicate that a given parameter is not */ +/* used -- this is only used to get rid of unpleasant compiler warnings */ +#ifndef FT_UNUSED +#define FT_UNUSED(arg) ((arg) = (arg)) +#endif + +/*************************************************************************/ +/* */ +/* AUTOMATIC CONFIGURATION MACROS */ +/* */ +/* These macros are computed from the ones defined above. Don't touch */ +/* their definition, unless you know precisely what you are doing. No */ +/* porter should need to mess with them. */ +/* */ +/*************************************************************************/ + +/*************************************************************************/ +/* */ +/* Mac support */ +/* */ +/* This is the only necessary change, so it is defined here instead */ +/* providing a new configuration file. */ +/* */ +#if defined(STARBOARD) +/* Nothing. */ +#elif defined(__APPLE__) || (defined(__MWERKS__) && defined(macintosh)) +/* no Carbon frameworks for 64bit 10.4.x */ +/* AvailabilityMacros.h is available since Mac OS X 10.2, */ +/* so guess the system version by maximum errno before inclusion */ +#include <errno.h> +#ifdef ECANCELED /* defined since 10.2 */ +#include "AvailabilityMacros.h" +#endif +#if defined(__LP64__) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4) +#undef FT_MACINTOSH +#endif + +#elif defined(__SC__) || defined(__MRC__) +/* Classic MacOS compilers */ +#include "ConditionalMacros.h" +#if TARGET_OS_MAC +#define FT_MACINTOSH 1 +#endif + +#endif + +/*************************************************************************/ +/* */ +/* <Section> */ +/* basic_types */ +/* */ +/*************************************************************************/ + +/*************************************************************************/ +/* */ +/* <Type> */ +/* FT_Int16 */ +/* */ +/* <Description> */ +/* A typedef for a 16bit signed integer type. */ +/* */ +typedef signed short FT_Int16; + +/*************************************************************************/ +/* */ +/* <Type> */ +/* FT_UInt16 */ +/* */ +/* <Description> */ +/* A typedef for a 16bit unsigned integer type. */ +/* */ +typedef unsigned short FT_UInt16; + +/* */ + +/* this #if 0 ... #endif clause is for documentation purposes */ +#if 0 + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Int32 */ + /* */ + /* <Description> */ + /* A typedef for a 32bit signed integer type. The size depends on */ + /* the configuration. */ + /* */ + typedef signed XXX FT_Int32; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_UInt32 */ + /* */ + /* A typedef for a 32bit unsigned integer type. The size depends on */ + /* the configuration. */ + /* */ + typedef unsigned XXX FT_UInt32; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Int64 */ + /* */ + /* A typedef for a 64bit signed integer type. The size depends on */ + /* the configuration. Only defined if there is real 64bit support; */ + /* otherwise, it gets emulated with a structure (if necessary). */ + /* */ + typedef signed XXX FT_Int64; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_UInt64 */ + /* */ + /* A typedef for a 64bit unsigned integer type. The size depends on */ + /* the configuration. Only defined if there is real 64bit support; */ + /* otherwise, it gets emulated with a structure (if necessary). */ + /* */ + typedef unsigned XXX FT_UInt64; + + /* */ + +#endif + +#if FT_SIZEOF_INT == (32 / FT_CHAR_BIT) + +typedef signed int FT_Int32; +typedef unsigned int FT_UInt32; + +#elif FT_SIZEOF_LONG == (32 / FT_CHAR_BIT) + +typedef signed long FT_Int32; +typedef unsigned long FT_UInt32; + +#else +#error "no 32bit type found -- please check your configuration files" +#endif + +/* look up an integer type that is at least 32 bits */ +#if FT_SIZEOF_INT >= (32 / FT_CHAR_BIT) + +typedef int FT_Fast; +typedef unsigned int FT_UFast; + +#elif FT_SIZEOF_LONG >= (32 / FT_CHAR_BIT) + +typedef long FT_Fast; +typedef unsigned long FT_UFast; + +#endif + +/* determine whether we have a 64-bit int type for platforms without */ +/* Autoconf */ +#if FT_SIZEOF_LONG == (64 / FT_CHAR_BIT) + +/* FT_LONG64 must be defined if a 64-bit type is available */ +#define FT_LONG64 +#define FT_INT64 long +#define FT_UINT64 unsigned long + +/*************************************************************************/ +/* */ +/* A 64-bit data type may create compilation problems if you compile */ +/* in strict ANSI mode. To avoid them, we disable other 64-bit data */ +/* types if __STDC__ is defined. You can however ignore this rule */ +/* by defining the FT_CONFIG_OPTION_FORCE_INT64 configuration macro. */ +/* */ +#elif !defined(__STDC__) || defined(FT_CONFIG_OPTION_FORCE_INT64) + +#if defined(_MSC_VER) && _MSC_VER >= 900 /* Visual C++ (and Intel C++) */ + +/* this compiler provides the __int64 type */ +#define FT_LONG64 +#define FT_INT64 __int64 +#define FT_UINT64 unsigned __int64 + +#elif defined(__BORLANDC__) /* Borland C++ */ + +/* XXXX: We should probably check the value of __BORLANDC__ in order */ +/* to test the compiler version. */ + +/* this compiler provides the __int64 type */ +#define FT_LONG64 +#define FT_INT64 __int64 +#define FT_UINT64 unsigned __int64 + +#elif defined(__WATCOMC__) /* Watcom C++ */ + +/* Watcom doesn't provide 64-bit data types */ + +#elif defined(__MWERKS__) /* Metrowerks CodeWarrior */ + +#define FT_LONG64 +#define FT_INT64 long long int +#define FT_UINT64 unsigned long long int + +#elif defined(__GNUC__) + +/* GCC provides the `long long' type */ +#define FT_LONG64 +#define FT_INT64 long long int +#define FT_UINT64 unsigned long long int + +#endif /* _MSC_VER */ + +#endif /* FT_SIZEOF_LONG == (64 / FT_CHAR_BIT) */ + +#ifdef FT_LONG64 +typedef FT_INT64 FT_Int64; +typedef FT_UINT64 FT_UInt64; +#endif + +/*************************************************************************/ +/* */ +/* miscellaneous */ +/* */ +/*************************************************************************/ + +#define FT_BEGIN_STMNT do { +#define FT_END_STMNT \ + } \ + while (0) +#define FT_DUMMY_STMNT FT_BEGIN_STMNT FT_END_STMNT + +/* typeof condition taken from gnulib's `intprops.h' header file */ +#if (__GNUC__ >= 2 || defined(__IBM__TYPEOF__) || \ + (__SUNPRO_C >= 0x5110 && !__STDC__)) +#define FT_TYPEOF(type) (__typeof__(type)) +#else +#define FT_TYPEOF(type) /* empty */ +#endif + +#ifdef FT_MAKE_OPTION_SINGLE_OBJECT + +#define FT_LOCAL(x) static x +#define FT_LOCAL_DEF(x) static x + +#else + +#ifdef __cplusplus +#define FT_LOCAL(x) extern "C" x +#define FT_LOCAL_DEF(x) extern "C" x +#else +#define FT_LOCAL(x) extern x +#define FT_LOCAL_DEF(x) x +#endif + +#endif /* FT_MAKE_OPTION_SINGLE_OBJECT */ + +#define FT_LOCAL_ARRAY(x) extern const x +#define FT_LOCAL_ARRAY_DEF(x) const x + +#ifndef FT_BASE + +#ifdef __cplusplus +#define FT_BASE(x) extern "C" x +#else +#define FT_BASE(x) extern x +#endif + +#endif /* !FT_BASE */ + +#ifndef FT_BASE_DEF + +#ifdef __cplusplus +#define FT_BASE_DEF(x) x +#else +#define FT_BASE_DEF(x) x +#endif + +#endif /* !FT_BASE_DEF */ + +#ifndef FT_EXPORT + +#ifdef __cplusplus +#define FT_EXPORT(x) extern "C" x +#else +#define FT_EXPORT(x) extern x +#endif + +#endif /* !FT_EXPORT */ + +#ifndef FT_EXPORT_DEF + +#ifdef __cplusplus +#define FT_EXPORT_DEF(x) extern "C" x +#else +#define FT_EXPORT_DEF(x) extern x +#endif + +#endif /* !FT_EXPORT_DEF */ + +#ifndef FT_EXPORT_VAR + +#ifdef __cplusplus +#define FT_EXPORT_VAR(x) extern "C" x +#else +#define FT_EXPORT_VAR(x) extern x +#endif + +#endif /* !FT_EXPORT_VAR */ + +/* The following macros are needed to compile the library with a */ +/* C++ compiler and with 16bit compilers. */ +/* */ + +/* This is special. Within C++, you must specify `extern "C"' for */ +/* functions which are used via function pointers, and you also */ +/* must do that for structures which contain function pointers to */ +/* assure C linkage -- it's not possible to have (local) anonymous */ +/* functions which are accessed by (global) function pointers. */ +/* */ +/* */ +/* FT_CALLBACK_DEF is used to _define_ a callback function. */ +/* */ +/* FT_CALLBACK_TABLE is used to _declare_ a constant variable that */ +/* contains pointers to callback functions. */ +/* */ +/* FT_CALLBACK_TABLE_DEF is used to _define_ a constant variable */ +/* that contains pointers to callback functions. */ +/* */ +/* */ +/* Some 16bit compilers have to redefine these macros to insert */ +/* the infamous `_cdecl' or `__fastcall' declarations. */ +/* */ +#ifndef FT_CALLBACK_DEF +#ifdef __cplusplus +#define FT_CALLBACK_DEF(x) extern "C" x +#else +#define FT_CALLBACK_DEF(x) static x +#endif +#endif /* FT_CALLBACK_DEF */ + +#ifndef FT_CALLBACK_TABLE +#ifdef __cplusplus +#define FT_CALLBACK_TABLE extern "C" +#define FT_CALLBACK_TABLE_DEF extern "C" +#else +#define FT_CALLBACK_TABLE extern +#define FT_CALLBACK_TABLE_DEF /* nothing */ +#endif +#endif /* FT_CALLBACK_TABLE */ + +FT_END_HEADER + +#endif /* __FTCONFIG_H__ */ + +/* END */
diff --git a/src/third_party/freetype2/include_cobalt/ftoption.h b/src/third_party/freetype2/include_cobalt/ftoption.h index 6f38e6d..36f93fc 100644 --- a/src/third_party/freetype2/include_cobalt/ftoption.h +++ b/src/third_party/freetype2/include_cobalt/ftoption.h
@@ -1,886 +1,849 @@ -/***************************************************************************/ -/* */ -/* ftoption.h */ -/* */ -/* User-selectable configuration macros (specification only). */ -/* */ -/* Copyright 1996-2015 by */ -/* David Turner, Robert Wilhelm, and Werner Lemberg. */ -/* */ -/* This file is part of the FreeType project, and may only be used, */ -/* modified, and distributed under the terms of the FreeType project */ -/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ -/* this file you indicate that you have read the license and */ -/* understand and accept it fully. */ -/* */ -/***************************************************************************/ - - -#ifndef COBALT___FTOPTION_H__ -#define COBALT___FTOPTION_H__ - - -#include <ft2build.h> - - -FT_BEGIN_HEADER - - /*************************************************************************/ - /* */ - /* USER-SELECTABLE CONFIGURATION MACROS */ - /* */ - /* This file contains the default configuration macro definitions for */ - /* a standard build of the FreeType library. There are three ways to */ - /* use this file to build project-specific versions of the library: */ - /* */ - /* - You can modify this file by hand, but this is not recommended in */ - /* cases where you would like to build several versions of the */ - /* library from a single source directory. */ - /* */ - /* - You can put a copy of this file in your build directory, more */ - /* precisely in `$BUILD/config/ftoption.h', where `$BUILD' is the */ - /* name of a directory that is included _before_ the FreeType include */ - /* path during compilation. */ - /* */ - /* The default FreeType Makefiles and Jamfiles use the build */ - /* directory `builds/<system>' by default, but you can easily change */ - /* that for your own projects. */ - /* */ - /* - Copy the file <ft2build.h> to `$BUILD/ft2build.h' and modify it */ - /* slightly to pre-define the macro FT_CONFIG_OPTIONS_H used to */ - /* locate this file during the build. For example, */ - /* */ - /* #define FT_CONFIG_OPTIONS_H <myftoptions.h> */ - /* #include <config/ftheader.h> */ - /* */ - /* will use `$BUILD/myftoptions.h' instead of this file for macro */ - /* definitions. */ - /* */ - /* Note also that you can similarly pre-define the macro */ - /* FT_CONFIG_MODULES_H used to locate the file listing of the modules */ - /* that are statically linked to the library at compile time. By */ - /* default, this file is <config/ftmodule.h>. */ - /* */ - /* We highly recommend using the third method whenever possible. */ - /* */ - /*************************************************************************/ - - - /*************************************************************************/ - /*************************************************************************/ - /**** ****/ - /**** G E N E R A L F R E E T Y P E 2 C O N F I G U R A T I O N ****/ - /**** ****/ - /*************************************************************************/ - /*************************************************************************/ - - - /*************************************************************************/ - /* */ - /* Uncomment the line below if you want to activate sub-pixel rendering */ - /* (a.k.a. LCD rendering, or ClearType) in this build of the library. */ - /* */ - /* Note that this feature is covered by several Microsoft patents */ - /* and should not be activated in any default build of the library. */ - /* */ - /* This macro has no impact on the FreeType API, only on its */ - /* _implementation_. For example, using FT_RENDER_MODE_LCD when calling */ - /* FT_Render_Glyph still generates a bitmap that is 3 times wider than */ - /* the original size in case this macro isn't defined; however, each */ - /* triplet of subpixels has R=G=B. */ - /* */ - /* This is done to allow FreeType clients to run unmodified, forcing */ - /* them to display normal gray-level anti-aliased glyphs. */ - /* */ -/* #define FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ - - - /*************************************************************************/ - /* */ - /* Many compilers provide a non-ANSI 64-bit data type that can be used */ - /* by FreeType to speed up some computations. However, this will create */ - /* some problems when compiling the library in strict ANSI mode. */ - /* */ - /* For this reason, the use of 64-bit integers is normally disabled when */ - /* the __STDC__ macro is defined. You can however disable this by */ - /* defining the macro FT_CONFIG_OPTION_FORCE_INT64 here. */ - /* */ - /* For most compilers, this will only create compilation warnings when */ - /* building the library. */ - /* */ - /* ObNote: The compiler-specific 64-bit integers are detected in the */ - /* file `ftconfig.h' either statically or through the */ - /* `configure' script on supported platforms. */ - /* */ -#undef FT_CONFIG_OPTION_FORCE_INT64 - - - /*************************************************************************/ - /* */ - /* If this macro is defined, do not try to use an assembler version of */ - /* performance-critical functions (e.g. FT_MulFix). You should only do */ - /* that to verify that the assembler function works properly, or to */ - /* execute benchmark tests of the various implementations. */ -/* #define FT_CONFIG_OPTION_NO_ASSEMBLER */ - - - /*************************************************************************/ - /* */ - /* If this macro is defined, try to use an inlined assembler version of */ - /* the `FT_MulFix' function, which is a `hotspot' when loading and */ - /* hinting glyphs, and which should be executed as fast as possible. */ - /* */ - /* Note that if your compiler or CPU is not supported, this will default */ - /* to the standard and portable implementation found in `ftcalc.c'. */ - /* */ -#define FT_CONFIG_OPTION_INLINE_MULFIX - - - /*************************************************************************/ - /* */ - /* LZW-compressed file support. */ - /* */ - /* FreeType now handles font files that have been compressed with the */ - /* `compress' program. This is mostly used to parse many of the PCF */ - /* files that come with various X11 distributions. The implementation */ - /* uses NetBSD's `zopen' to partially uncompress the file on the fly */ - /* (see src/lzw/ftgzip.c). */ - /* */ - /* Define this macro if you want to enable this `feature'. */ - /* */ -#define FT_CONFIG_OPTION_USE_LZW - - - /*************************************************************************/ - /* */ - /* Gzip-compressed file support. */ - /* */ - /* FreeType now handles font files that have been compressed with the */ - /* `gzip' program. This is mostly used to parse many of the PCF files */ - /* that come with XFree86. The implementation uses `zlib' to */ - /* partially uncompress the file on the fly (see src/gzip/ftgzip.c). */ - /* */ - /* Define this macro if you want to enable this `feature'. See also */ - /* the macro FT_CONFIG_OPTION_SYSTEM_ZLIB below. */ - /* */ -#define FT_CONFIG_OPTION_USE_ZLIB - - - /*************************************************************************/ - /* */ - /* ZLib library selection */ - /* */ - /* This macro is only used when FT_CONFIG_OPTION_USE_ZLIB is defined. */ - /* It allows FreeType's `ftgzip' component to link to the system's */ - /* installation of the ZLib library. This is useful on systems like */ - /* Unix or VMS where it generally is already available. */ - /* */ - /* If you let it undefined, the component will use its own copy */ - /* of the zlib sources instead. These have been modified to be */ - /* included directly within the component and *not* export external */ - /* function names. This allows you to link any program with FreeType */ - /* _and_ ZLib without linking conflicts. */ - /* */ - /* Do not #undef this macro here since the build system might define */ - /* it for certain configurations only. */ - /* */ -/* #define FT_CONFIG_OPTION_SYSTEM_ZLIB */ - - - /*************************************************************************/ - /* */ - /* Bzip2-compressed file support. */ - /* */ - /* FreeType now handles font files that have been compressed with the */ - /* `bzip2' program. This is mostly used to parse many of the PCF */ - /* files that come with XFree86. The implementation uses `libbz2' to */ - /* partially uncompress the file on the fly (see src/bzip2/ftbzip2.c). */ - /* Contrary to gzip, bzip2 currently is not included and need to use */ - /* the system available bzip2 implementation. */ - /* */ - /* Define this macro if you want to enable this `feature'. */ - /* */ -/* #define FT_CONFIG_OPTION_USE_BZIP2 */ - - - /*************************************************************************/ - /* */ - /* Define to disable the use of file stream functions and types, FILE, */ - /* fopen() etc. Enables the use of smaller system libraries on embedded */ - /* systems that have multiple system libraries, some with or without */ - /* file stream support, in the cases where file stream support is not */ - /* necessary such as memory loading of font files. */ - /* */ -/* #define FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT */ - - - /*************************************************************************/ - /* */ - /* PNG bitmap support. */ - /* */ - /* FreeType now handles loading color bitmap glyphs in the PNG format. */ - /* This requires help from the external libpng library. Uncompressed */ - /* color bitmaps do not need any external libraries and will be */ - /* supported regardless of this configuration. */ - /* */ - /* Define this macro if you want to enable this `feature'. */ - /* */ -#define FT_CONFIG_OPTION_USE_PNG - - - /*************************************************************************/ - /* */ - /* HarfBuzz support. */ - /* */ - /* FreeType uses the HarfBuzz library to improve auto-hinting of */ - /* OpenType fonts. If available, many glyphs not directly addressable */ - /* by a font's character map will be hinted also. */ - /* */ - /* Define this macro if you want to enable this `feature'. */ - /* */ -/* #define FT_CONFIG_OPTION_USE_HARFBUZZ */ - - - /*************************************************************************/ - /* */ - /* DLL export compilation */ - /* */ - /* When compiling FreeType as a DLL, some systems/compilers need a */ - /* special keyword in front OR after the return type of function */ - /* declarations. */ - /* */ - /* Two macros are used within the FreeType source code to define */ - /* exported library functions: FT_EXPORT and FT_EXPORT_DEF. */ - /* */ - /* FT_EXPORT( return_type ) */ - /* */ - /* is used in a function declaration, as in */ - /* */ - /* FT_EXPORT( FT_Error ) */ - /* FT_Init_FreeType( FT_Library* alibrary ); */ - /* */ - /* */ - /* FT_EXPORT_DEF( return_type ) */ - /* */ - /* is used in a function definition, as in */ - /* */ - /* FT_EXPORT_DEF( FT_Error ) */ - /* FT_Init_FreeType( FT_Library* alibrary ) */ - /* { */ - /* ... some code ... */ - /* return FT_Err_Ok; */ - /* } */ - /* */ - /* You can provide your own implementation of FT_EXPORT and */ - /* FT_EXPORT_DEF here if you want. If you leave them undefined, they */ - /* will be later automatically defined as `extern return_type' to */ - /* allow normal compilation. */ - /* */ - /* Do not #undef these macros here since the build system might define */ - /* them for certain configurations only. */ - /* */ -/* #define FT_EXPORT(x) extern x */ -/* #define FT_EXPORT_DEF(x) x */ - - - /*************************************************************************/ - /* */ - /* Glyph Postscript Names handling */ - /* */ - /* By default, FreeType 2 is compiled with the `psnames' module. This */ - /* module is in charge of converting a glyph name string into a */ - /* Unicode value, or return a Macintosh standard glyph name for the */ - /* use with the TrueType `post' table. */ - /* */ - /* Undefine this macro if you do not want `psnames' compiled in your */ - /* build of FreeType. This has the following effects: */ - /* */ - /* - The TrueType driver will provide its own set of glyph names, */ - /* if you build it to support postscript names in the TrueType */ - /* `post' table. */ - /* */ - /* - The Type 1 driver will not be able to synthesize a Unicode */ - /* charmap out of the glyphs found in the fonts. */ - /* */ - /* You would normally undefine this configuration macro when building */ - /* a version of FreeType that doesn't contain a Type 1 or CFF driver. */ - /* */ -#define FT_CONFIG_OPTION_POSTSCRIPT_NAMES - - - /*************************************************************************/ - /* */ - /* Postscript Names to Unicode Values support */ - /* */ - /* By default, FreeType 2 is built with the `PSNames' module compiled */ - /* in. Among other things, the module is used to convert a glyph name */ - /* into a Unicode value. This is especially useful in order to */ - /* synthesize on the fly a Unicode charmap from the CFF/Type 1 driver */ - /* through a big table named the `Adobe Glyph List' (AGL). */ - /* */ - /* Undefine this macro if you do not want the Adobe Glyph List */ - /* compiled in your `PSNames' module. The Type 1 driver will not be */ - /* able to synthesize a Unicode charmap out of the glyphs found in the */ - /* fonts. */ - /* */ -#define FT_CONFIG_OPTION_ADOBE_GLYPH_LIST - - - /*************************************************************************/ - /* */ - /* Support for Mac fonts */ - /* */ - /* Define this macro if you want support for outline fonts in Mac */ - /* format (mac dfont, mac resource, macbinary containing a mac */ - /* resource) on non-Mac platforms. */ - /* */ - /* Note that the `FOND' resource isn't checked. */ - /* */ -#define FT_CONFIG_OPTION_MAC_FONTS - - - /*************************************************************************/ - /* */ - /* Guessing methods to access embedded resource forks */ - /* */ - /* Enable extra Mac fonts support on non-Mac platforms (e.g. */ - /* GNU/Linux). */ - /* */ - /* Resource forks which include fonts data are stored sometimes in */ - /* locations which users or developers don't expected. In some cases, */ - /* resource forks start with some offset from the head of a file. In */ - /* other cases, the actual resource fork is stored in file different */ - /* from what the user specifies. If this option is activated, */ - /* FreeType tries to guess whether such offsets or different file */ - /* names must be used. */ - /* */ - /* Note that normal, direct access of resource forks is controlled via */ - /* the FT_CONFIG_OPTION_MAC_FONTS option. */ - /* */ -#ifdef FT_CONFIG_OPTION_MAC_FONTS -#define FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK -#endif - - - /*************************************************************************/ - /* */ - /* Allow the use of FT_Incremental_Interface to load typefaces that */ - /* contain no glyph data, but supply it via a callback function. */ - /* This is required by clients supporting document formats which */ - /* supply font data incrementally as the document is parsed, such */ - /* as the Ghostscript interpreter for the PostScript language. */ - /* */ -/* #define FT_CONFIG_OPTION_INCREMENTAL */ - - - /*************************************************************************/ - /* */ - /* The size in bytes of the render pool used by the scan-line converter */ - /* to do all of its work. */ - /* */ -#define FT_RENDER_POOL_SIZE 16384L - - - /*************************************************************************/ - /* */ - /* FT_MAX_MODULES */ - /* */ - /* The maximum number of modules that can be registered in a single */ - /* FreeType library object. 32 is the default. */ - /* */ -#define FT_MAX_MODULES 32 - - - /*************************************************************************/ - /* */ - /* Debug level */ - /* */ - /* FreeType can be compiled in debug or trace mode. In debug mode, */ - /* errors are reported through the `ftdebug' component. In trace */ - /* mode, additional messages are sent to the standard output during */ - /* execution. */ - /* */ - /* Define FT_DEBUG_LEVEL_ERROR to build the library in debug mode. */ - /* Define FT_DEBUG_LEVEL_TRACE to build it in trace mode. */ - /* */ - /* Don't define any of these macros to compile in `release' mode! */ - /* */ - /* Do not #undef these macros here since the build system might define */ - /* them for certain configurations only. */ - /* */ -/* #define FT_DEBUG_LEVEL_ERROR */ -/* #define FT_DEBUG_LEVEL_TRACE */ - - - /*************************************************************************/ - /* */ - /* Autofitter debugging */ - /* */ - /* If FT_DEBUG_AUTOFIT is defined, FreeType provides some means to */ - /* control the autofitter behaviour for debugging purposes with global */ - /* boolean variables (consequently, you should *never* enable this */ - /* while compiling in `release' mode): */ - /* */ - /* _af_debug_disable_horz_hints */ - /* _af_debug_disable_vert_hints */ - /* _af_debug_disable_blue_hints */ - /* */ - /* Additionally, the following functions provide dumps of various */ - /* internal autofit structures to stdout (using `printf'): */ - /* */ - /* af_glyph_hints_dump_points */ - /* af_glyph_hints_dump_segments */ - /* af_glyph_hints_dump_edges */ - /* af_glyph_hints_get_num_segments */ - /* af_glyph_hints_get_segment_offset */ - /* */ - /* As an argument, they use another global variable: */ - /* */ - /* _af_debug_hints */ - /* */ - /* Please have a look at the `ftgrid' demo program to see how those */ - /* variables and macros should be used. */ - /* */ - /* Do not #undef these macros here since the build system might define */ - /* them for certain configurations only. */ - /* */ -/* #define FT_DEBUG_AUTOFIT */ - - - /*************************************************************************/ - /* */ - /* Memory Debugging */ - /* */ - /* FreeType now comes with an integrated memory debugger that is */ - /* capable of detecting simple errors like memory leaks or double */ - /* deletes. To compile it within your build of the library, you */ - /* should define FT_DEBUG_MEMORY here. */ - /* */ - /* Note that the memory debugger is only activated at runtime when */ - /* when the _environment_ variable `FT2_DEBUG_MEMORY' is defined also! */ - /* */ - /* Do not #undef this macro here since the build system might define */ - /* it for certain configurations only. */ - /* */ -/* #define FT_DEBUG_MEMORY */ - - - /*************************************************************************/ - /* */ - /* Module errors */ - /* */ - /* If this macro is set (which is _not_ the default), the higher byte */ - /* of an error code gives the module in which the error has occurred, */ - /* while the lower byte is the real error code. */ - /* */ - /* Setting this macro makes sense for debugging purposes only, since */ - /* it would break source compatibility of certain programs that use */ - /* FreeType 2. */ - /* */ - /* More details can be found in the files ftmoderr.h and fterrors.h. */ - /* */ -#undef FT_CONFIG_OPTION_USE_MODULE_ERRORS - - - /*************************************************************************/ - /* */ - /* Position Independent Code */ - /* */ - /* If this macro is set (which is _not_ the default), FreeType2 will */ - /* avoid creating constants that require address fixups. Instead the */ - /* constants will be moved into a struct and additional intialization */ - /* code will be used. */ - /* */ - /* Setting this macro is needed for systems that prohibit address */ - /* fixups, such as BREW. */ - /* */ -/* #define FT_CONFIG_OPTION_PIC */ - - - /*************************************************************************/ - /*************************************************************************/ - /**** ****/ - /**** S F N T D R I V E R C O N F I G U R A T I O N ****/ - /**** ****/ - /*************************************************************************/ - /*************************************************************************/ - - - /*************************************************************************/ - /* */ - /* Define TT_CONFIG_OPTION_EMBEDDED_BITMAPS if you want to support */ - /* embedded bitmaps in all formats using the SFNT module (namely */ - /* TrueType & OpenType). */ - /* */ -#define TT_CONFIG_OPTION_EMBEDDED_BITMAPS - - - /*************************************************************************/ - /* */ - /* Define TT_CONFIG_OPTION_POSTSCRIPT_NAMES if you want to be able to */ - /* load and enumerate the glyph Postscript names in a TrueType or */ - /* OpenType file. */ - /* */ - /* Note that when you do not compile the `PSNames' module by undefining */ - /* the above FT_CONFIG_OPTION_POSTSCRIPT_NAMES, the `sfnt' module will */ - /* contain additional code used to read the PS Names table from a font. */ - /* */ - /* (By default, the module uses `PSNames' to extract glyph names.) */ - /* */ -#define TT_CONFIG_OPTION_POSTSCRIPT_NAMES - - - /*************************************************************************/ - /* */ - /* Define TT_CONFIG_OPTION_SFNT_NAMES if your applications need to */ - /* access the internal name table in a SFNT-based format like TrueType */ - /* or OpenType. The name table contains various strings used to */ - /* describe the font, like family name, copyright, version, etc. It */ - /* does not contain any glyph name though. */ - /* */ - /* Accessing SFNT names is done through the functions declared in */ - /* `ftsnames.h'. */ - /* */ -#define TT_CONFIG_OPTION_SFNT_NAMES - - - /*************************************************************************/ - /* */ - /* TrueType CMap support */ - /* */ - /* Here you can fine-tune which TrueType CMap table format shall be */ - /* supported. */ -#define TT_CONFIG_CMAP_FORMAT_0 -#define TT_CONFIG_CMAP_FORMAT_2 -#define TT_CONFIG_CMAP_FORMAT_4 -#define TT_CONFIG_CMAP_FORMAT_6 -#define TT_CONFIG_CMAP_FORMAT_8 -#define TT_CONFIG_CMAP_FORMAT_10 -#define TT_CONFIG_CMAP_FORMAT_12 -#define TT_CONFIG_CMAP_FORMAT_13 -#define TT_CONFIG_CMAP_FORMAT_14 - - - /*************************************************************************/ - /*************************************************************************/ - /**** ****/ - /**** T R U E T Y P E D R I V E R C O N F I G U R A T I O N ****/ - /**** ****/ - /*************************************************************************/ - /*************************************************************************/ - - /*************************************************************************/ - /* */ - /* Define TT_CONFIG_OPTION_BYTECODE_INTERPRETER if you want to compile */ - /* a bytecode interpreter in the TrueType driver. */ - /* */ - /* By undefining this, you will only compile the code necessary to load */ - /* TrueType glyphs without hinting. */ - /* */ - /* Do not #undef this macro here, since the build system might */ - /* define it for certain configurations only. */ - /* */ -#define TT_CONFIG_OPTION_BYTECODE_INTERPRETER - - - /*************************************************************************/ - /* */ - /* Define TT_CONFIG_OPTION_SUBPIXEL_HINTING if you want to compile */ - /* EXPERIMENTAL subpixel hinting support into the TrueType driver. This */ - /* replaces the native TrueType hinting mechanism when anything but */ - /* FT_RENDER_MODE_MONO is requested. */ - /* */ - /* Enabling this causes the TrueType driver to ignore instructions under */ - /* certain conditions. This is done in accordance with the guide here, */ - /* with some minor differences: */ - /* */ - /* http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx */ - /* */ - /* By undefining this, you only compile the code necessary to hint */ - /* TrueType glyphs with native TT hinting. */ - /* */ - /* This option requires TT_CONFIG_OPTION_BYTECODE_INTERPRETER to be */ - /* defined. */ - /* */ -/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING */ - - - /*************************************************************************/ - /* */ - /* If you define TT_CONFIG_OPTION_UNPATENTED_HINTING, a special version */ - /* of the TrueType bytecode interpreter is used that doesn't implement */ - /* any of the patented opcodes and algorithms. The patents related to */ - /* TrueType hinting have expired worldwide since May 2010; this option */ - /* is now deprecated. */ - /* */ - /* Note that the TT_CONFIG_OPTION_UNPATENTED_HINTING macro is *ignored* */ - /* if you define TT_CONFIG_OPTION_BYTECODE_INTERPRETER; in other words, */ - /* either define TT_CONFIG_OPTION_BYTECODE_INTERPRETER or */ - /* TT_CONFIG_OPTION_UNPATENTED_HINTING but not both at the same time. */ - /* */ - /* This macro is only useful for a small number of font files (mostly */ - /* for Asian scripts) that require bytecode interpretation to properly */ - /* load glyphs. For all other fonts, this produces unpleasant results, */ - /* thus the unpatented interpreter is never used to load glyphs from */ - /* TrueType fonts unless one of the following two options is used. */ - /* */ - /* - The unpatented interpreter is explicitly activated by the user */ - /* through the FT_PARAM_TAG_UNPATENTED_HINTING parameter tag */ - /* when opening the FT_Face. */ - /* */ - /* - FreeType detects that the FT_Face corresponds to one of the */ - /* `trick' fonts (e.g., `Mingliu') it knows about. The font engine */ - /* contains a hard-coded list of font names and other matching */ - /* parameters (see function `tt_face_init' in file */ - /* `src/truetype/ttobjs.c'). */ - /* */ - /* Here a sample code snippet for using FT_PARAM_TAG_UNPATENTED_HINTING. */ - /* */ - /* { */ - /* FT_Parameter parameter; */ - /* FT_Open_Args open_args; */ - /* */ - /* */ - /* parameter.tag = FT_PARAM_TAG_UNPATENTED_HINTING; */ - /* */ - /* open_args.flags = FT_OPEN_PATHNAME | FT_OPEN_PARAMS; */ - /* open_args.pathname = my_font_pathname; */ - /* open_args.num_params = 1; */ - /* open_args.params = ¶meter; */ - /* */ - /* error = FT_Open_Face( library, &open_args, index, &face ); */ - /* ... */ - /* } */ - /* */ -/* #define TT_CONFIG_OPTION_UNPATENTED_HINTING */ - - - /*************************************************************************/ - /* */ - /* Define TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED to compile the */ - /* TrueType glyph loader to use Apple's definition of how to handle */ - /* component offsets in composite glyphs. */ - /* */ - /* Apple and MS disagree on the default behavior of component offsets */ - /* in composites. Apple says that they should be scaled by the scaling */ - /* factors in the transformation matrix (roughly, it's more complex) */ - /* while MS says they should not. OpenType defines two bits in the */ - /* composite flags array which can be used to disambiguate, but old */ - /* fonts will not have them. */ - /* */ - /* http://www.microsoft.com/typography/otspec/glyf.htm */ - /* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html */ - /* */ -#undef TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED - - - /*************************************************************************/ - /* */ - /* Define TT_CONFIG_OPTION_GX_VAR_SUPPORT if you want to include */ - /* support for Apple's distortable font technology (fvar, gvar, cvar, */ - /* and avar tables). This has many similarities to Type 1 Multiple */ - /* Masters support. */ - /* */ -#define TT_CONFIG_OPTION_GX_VAR_SUPPORT - - - /*************************************************************************/ - /* */ - /* Define TT_CONFIG_OPTION_BDF if you want to include support for */ - /* an embedded `BDF ' table within SFNT-based bitmap formats. */ - /* */ -#define TT_CONFIG_OPTION_BDF - - - /*************************************************************************/ - /*************************************************************************/ - /**** ****/ - /**** T Y P E 1 D R I V E R C O N F I G U R A T I O N ****/ - /**** ****/ - /*************************************************************************/ - /*************************************************************************/ - - - /*************************************************************************/ - /* */ - /* T1_MAX_DICT_DEPTH is the maximum depth of nest dictionaries and */ - /* arrays in the Type 1 stream (see t1load.c). A minimum of 4 is */ - /* required. */ - /* */ -#define T1_MAX_DICT_DEPTH 5 - - - /*************************************************************************/ - /* */ - /* T1_MAX_SUBRS_CALLS details the maximum number of nested sub-routine */ - /* calls during glyph loading. */ - /* */ -#define T1_MAX_SUBRS_CALLS 16 - - - /*************************************************************************/ - /* */ - /* T1_MAX_CHARSTRING_OPERANDS is the charstring stack's capacity. A */ - /* minimum of 16 is required. */ - /* */ - /* The Chinese font MingTiEG-Medium (CNS 11643 character set) needs 256. */ - /* */ -#define T1_MAX_CHARSTRINGS_OPERANDS 256 - - - /*************************************************************************/ - /* */ - /* Define this configuration macro if you want to prevent the */ - /* compilation of `t1afm', which is in charge of reading Type 1 AFM */ - /* files into an existing face. Note that if set, the T1 driver will be */ - /* unable to produce kerning distances. */ - /* */ -#undef T1_CONFIG_OPTION_NO_AFM - - - /*************************************************************************/ - /* */ - /* Define this configuration macro if you want to prevent the */ - /* compilation of the Multiple Masters font support in the Type 1 */ - /* driver. */ - /* */ -#undef T1_CONFIG_OPTION_NO_MM_SUPPORT - - - /*************************************************************************/ - /*************************************************************************/ - /**** ****/ - /**** C F F D R I V E R C O N F I G U R A T I O N ****/ - /**** ****/ - /*************************************************************************/ - /*************************************************************************/ - - - /*************************************************************************/ - /* */ - /* Using CFF_CONFIG_OPTION_DARKENING_PARAMETER_{X,Y}{1,2,3,4} it is */ - /* possible to set up the default values of the four control points that */ - /* define the stem darkening behaviour of the (new) CFF engine. For */ - /* more details please read the documentation of the */ - /* `darkening-parameters' property of the cff driver module (file */ - /* `ftcffdrv.h'), which allows the control at run-time. */ - /* */ - /* Do *not* undefine these macros! */ - /* */ -#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 500 -#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 400 - -#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 1000 -#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 275 - -#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 1667 -#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 275 - -#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 2333 -#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 0 - - - /*************************************************************************/ - /* */ - /* CFF_CONFIG_OPTION_OLD_ENGINE controls whether the pre-Adobe CFF */ - /* engine gets compiled into FreeType. If defined, it is possible to */ - /* switch between the two engines using the `hinting-engine' property of */ - /* the cff driver module. */ - /* */ -/* #define CFF_CONFIG_OPTION_OLD_ENGINE */ - - - /*************************************************************************/ - /*************************************************************************/ - /**** ****/ - /**** A U T O F I T M O D U L E C O N F I G U R A T I O N ****/ - /**** ****/ - /*************************************************************************/ - /*************************************************************************/ - - - /*************************************************************************/ - /* */ - /* Compile autofit module with CJK (Chinese, Japanese, Korean) script */ - /* support. */ - /* */ -#define AF_CONFIG_OPTION_CJK - - /*************************************************************************/ - /* */ - /* Compile autofit module with Indic script support. */ - /* */ -#define AF_CONFIG_OPTION_INDIC - - /*************************************************************************/ - /* */ - /* Compile autofit module with warp hinting. The idea of the warping */ - /* code is to slightly scale and shift a glyph within a single dimension */ - /* so that as much of its segments are aligned (more or less) on the */ - /* grid. To find out the optimal scaling and shifting value, various */ - /* parameter combinations are tried and scored. */ - /* */ - /* This experimental option is active only if the rendering mode is */ - /* FT_RENDER_MODE_LIGHT; you can switch warping on and off with the */ - /* `warping' property of the auto-hinter (see file `ftautoh.h' for more */ - /* information; by default it is switched off). */ - /* */ -/*#define AF_CONFIG_OPTION_USE_WARPER*/ - - /* */ - - - /* - * This macro is obsolete. Support has been removed in FreeType - * version 2.5. - */ -/* #define FT_CONFIG_OPTION_OLD_INTERNALS */ - - - /* - * This macro is defined if either unpatented or native TrueType - * hinting is requested by the definitions above. - */ -#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER -#define TT_USE_BYTECODE_INTERPRETER -#undef TT_CONFIG_OPTION_UNPATENTED_HINTING -#elif defined TT_CONFIG_OPTION_UNPATENTED_HINTING -#define TT_USE_BYTECODE_INTERPRETER -#endif - - - /* - * Check CFF darkening parameters. The checks are the same as in function - * `cff_property_set' in file `cffdrivr.c'. - */ -#if CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 < 0 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 < 0 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 < 0 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 < 0 || \ - \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 < 0 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 < 0 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 < 0 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 < 0 || \ - \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 > \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 > \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 > \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 || \ - \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 > 500 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 > 500 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 > 500 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 > 500 -#error "Invalid CFF darkening parameters!" -#endif - -FT_END_HEADER - - -#endif /* COBALT___FTOPTION_H__ */ - - -/* END */ +/***************************************************************************/ +/* */ +/* ftoption.h */ +/* */ +/* User-selectable configuration macros (specification only). */ +/* */ +/* Copyright 1996-2015 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#ifndef COBALT___FTOPTION_H__ +#define COBALT___FTOPTION_H__ + +#include <ft2build.h> + +FT_BEGIN_HEADER + +/*************************************************************************/ +/* */ +/* USER-SELECTABLE CONFIGURATION MACROS */ +/* */ +/* This file contains the default configuration macro definitions for */ +/* a standard build of the FreeType library. There are three ways to */ +/* use this file to build project-specific versions of the library: */ +/* */ +/* - You can modify this file by hand, but this is not recommended in */ +/* cases where you would like to build several versions of the */ +/* library from a single source directory. */ +/* */ +/* - You can put a copy of this file in your build directory, more */ +/* precisely in `$BUILD/config/ftoption.h', where `$BUILD' is the */ +/* name of a directory that is included _before_ the FreeType include */ +/* path during compilation. */ +/* */ +/* The default FreeType Makefiles and Jamfiles use the build */ +/* directory `builds/<system>' by default, but you can easily change */ +/* that for your own projects. */ +/* */ +/* - Copy the file <ft2build.h> to `$BUILD/ft2build.h' and modify it */ +/* slightly to pre-define the macro FT_CONFIG_OPTIONS_H used to */ +/* locate this file during the build. For example, */ +/* */ +/* #define FT_CONFIG_OPTIONS_H <myftoptions.h> */ +/* #include <config/ftheader.h> */ +/* */ +/* will use `$BUILD/myftoptions.h' instead of this file for macro */ +/* definitions. */ +/* */ +/* Note also that you can similarly pre-define the macro */ +/* FT_CONFIG_MODULES_H used to locate the file listing of the modules */ +/* that are statically linked to the library at compile time. By */ +/* default, this file is <config/ftmodule.h>. */ +/* */ +/* We highly recommend using the third method whenever possible. */ +/* */ +/*************************************************************************/ + +/*************************************************************************/ +/*************************************************************************/ +/**** ****/ +/**** G E N E R A L F R E E T Y P E 2 C O N F I G U R A T I O N ****/ +/**** ****/ +/*************************************************************************/ +/*************************************************************************/ + +/*************************************************************************/ +/* */ +/* Uncomment the line below if you want to activate sub-pixel rendering */ +/* (a.k.a. LCD rendering, or ClearType) in this build of the library. */ +/* */ +/* Note that this feature is covered by several Microsoft patents */ +/* and should not be activated in any default build of the library. */ +/* */ +/* This macro has no impact on the FreeType API, only on its */ +/* _implementation_. For example, using FT_RENDER_MODE_LCD when calling */ +/* FT_Render_Glyph still generates a bitmap that is 3 times wider than */ +/* the original size in case this macro isn't defined; however, each */ +/* triplet of subpixels has R=G=B. */ +/* */ +/* This is done to allow FreeType clients to run unmodified, forcing */ +/* them to display normal gray-level anti-aliased glyphs. */ +/* */ +/* #define FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ + +/*************************************************************************/ +/* */ +/* Many compilers provide a non-ANSI 64-bit data type that can be used */ +/* by FreeType to speed up some computations. However, this will create */ +/* some problems when compiling the library in strict ANSI mode. */ +/* */ +/* For this reason, the use of 64-bit integers is normally disabled when */ +/* the __STDC__ macro is defined. You can however disable this by */ +/* defining the macro FT_CONFIG_OPTION_FORCE_INT64 here. */ +/* */ +/* For most compilers, this will only create compilation warnings when */ +/* building the library. */ +/* */ +/* ObNote: The compiler-specific 64-bit integers are detected in the */ +/* file `ftconfig.h' either statically or through the */ +/* `configure' script on supported platforms. */ +/* */ +#undef FT_CONFIG_OPTION_FORCE_INT64 + +/*************************************************************************/ +/* */ +/* If this macro is defined, do not try to use an assembler version of */ +/* performance-critical functions (e.g. FT_MulFix). You should only do */ +/* that to verify that the assembler function works properly, or to */ +/* execute benchmark tests of the various implementations. */ +/* #define FT_CONFIG_OPTION_NO_ASSEMBLER */ + +/*************************************************************************/ +/* */ +/* If this macro is defined, try to use an inlined assembler version of */ +/* the `FT_MulFix' function, which is a `hotspot' when loading and */ +/* hinting glyphs, and which should be executed as fast as possible. */ +/* */ +/* Note that if your compiler or CPU is not supported, this will default */ +/* to the standard and portable implementation found in `ftcalc.c'. */ +/* */ +#define FT_CONFIG_OPTION_INLINE_MULFIX + +/*************************************************************************/ +/* */ +/* LZW-compressed file support. */ +/* */ +/* FreeType now handles font files that have been compressed with the */ +/* `compress' program. This is mostly used to parse many of the PCF */ +/* files that come with various X11 distributions. The implementation */ +/* uses NetBSD's `zopen' to partially uncompress the file on the fly */ +/* (see src/lzw/ftgzip.c). */ +/* */ +/* Define this macro if you want to enable this `feature'. */ +/* */ +#define FT_CONFIG_OPTION_USE_LZW + +/*************************************************************************/ +/* */ +/* Gzip-compressed file support. */ +/* */ +/* FreeType now handles font files that have been compressed with the */ +/* `gzip' program. This is mostly used to parse many of the PCF files */ +/* that come with XFree86. The implementation uses `zlib' to */ +/* partially uncompress the file on the fly (see src/gzip/ftgzip.c). */ +/* */ +/* Define this macro if you want to enable this `feature'. See also */ +/* the macro FT_CONFIG_OPTION_SYSTEM_ZLIB below. */ +/* */ +#define FT_CONFIG_OPTION_USE_ZLIB + +/*************************************************************************/ +/* */ +/* ZLib library selection */ +/* */ +/* This macro is only used when FT_CONFIG_OPTION_USE_ZLIB is defined. */ +/* It allows FreeType's `ftgzip' component to link to the system's */ +/* installation of the ZLib library. This is useful on systems like */ +/* Unix or VMS where it generally is already available. */ +/* */ +/* If you let it undefined, the component will use its own copy */ +/* of the zlib sources instead. These have been modified to be */ +/* included directly within the component and *not* export external */ +/* function names. This allows you to link any program with FreeType */ +/* _and_ ZLib without linking conflicts. */ +/* */ +/* Do not #undef this macro here since the build system might define */ +/* it for certain configurations only. */ +/* */ +/* #define FT_CONFIG_OPTION_SYSTEM_ZLIB */ + +/*************************************************************************/ +/* */ +/* Bzip2-compressed file support. */ +/* */ +/* FreeType now handles font files that have been compressed with the */ +/* `bzip2' program. This is mostly used to parse many of the PCF */ +/* files that come with XFree86. The implementation uses `libbz2' to */ +/* partially uncompress the file on the fly (see src/bzip2/ftbzip2.c). */ +/* Contrary to gzip, bzip2 currently is not included and need to use */ +/* the system available bzip2 implementation. */ +/* */ +/* Define this macro if you want to enable this `feature'. */ +/* */ +/* #define FT_CONFIG_OPTION_USE_BZIP2 */ + +/*************************************************************************/ +/* */ +/* Define to disable the use of file stream functions and types, FILE, */ +/* fopen() etc. Enables the use of smaller system libraries on embedded */ +/* systems that have multiple system libraries, some with or without */ +/* file stream support, in the cases where file stream support is not */ +/* necessary such as memory loading of font files. */ +/* */ +/* #define FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT */ + +/*************************************************************************/ +/* */ +/* PNG bitmap support. */ +/* */ +/* FreeType now handles loading color bitmap glyphs in the PNG format. */ +/* This requires help from the external libpng library. Uncompressed */ +/* color bitmaps do not need any external libraries and will be */ +/* supported regardless of this configuration. */ +/* */ +/* Define this macro if you want to enable this `feature'. */ +/* */ +#define FT_CONFIG_OPTION_USE_PNG + +/*************************************************************************/ +/* */ +/* HarfBuzz support. */ +/* */ +/* FreeType uses the HarfBuzz library to improve auto-hinting of */ +/* OpenType fonts. If available, many glyphs not directly addressable */ +/* by a font's character map will be hinted also. */ +/* */ +/* Define this macro if you want to enable this `feature'. */ +/* */ +/* #define FT_CONFIG_OPTION_USE_HARFBUZZ */ + +/*************************************************************************/ +/* */ +/* DLL export compilation */ +/* */ +/* When compiling FreeType as a DLL, some systems/compilers need a */ +/* special keyword in front OR after the return type of function */ +/* declarations. */ +/* */ +/* Two macros are used within the FreeType source code to define */ +/* exported library functions: FT_EXPORT and FT_EXPORT_DEF. */ +/* */ +/* FT_EXPORT( return_type ) */ +/* */ +/* is used in a function declaration, as in */ +/* */ +/* FT_EXPORT( FT_Error ) */ +/* FT_Init_FreeType( FT_Library* alibrary ); */ +/* */ +/* */ +/* FT_EXPORT_DEF( return_type ) */ +/* */ +/* is used in a function definition, as in */ +/* */ +/* FT_EXPORT_DEF( FT_Error ) */ +/* FT_Init_FreeType( FT_Library* alibrary ) */ +/* { */ +/* ... some code ... */ +/* return FT_Err_Ok; */ +/* } */ +/* */ +/* You can provide your own implementation of FT_EXPORT and */ +/* FT_EXPORT_DEF here if you want. If you leave them undefined, they */ +/* will be later automatically defined as `extern return_type' to */ +/* allow normal compilation. */ +/* */ +/* Do not #undef these macros here since the build system might define */ +/* them for certain configurations only. */ +/* */ +/* #define FT_EXPORT(x) extern x */ +/* #define FT_EXPORT_DEF(x) x */ + +/*************************************************************************/ +/* */ +/* Glyph Postscript Names handling */ +/* */ +/* By default, FreeType 2 is compiled with the `psnames' module. This */ +/* module is in charge of converting a glyph name string into a */ +/* Unicode value, or return a Macintosh standard glyph name for the */ +/* use with the TrueType `post' table. */ +/* */ +/* Undefine this macro if you do not want `psnames' compiled in your */ +/* build of FreeType. This has the following effects: */ +/* */ +/* - The TrueType driver will provide its own set of glyph names, */ +/* if you build it to support postscript names in the TrueType */ +/* `post' table. */ +/* */ +/* - The Type 1 driver will not be able to synthesize a Unicode */ +/* charmap out of the glyphs found in the fonts. */ +/* */ +/* You would normally undefine this configuration macro when building */ +/* a version of FreeType that doesn't contain a Type 1 or CFF driver. */ +/* */ +#define FT_CONFIG_OPTION_POSTSCRIPT_NAMES + +/*************************************************************************/ +/* */ +/* Postscript Names to Unicode Values support */ +/* */ +/* By default, FreeType 2 is built with the `PSNames' module compiled */ +/* in. Among other things, the module is used to convert a glyph name */ +/* into a Unicode value. This is especially useful in order to */ +/* synthesize on the fly a Unicode charmap from the CFF/Type 1 driver */ +/* through a big table named the `Adobe Glyph List' (AGL). */ +/* */ +/* Undefine this macro if you do not want the Adobe Glyph List */ +/* compiled in your `PSNames' module. The Type 1 driver will not be */ +/* able to synthesize a Unicode charmap out of the glyphs found in the */ +/* fonts. */ +/* */ +#define FT_CONFIG_OPTION_ADOBE_GLYPH_LIST + +/*************************************************************************/ +/* */ +/* Support for Mac fonts */ +/* */ +/* Define this macro if you want support for outline fonts in Mac */ +/* format (mac dfont, mac resource, macbinary containing a mac */ +/* resource) on non-Mac platforms. */ +/* */ +/* Note that the `FOND' resource isn't checked. */ +/* */ +#define FT_CONFIG_OPTION_MAC_FONTS + +/*************************************************************************/ +/* */ +/* Guessing methods to access embedded resource forks */ +/* */ +/* Enable extra Mac fonts support on non-Mac platforms (e.g. */ +/* GNU/Linux). */ +/* */ +/* Resource forks which include fonts data are stored sometimes in */ +/* locations which users or developers don't expected. In some cases, */ +/* resource forks start with some offset from the head of a file. In */ +/* other cases, the actual resource fork is stored in file different */ +/* from what the user specifies. If this option is activated, */ +/* FreeType tries to guess whether such offsets or different file */ +/* names must be used. */ +/* */ +/* Note that normal, direct access of resource forks is controlled via */ +/* the FT_CONFIG_OPTION_MAC_FONTS option. */ +/* */ +#ifdef FT_CONFIG_OPTION_MAC_FONTS +#define FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK +#endif + +/*************************************************************************/ +/* */ +/* Allow the use of FT_Incremental_Interface to load typefaces that */ +/* contain no glyph data, but supply it via a callback function. */ +/* This is required by clients supporting document formats which */ +/* supply font data incrementally as the document is parsed, such */ +/* as the Ghostscript interpreter for the PostScript language. */ +/* */ +/* #define FT_CONFIG_OPTION_INCREMENTAL */ + +/*************************************************************************/ +/* */ +/* The size in bytes of the render pool used by the scan-line converter */ +/* to do all of its work. */ +/* */ +#define FT_RENDER_POOL_SIZE 16384L + +/*************************************************************************/ +/* */ +/* FT_MAX_MODULES */ +/* */ +/* The maximum number of modules that can be registered in a single */ +/* FreeType library object. 32 is the default. */ +/* */ +#define FT_MAX_MODULES 32 + +/*************************************************************************/ +/* */ +/* Debug level */ +/* */ +/* FreeType can be compiled in debug or trace mode. In debug mode, */ +/* errors are reported through the `ftdebug' component. In trace */ +/* mode, additional messages are sent to the standard output during */ +/* execution. */ +/* */ +/* Define FT_DEBUG_LEVEL_ERROR to build the library in debug mode. */ +/* Define FT_DEBUG_LEVEL_TRACE to build it in trace mode. */ +/* */ +/* Don't define any of these macros to compile in `release' mode! */ +/* */ +/* Do not #undef these macros here since the build system might define */ +/* them for certain configurations only. */ +/* */ +/* #define FT_DEBUG_LEVEL_ERROR */ +/* #define FT_DEBUG_LEVEL_TRACE */ + +/*************************************************************************/ +/* */ +/* Autofitter debugging */ +/* */ +/* If FT_DEBUG_AUTOFIT is defined, FreeType provides some means to */ +/* control the autofitter behaviour for debugging purposes with global */ +/* boolean variables (consequently, you should *never* enable this */ +/* while compiling in `release' mode): */ +/* */ +/* _af_debug_disable_horz_hints */ +/* _af_debug_disable_vert_hints */ +/* _af_debug_disable_blue_hints */ +/* */ +/* Additionally, the following functions provide dumps of various */ +/* internal autofit structures to stdout (using `printf'): */ +/* */ +/* af_glyph_hints_dump_points */ +/* af_glyph_hints_dump_segments */ +/* af_glyph_hints_dump_edges */ +/* af_glyph_hints_get_num_segments */ +/* af_glyph_hints_get_segment_offset */ +/* */ +/* As an argument, they use another global variable: */ +/* */ +/* _af_debug_hints */ +/* */ +/* Please have a look at the `ftgrid' demo program to see how those */ +/* variables and macros should be used. */ +/* */ +/* Do not #undef these macros here since the build system might define */ +/* them for certain configurations only. */ +/* */ +/* #define FT_DEBUG_AUTOFIT */ + +/*************************************************************************/ +/* */ +/* Memory Debugging */ +/* */ +/* FreeType now comes with an integrated memory debugger that is */ +/* capable of detecting simple errors like memory leaks or double */ +/* deletes. To compile it within your build of the library, you */ +/* should define FT_DEBUG_MEMORY here. */ +/* */ +/* Note that the memory debugger is only activated at runtime when */ +/* when the _environment_ variable `FT2_DEBUG_MEMORY' is defined also! */ +/* */ +/* Do not #undef this macro here since the build system might define */ +/* it for certain configurations only. */ +/* */ +/* #define FT_DEBUG_MEMORY */ + +/*************************************************************************/ +/* */ +/* Module errors */ +/* */ +/* If this macro is set (which is _not_ the default), the higher byte */ +/* of an error code gives the module in which the error has occurred, */ +/* while the lower byte is the real error code. */ +/* */ +/* Setting this macro makes sense for debugging purposes only, since */ +/* it would break source compatibility of certain programs that use */ +/* FreeType 2. */ +/* */ +/* More details can be found in the files ftmoderr.h and fterrors.h. */ +/* */ +#undef FT_CONFIG_OPTION_USE_MODULE_ERRORS + +/*************************************************************************/ +/* */ +/* Position Independent Code */ +/* */ +/* If this macro is set (which is _not_ the default), FreeType2 will */ +/* avoid creating constants that require address fixups. Instead the */ +/* constants will be moved into a struct and additional intialization */ +/* code will be used. */ +/* */ +/* Setting this macro is needed for systems that prohibit address */ +/* fixups, such as BREW. */ +/* */ +/* #define FT_CONFIG_OPTION_PIC */ + +/*************************************************************************/ +/*************************************************************************/ +/**** ****/ +/**** S F N T D R I V E R C O N F I G U R A T I O N ****/ +/**** ****/ +/*************************************************************************/ +/*************************************************************************/ + +/*************************************************************************/ +/* */ +/* Define TT_CONFIG_OPTION_EMBEDDED_BITMAPS if you want to support */ +/* embedded bitmaps in all formats using the SFNT module (namely */ +/* TrueType & OpenType). */ +/* */ +#define TT_CONFIG_OPTION_EMBEDDED_BITMAPS + +/*************************************************************************/ +/* */ +/* Define TT_CONFIG_OPTION_POSTSCRIPT_NAMES if you want to be able to */ +/* load and enumerate the glyph Postscript names in a TrueType or */ +/* OpenType file. */ +/* */ +/* Note that when you do not compile the `PSNames' module by undefining */ +/* the above FT_CONFIG_OPTION_POSTSCRIPT_NAMES, the `sfnt' module will */ +/* contain additional code used to read the PS Names table from a font. */ +/* */ +/* (By default, the module uses `PSNames' to extract glyph names.) */ +/* */ +#define TT_CONFIG_OPTION_POSTSCRIPT_NAMES + +/*************************************************************************/ +/* */ +/* Define TT_CONFIG_OPTION_SFNT_NAMES if your applications need to */ +/* access the internal name table in a SFNT-based format like TrueType */ +/* or OpenType. The name table contains various strings used to */ +/* describe the font, like family name, copyright, version, etc. It */ +/* does not contain any glyph name though. */ +/* */ +/* Accessing SFNT names is done through the functions declared in */ +/* `ftsnames.h'. */ +/* */ +#define TT_CONFIG_OPTION_SFNT_NAMES + +/*************************************************************************/ +/* */ +/* TrueType CMap support */ +/* */ +/* Here you can fine-tune which TrueType CMap table format shall be */ +/* supported. */ +#define TT_CONFIG_CMAP_FORMAT_0 +#define TT_CONFIG_CMAP_FORMAT_2 +#define TT_CONFIG_CMAP_FORMAT_4 +#define TT_CONFIG_CMAP_FORMAT_6 +#define TT_CONFIG_CMAP_FORMAT_8 +#define TT_CONFIG_CMAP_FORMAT_10 +#define TT_CONFIG_CMAP_FORMAT_12 +#define TT_CONFIG_CMAP_FORMAT_13 +#define TT_CONFIG_CMAP_FORMAT_14 + +/*************************************************************************/ +/*************************************************************************/ +/**** ****/ +/**** T R U E T Y P E D R I V E R C O N F I G U R A T I O N ****/ +/**** ****/ +/*************************************************************************/ +/*************************************************************************/ + +/*************************************************************************/ +/* */ +/* Define TT_CONFIG_OPTION_BYTECODE_INTERPRETER if you want to compile */ +/* a bytecode interpreter in the TrueType driver. */ +/* */ +/* By undefining this, you will only compile the code necessary to load */ +/* TrueType glyphs without hinting. */ +/* */ +/* Do not #undef this macro here, since the build system might */ +/* define it for certain configurations only. */ +/* */ +#define TT_CONFIG_OPTION_BYTECODE_INTERPRETER + +/*************************************************************************/ +/* */ +/* Define TT_CONFIG_OPTION_SUBPIXEL_HINTING if you want to compile */ +/* EXPERIMENTAL subpixel hinting support into the TrueType driver. This */ +/* replaces the native TrueType hinting mechanism when anything but */ +/* FT_RENDER_MODE_MONO is requested. */ +/* */ +/* Enabling this causes the TrueType driver to ignore instructions under */ +/* certain conditions. This is done in accordance with the guide here, */ +/* with some minor differences: */ +/* */ +/* http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx */ +/* */ +/* By undefining this, you only compile the code necessary to hint */ +/* TrueType glyphs with native TT hinting. */ +/* */ +/* This option requires TT_CONFIG_OPTION_BYTECODE_INTERPRETER to be */ +/* defined. */ +/* */ +/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + +/*************************************************************************/ +/* */ +/* If you define TT_CONFIG_OPTION_UNPATENTED_HINTING, a special version */ +/* of the TrueType bytecode interpreter is used that doesn't implement */ +/* any of the patented opcodes and algorithms. The patents related to */ +/* TrueType hinting have expired worldwide since May 2010; this option */ +/* is now deprecated. */ +/* */ +/* Note that the TT_CONFIG_OPTION_UNPATENTED_HINTING macro is *ignored* */ +/* if you define TT_CONFIG_OPTION_BYTECODE_INTERPRETER; in other words, */ +/* either define TT_CONFIG_OPTION_BYTECODE_INTERPRETER or */ +/* TT_CONFIG_OPTION_UNPATENTED_HINTING but not both at the same time. */ +/* */ +/* This macro is only useful for a small number of font files (mostly */ +/* for Asian scripts) that require bytecode interpretation to properly */ +/* load glyphs. For all other fonts, this produces unpleasant results, */ +/* thus the unpatented interpreter is never used to load glyphs from */ +/* TrueType fonts unless one of the following two options is used. */ +/* */ +/* - The unpatented interpreter is explicitly activated by the user */ +/* through the FT_PARAM_TAG_UNPATENTED_HINTING parameter tag */ +/* when opening the FT_Face. */ +/* */ +/* - FreeType detects that the FT_Face corresponds to one of the */ +/* `trick' fonts (e.g., `Mingliu') it knows about. The font engine */ +/* contains a hard-coded list of font names and other matching */ +/* parameters (see function `tt_face_init' in file */ +/* `src/truetype/ttobjs.c'). */ +/* */ +/* Here a sample code snippet for using FT_PARAM_TAG_UNPATENTED_HINTING. */ +/* */ +/* { */ +/* FT_Parameter parameter; */ +/* FT_Open_Args open_args; */ +/* */ +/* */ +/* parameter.tag = FT_PARAM_TAG_UNPATENTED_HINTING; */ +/* */ +/* open_args.flags = FT_OPEN_PATHNAME | FT_OPEN_PARAMS; */ +/* open_args.pathname = my_font_pathname; */ +/* open_args.num_params = 1; */ +/* open_args.params = ¶meter; */ +/* */ +/* error = FT_Open_Face( library, &open_args, index, &face ); */ +/* ... */ +/* } */ +/* */ +/* #define TT_CONFIG_OPTION_UNPATENTED_HINTING */ + +/*************************************************************************/ +/* */ +/* Define TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED to compile the */ +/* TrueType glyph loader to use Apple's definition of how to handle */ +/* component offsets in composite glyphs. */ +/* */ +/* Apple and MS disagree on the default behavior of component offsets */ +/* in composites. Apple says that they should be scaled by the scaling */ +/* factors in the transformation matrix (roughly, it's more complex) */ +/* while MS says they should not. OpenType defines two bits in the */ +/* composite flags array which can be used to disambiguate, but old */ +/* fonts will not have them. */ +/* */ +/* http://www.microsoft.com/typography/otspec/glyf.htm */ +/* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html + */ +/* */ +#undef TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED + +/*************************************************************************/ +/* */ +/* Define TT_CONFIG_OPTION_GX_VAR_SUPPORT if you want to include */ +/* support for Apple's distortable font technology (fvar, gvar, cvar, */ +/* and avar tables). This has many similarities to Type 1 Multiple */ +/* Masters support. */ +/* */ +#define TT_CONFIG_OPTION_GX_VAR_SUPPORT + +/*************************************************************************/ +/* */ +/* Define TT_CONFIG_OPTION_BDF if you want to include support for */ +/* an embedded `BDF ' table within SFNT-based bitmap formats. */ +/* */ +#define TT_CONFIG_OPTION_BDF + +/*************************************************************************/ +/* */ +/* Option TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES controls the maximum */ +/* number of bytecode instructions executed for a single run of the */ +/* bytecode interpreter, needed to prevent infinite loops. You don't */ +/* want to change this except for very special situations (e.g., making */ +/* a library fuzzer spend less time to handle broken fonts). */ +/* */ +/* It is not expected that this value is ever modified by a configuring */ +/* script; instead, it gets surrounded with #ifndef ... #endif so that */ +/* the value can be set as a preprocessor option on the compiler's */ +/* command line. */ +/* */ +#ifndef TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES +#define TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES 1000000L +#endif + +/*************************************************************************/ +/*************************************************************************/ +/**** ****/ +/**** T Y P E 1 D R I V E R C O N F I G U R A T I O N ****/ +/**** ****/ +/*************************************************************************/ +/*************************************************************************/ + +/*************************************************************************/ +/* */ +/* T1_MAX_DICT_DEPTH is the maximum depth of nest dictionaries and */ +/* arrays in the Type 1 stream (see t1load.c). A minimum of 4 is */ +/* required. */ +/* */ +#define T1_MAX_DICT_DEPTH 5 + +/*************************************************************************/ +/* */ +/* T1_MAX_SUBRS_CALLS details the maximum number of nested sub-routine */ +/* calls during glyph loading. */ +/* */ +#define T1_MAX_SUBRS_CALLS 16 + +/*************************************************************************/ +/* */ +/* T1_MAX_CHARSTRING_OPERANDS is the charstring stack's capacity. A */ +/* minimum of 16 is required. */ +/* */ +/* The Chinese font MingTiEG-Medium (CNS 11643 character set) needs 256. */ +/* */ +#define T1_MAX_CHARSTRINGS_OPERANDS 256 + +/*************************************************************************/ +/* */ +/* Define this configuration macro if you want to prevent the */ +/* compilation of `t1afm', which is in charge of reading Type 1 AFM */ +/* files into an existing face. Note that if set, the T1 driver will be */ +/* unable to produce kerning distances. */ +/* */ +#undef T1_CONFIG_OPTION_NO_AFM + +/*************************************************************************/ +/* */ +/* Define this configuration macro if you want to prevent the */ +/* compilation of the Multiple Masters font support in the Type 1 */ +/* driver. */ +/* */ +#undef T1_CONFIG_OPTION_NO_MM_SUPPORT + +/*************************************************************************/ +/*************************************************************************/ +/**** ****/ +/**** C F F D R I V E R C O N F I G U R A T I O N ****/ +/**** ****/ +/*************************************************************************/ +/*************************************************************************/ + +/*************************************************************************/ +/* */ +/* Using CFF_CONFIG_OPTION_DARKENING_PARAMETER_{X,Y}{1,2,3,4} it is */ +/* possible to set up the default values of the four control points that */ +/* define the stem darkening behaviour of the (new) CFF engine. For */ +/* more details please read the documentation of the */ +/* `darkening-parameters' property of the cff driver module (file */ +/* `ftcffdrv.h'), which allows the control at run-time. */ +/* */ +/* Do *not* undefine these macros! */ +/* */ +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 500 +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 400 + +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 1000 +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 275 + +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 1667 +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 275 + +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 2333 +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 0 + +/*************************************************************************/ +/* */ +/* CFF_CONFIG_OPTION_OLD_ENGINE controls whether the pre-Adobe CFF */ +/* engine gets compiled into FreeType. If defined, it is possible to */ +/* switch between the two engines using the `hinting-engine' property of */ +/* the cff driver module. */ +/* */ +/* #define CFF_CONFIG_OPTION_OLD_ENGINE */ + +/*************************************************************************/ +/*************************************************************************/ +/**** ****/ +/**** A U T O F I T M O D U L E C O N F I G U R A T I O N ****/ +/**** ****/ +/*************************************************************************/ +/*************************************************************************/ + +/*************************************************************************/ +/* */ +/* Compile autofit module with CJK (Chinese, Japanese, Korean) script */ +/* support. */ +/* */ +#define AF_CONFIG_OPTION_CJK + +/*************************************************************************/ +/* */ +/* Compile autofit module with Indic script support. */ +/* */ +#define AF_CONFIG_OPTION_INDIC + +/*************************************************************************/ +/* */ +/* Compile autofit module with warp hinting. The idea of the warping */ +/* code is to slightly scale and shift a glyph within a single dimension */ +/* so that as much of its segments are aligned (more or less) on the */ +/* grid. To find out the optimal scaling and shifting value, various */ +/* parameter combinations are tried and scored. */ +/* */ +/* This experimental option is active only if the rendering mode is */ +/* FT_RENDER_MODE_LIGHT; you can switch warping on and off with the */ +/* `warping' property of the auto-hinter (see file `ftautoh.h' for more */ +/* information; by default it is switched off). */ +/* */ +/*#define AF_CONFIG_OPTION_USE_WARPER*/ + +/* */ + +/* + * This macro is obsolete. Support has been removed in FreeType + * version 2.5. + */ +/* #define FT_CONFIG_OPTION_OLD_INTERNALS */ + +/* + * This macro is defined if either unpatented or native TrueType + * hinting is requested by the definitions above. + */ +#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER +#define TT_USE_BYTECODE_INTERPRETER +#undef TT_CONFIG_OPTION_UNPATENTED_HINTING +#elif defined TT_CONFIG_OPTION_UNPATENTED_HINTING +#define TT_USE_BYTECODE_INTERPRETER +#endif + +/* + * Check CFF darkening parameters. The checks are the same as in function + * `cff_property_set' in file `cffdrivr.c'. + */ +#if CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 < 0 || \ + \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 < 0 || \ + \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 > \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 > \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 > \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 || \ + \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 > 500 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 > 500 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 > 500 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 > 500 +#error "Invalid CFF darkening parameters!" +#endif + +FT_END_HEADER + +#endif /* COBALT___FTOPTION_H__ */ + +/* END */
diff --git a/src/third_party/mozjs/js/src/jit/mips/Architecture-mips.cpp b/src/third_party/mozjs/js/src/jit/mips/Architecture-mips.cpp new file mode 100644 index 0000000..d0d61ff --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/Architecture-mips.cpp
@@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/mips/Architecture-mips.h" + +#include <elf.h> + +#include <fcntl.h> +#include <unistd.h> + +#define HWCAP_MIPS (1 << 31) +#define HWCAP_FPU (1 << 0) + +namespace js { +namespace jit { + +uint32_t GetMIPSFlags() +{ + static bool isSet = false; + static uint32_t flags = 0; + if (isSet) + return flags; +#if WTF_OS_LINUX + FILE *fp = fopen("/proc/cpuinfo", "r"); + if (!fp) + return false; + + char buf[1024]; + memset(buf, 0, sizeof(buf)); + fread(buf, sizeof(char), sizeof(buf)-1, fp); + fclose(fp); + if (strstr(buf, "FPU")) + flags |= HWCAP_FPU; + + isSet = true; + return flags; +#endif + + return false; +} + +bool hasFPU() +{ + return js::jit::GetMIPSFlags() & HWCAP_FPU; +} + +Registers::Code +Registers::FromName(const char *name) +{ + for (size_t i = 0; i < Total; i++) { + if (strcmp(GetName(i), name) == 0) + return Code(i); + } + + return Invalid; +} + +FloatRegisters::Code +FloatRegisters::FromName(const char *name) +{ + for (size_t i = 0; i < Total; i++) { + if (strcmp(GetName(i), name) == 0) + return Code(i); + } + + return Invalid; +} + + + +} // namespace ion +} // namespace js +
diff --git a/src/third_party/mozjs/js/src/jit/mips/Architecture-mips.h b/src/third_party/mozjs/js/src/jit/mips/Architecture-mips.h new file mode 100644 index 0000000..0bc7346 --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/Architecture-mips.h
@@ -0,0 +1,324 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_mips_Architecture_mips_h +#define jit_mips_Architecture_mips_h + +#include <limits.h> +#include <stdint.h> + +#include "js/Utility.h" + +// gcc appears to use _mips_hard_float to denote +// that the target is a hard-float target. +#ifdef _mips_hard_float +#define JS_CODEGEN_MIPS_HARDFP +#endif +namespace js { +namespace jit { + +// Shadow stack space is not required on MIPS. +static const uint32_t ShadowStackSpace = 0; + +// These offsets are specific to nunboxing, and capture offsets into the +// components of a js::Value. +// Size of MIPS32 general purpose registers is 32 bits. +static const int32_t NUNBOX32_TYPE_OFFSET = 4; +static const int32_t NUNBOX32_PAYLOAD_OFFSET = 0; + +// Size of each bailout table entry. +// For MIPS this is 2 instructions relative call. +static const uint32_t BAILOUT_TABLE_ENTRY_SIZE = 2 * sizeof(void *); + +class Registers +{ + public: + enum RegisterID { + r0 = 0, + r1, + r2, + r3, + r4, + r5, + r6, + r7, + r8, + r9, + r10, + r11, + r12, + r13, + r14, + r15, + r16, + r17, + r18, + r19, + r20, + r21, + r22, + r23, + r24, + r25, + r26, + r27, + r28, + r29, + r30, + r31, + zero = r0, + at = r1, + v0 = r2, + v1 = r3, + a0 = r4, + a1 = r5, + a2 = r6, + a3 = r7, + t0 = r8, + t1 = r9, + t2 = r10, + t3 = r11, + t4 = r12, + t5 = r13, + t6 = r14, + t7 = r15, + s0 = r16, + s1 = r17, + s2 = r18, + s3 = r19, + s4 = r20, + s5 = r21, + s6 = r22, + s7 = r23, + t8 = r24, + t9 = r25, + k0 = r26, + k1 = r27, + gp = r28, + sp = r29, + fp = r30, + ra = r31, + invalid_reg + }; + typedef RegisterID Code; + + static const char *GetName(Code code) { + static const char * const Names[] = { "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra"}; + return Names[code]; + } + static const char *GetName(uint32_t i) { + JS_ASSERT(i < Total); + return GetName(Code(i)); + } + + static Code FromName(const char *name); + + static const Code StackPointer = sp; + static const Code Invalid = invalid_reg; + + static const uint32_t Total = 32; + static const uint32_t Allocatable = 14; + + static const uint32_t AllMask = 0xffffffff; + static const uint32_t ArgRegMask = (1 << a0) | (1 << a1) | (1 << a2) | (1 << a3); + + static const uint32_t VolatileMask = + (1 << Registers::v0) | + (1 << Registers::v1) | + (1 << Registers::a0) | + (1 << Registers::a1) | + (1 << Registers::a2) | + (1 << Registers::a3) | + (1 << Registers::t0) | + (1 << Registers::t1) | + (1 << Registers::t2) | + (1 << Registers::t3) | + (1 << Registers::t4) | + (1 << Registers::t5) | + (1 << Registers::t6) | + (1 << Registers::t7); + + static const uint32_t NonVolatileMask = + (1 << Registers::s0) | + (1 << Registers::s1) | + (1 << Registers::s2) | + (1 << Registers::s3) | + (1 << Registers::s4) | + (1 << Registers::s5) | + (1 << Registers::s6) | + (1 << Registers::s7); + + static const uint32_t WrapperMask = + VolatileMask | // = arguments + (1 << Registers::t0) | // = outReg + (1 << Registers::t1); // = argBase + + static const uint32_t NonAllocatableMask = + (1 << Registers::zero) | + (1 << Registers::at) | // at = scratch + (1 << Registers::t8) | // t8 = scratch + (1 << Registers::t9) | // t9 = scratch + (1 << Registers::k0) | + (1 << Registers::k1) | + (1 << Registers::gp) | + (1 << Registers::sp) | + (1 << Registers::fp) | + (1 << Registers::ra); + + // Registers that can be allocated without being saved, generally. + static const uint32_t TempMask = VolatileMask & ~NonAllocatableMask; + + // Registers returned from a JS -> JS call. + static const uint32_t JSCallMask = + (1 << Registers::v0) | + (1 << Registers::v1); + + // Registers returned from a JS -> C call. + static const uint32_t CallMask = + (1 << Registers::v0) | + (1 << Registers::v1); // used for double-size returns + + static const uint32_t AllocatableMask = AllMask & ~NonAllocatableMask; +}; + +// Smallest integer type that can hold a register bitmask. +typedef uint32_t PackedRegisterMask; + + +// MIPS32 can have two types of floating-point coprocessors: +// - 32 bit floating-point coprocessor - In this case, there are 32 single +// precision registers and pairs of even and odd float registers are used as +// double precision registers. Example: f0 (double) is composed of +// f0 and f1 (single). +// - 64 bit floating-point coprocessor - In this case, there are 32 double +// precision register which can also be used as single precision registers. + +// When using O32 ABI, floating-point coprocessor is 32 bit +// When using N32 ABI, floating-point coprocessor is 64 bit. +class FloatRegisters +{ + public: + enum FPRegisterID { + f0 = 0, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20, + f21, + f22, + f23, + f24, + f25, + f26, + f27, + f28, + f29, + f30, + f31, + invalid_freg + }; + typedef FPRegisterID Code; + + static const char *GetName(Code code) { + static const char * const Names[] = { "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "f8", "f9", "f10", "f11", "f12", "f13", + "f14", "f15", "f16", "f17", "f18", "f19", + "f20", "f21", "f22", "f23", "f24", "f25", + "f26", "f27", "f28", "f29", "f30", "f31"}; + return Names[code]; + } + static const char *GetName(uint32_t i) { + JS_ASSERT(i < Total); + return GetName(Code(i)); + } + + static Code FromName(const char *name); + + static const Code Invalid = invalid_freg; + + static const uint32_t Total = 32; + // :TODO: (Bug 972836) // Fix this once odd regs can be used as float32 + // only. For now we don't allocate odd regs for O32 ABI. + static const uint32_t Allocatable = 14; + + static const uint32_t AllMask = 0xffffffff; + + static const uint32_t VolatileMask = + (1 << FloatRegisters::f0) | + (1 << FloatRegisters::f2) | + (1 << FloatRegisters::f4) | + (1 << FloatRegisters::f6) | + (1 << FloatRegisters::f8) | + (1 << FloatRegisters::f10) | + (1 << FloatRegisters::f12) | + (1 << FloatRegisters::f14) | + (1 << FloatRegisters::f16) | + (1 << FloatRegisters::f18); + static const uint32_t NonVolatileMask = + (1 << FloatRegisters::f20) | + (1 << FloatRegisters::f22) | + (1 << FloatRegisters::f24) | + (1 << FloatRegisters::f26) | + (1 << FloatRegisters::f28) | + (1 << FloatRegisters::f30); + + static const uint32_t WrapperMask = VolatileMask; + + // :TODO: (Bug 972836) // Fix this once odd regs can be used as float32 + // only. For now we don't allocate odd regs for O32 ABI. + static const uint32_t NonAllocatableMask = + (1 << FloatRegisters::f1) | + (1 << FloatRegisters::f3) | + (1 << FloatRegisters::f5) | + (1 << FloatRegisters::f7) | + (1 << FloatRegisters::f9) | + (1 << FloatRegisters::f11) | + (1 << FloatRegisters::f13) | + (1 << FloatRegisters::f15) | + (1 << FloatRegisters::f17) | + (1 << FloatRegisters::f19) | + (1 << FloatRegisters::f21) | + (1 << FloatRegisters::f23) | + (1 << FloatRegisters::f25) | + (1 << FloatRegisters::f27) | + (1 << FloatRegisters::f29) | + (1 << FloatRegisters::f31) | + // f18 and f16 are MIPS scratch float registers. + (1 << FloatRegisters::f16) | + (1 << FloatRegisters::f18); + + // Registers that can be allocated without being saved, generally. + static const uint32_t TempMask = VolatileMask & ~NonAllocatableMask; + + static const uint32_t AllocatableMask = AllMask & ~NonAllocatableMask; +}; + +uint32_t GetMIPSFlags(); +bool hasFPU(); + +} // namespace jit +} // namespace js + +#endif /* jit_mips_Architecture_mips_h */
diff --git a/src/third_party/mozjs/js/src/jit/mips/Assembler-mips.cpp b/src/third_party/mozjs/js/src/jit/mips/Assembler-mips.cpp new file mode 100644 index 0000000..57fe57b --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/Assembler-mips.cpp
@@ -0,0 +1,1529 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/mips/Assembler-mips.h" + +#include "mozilla/DebugOnly.h" +#include "mozilla/MathAlgorithms.h" + +#include "jscompartment.h" +#include "jsutil.h" + +#include "assembler/jit/ExecutableAllocator.h" +#include "gc/Marking.h" +#include "jit/JitCompartment.h" + +using mozilla::DebugOnly; + +using namespace js; +using namespace js::jit; + +ABIArgGenerator::ABIArgGenerator() + : usedArgSlots_(0), + firstArgFloat(false), + current_() +{} + +ABIArg +ABIArgGenerator::next(MIRType type) +{ + MOZ_ASSUME_UNREACHABLE("NYI"); + return ABIArg(); +} +const Register ABIArgGenerator::NonArgReturnVolatileReg0 = t0; +const Register ABIArgGenerator::NonArgReturnVolatileReg1 = t1; + +// Encode a standard register when it is being used as rd, the rs, and +// an extra register(rt). These should never be called with an InvalidReg. +uint32_t +js::jit::RS(Register r) +{ + JS_ASSERT((r.code() & ~RegMask) == 0); + return r.code() << RSShift; +} + +uint32_t +js::jit::RT(Register r) +{ + JS_ASSERT((r.code() & ~RegMask) == 0); + return r.code() << RTShift; +} + +uint32_t +js::jit::RT(FloatRegister r) +{ + JS_ASSERT(r.code() < FloatRegisters::Total); + return r.code() << RTShift; +} + +uint32_t +js::jit::RD(Register r) +{ + JS_ASSERT((r.code() & ~RegMask) == 0); + return r.code() << RDShift; +} + +uint32_t +js::jit::RD(FloatRegister r) +{ + JS_ASSERT(r.code() < FloatRegisters::Total); + return r.code() << RDShift; +} + +uint32_t +js::jit::SA(uint32_t value) +{ + JS_ASSERT(value < 32); + return value << SAShift; +} + +uint32_t +js::jit::SA(FloatRegister r) +{ + JS_ASSERT(r.code() < FloatRegisters::Total); + return r.code() << SAShift; +} + +Register +js::jit::toRS(Instruction &i) +{ + return Register::FromCode((i.encode() & RSMask ) >> RSShift); +} + +Register +js::jit::toRT(Instruction &i) +{ + return Register::FromCode((i.encode() & RTMask ) >> RTShift); +} + +Register +js::jit::toRD(Instruction &i) +{ + return Register::FromCode((i.encode() & RDMask ) >> RDShift); +} + +Register +js::jit::toR(Instruction &i) +{ + return Register::FromCode(i.encode() & RegMask); +} + +void +InstImm::extractImm16(BOffImm16 *dest) +{ + *dest = BOffImm16(*this); +} + +// Used to patch jumps created by MacroAssemblerMIPSCompat::jumpWithPatch. +void +jit::PatchJump(CodeLocationJump &jump_, CodeLocationLabel label) +{ + Instruction *inst1 = (Instruction *)jump_.raw(); + Instruction *inst2 = inst1->next(); + + Assembler::updateLuiOriValue(inst1, inst2, (uint32_t)label.raw()); + + AutoFlushICache::flush(uintptr_t(inst1), 8); +} + +void +Assembler::finish() +{ + JS_ASSERT(!isFinished); + isFinished = true; +} + +void +Assembler::executableCopy(uint8_t *buffer) +{ + JS_ASSERT(isFinished); + m_buffer.executableCopy(buffer); + + // Patch all long jumps during code copy. + for (size_t i = 0; i < longJumps_.length(); i++) { + Instruction *inst1 = (Instruction *) ((uint32_t)buffer + longJumps_[i]); + + uint32_t value = extractLuiOriValue(inst1, inst1->next()); + updateLuiOriValue(inst1, inst1->next(), (uint32_t)buffer + value); + } + + AutoFlushICache::setRange(uintptr_t(buffer), m_buffer.size()); +} + +uint32_t +Assembler::actualOffset(uint32_t off_) const +{ + return off_; +} + +uint32_t +Assembler::actualIndex(uint32_t idx_) const +{ + return idx_; +} + +uint8_t * +Assembler::PatchableJumpAddress(JitCode *code, uint32_t pe_) +{ + return code->raw() + pe_; +} + +class RelocationIterator +{ + CompactBufferReader reader_; + // offset in bytes + uint32_t offset_; + + public: + RelocationIterator(CompactBufferReader &reader) + : reader_(reader) + { } + + bool read() { + if (!reader_.more()) + return false; + offset_ = reader_.readUnsigned(); + return true; + } + + uint32_t offset() const { + return offset_; + } +}; + +uintptr_t +Assembler::getPointer(uint8_t *instPtr) +{ + Instruction *inst = (Instruction*)instPtr; + return Assembler::extractLuiOriValue(inst, inst->next()); +} + +static JitCode * +CodeFromJump(Instruction *jump) +{ + uint8_t *target = (uint8_t *)Assembler::extractLuiOriValue(jump, jump->next()); + return JitCode::FromExecutable(target); +} + +void +Assembler::TraceJumpRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader) +{ + RelocationIterator iter(reader); + while (iter.read()) { + JitCode *child = CodeFromJump((Instruction *)(code->raw() + iter.offset())); + MarkJitCodeUnbarriered(trc, &child, "rel32"); + } +} + +static void +TraceDataRelocations(JSTracer *trc, uint8_t *buffer, CompactBufferReader &reader) +{ + while (reader.more()) { + size_t offset = reader.readUnsigned(); + Instruction *inst = (Instruction*)(buffer + offset); + void *ptr = (void *)Assembler::extractLuiOriValue(inst, inst->next()); + + // No barrier needed since these are constants. + gc::MarkGCThingUnbarriered(trc, reinterpret_cast<void **>(&ptr), "ion-masm-ptr"); + } +} + +static void +TraceDataRelocations(JSTracer *trc, MIPSBuffer *buffer, CompactBufferReader &reader) +{ + while (reader.more()) { + BufferOffset bo (reader.readUnsigned()); + MIPSBuffer::AssemblerBufferInstIterator iter(bo, buffer); + + void *ptr = (void *)Assembler::extractLuiOriValue(iter.cur(), iter.next()); + + // No barrier needed since these are constants. + gc::MarkGCThingUnbarriered(trc, reinterpret_cast<void **>(&ptr), "ion-masm-ptr"); + } +} + +void +Assembler::TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader) +{ + ::TraceDataRelocations(trc, code->raw(), reader); +} + +void +Assembler::copyJumpRelocationTable(uint8_t *dest) +{ + if (jumpRelocations_.length()) + memcpy(dest, jumpRelocations_.buffer(), jumpRelocations_.length()); +} + +void +Assembler::copyDataRelocationTable(uint8_t *dest) +{ + if (dataRelocations_.length()) + memcpy(dest, dataRelocations_.buffer(), dataRelocations_.length()); +} + +void +Assembler::copyPreBarrierTable(uint8_t *dest) +{ + if (preBarriers_.length()) + memcpy(dest, preBarriers_.buffer(), preBarriers_.length()); +} + +void +Assembler::trace(JSTracer *trc) +{ + for (size_t i = 0; i < jumps_.length(); i++) { + RelativePatch &rp = jumps_[i]; + if (rp.kind == Relocation::JITCODE) { + JitCode *code = JitCode::FromExecutable((uint8_t *)rp.target); + MarkJitCodeUnbarriered(trc, &code, "masmrel32"); + JS_ASSERT(code == JitCode::FromExecutable((uint8_t *)rp.target)); + } + } + if (dataRelocations_.length()) { + CompactBufferReader reader(dataRelocations_); + ::TraceDataRelocations(trc, &m_buffer, reader); + } +} + +void +Assembler::processCodeLabels(uint8_t *rawCode) +{ + for (size_t i = 0; i < codeLabels_.length(); i++) { + CodeLabel label = codeLabels_[i]; + Bind(rawCode, label.dest(), rawCode + actualOffset(label.src()->offset())); + } +} + +void +Assembler::Bind(uint8_t *rawCode, AbsoluteLabel *label, const void *address) +{ + if (label->used()) { + int32_t src = label->offset(); + do { + Instruction *inst = (Instruction *) (rawCode + src); + uint32_t next = Assembler::extractLuiOriValue(inst, inst->next()); + Assembler::updateLuiOriValue(inst, inst->next(), (uint32_t)address); + src = next; + } while (src != AbsoluteLabel::INVALID_OFFSET); + } + label->bind(); +} + +Assembler::Condition +Assembler::InvertCondition(Condition cond) +{ + switch (cond) { + case Equal: + return NotEqual; + case NotEqual: + return Equal; + case Zero: + return NonZero; + case NonZero: + return Zero; + case LessThan: + return GreaterThanOrEqual; + case LessThanOrEqual: + return GreaterThan; + case GreaterThan: + return LessThanOrEqual; + case GreaterThanOrEqual: + return LessThan; + case Above: + return BelowOrEqual; + case AboveOrEqual: + return Below; + case Below: + return AboveOrEqual; + case BelowOrEqual: + return Above; + case Signed: + return NotSigned; + case NotSigned: + return Signed; + default: + MOZ_ASSUME_UNREACHABLE("unexpected condition"); + return Equal; + } +} + +Assembler::DoubleCondition +Assembler::InvertCondition(DoubleCondition cond) +{ + switch (cond) { + case DoubleOrdered: + return DoubleUnordered; + case DoubleEqual: + return DoubleNotEqualOrUnordered; + case DoubleNotEqual: + return DoubleEqualOrUnordered; + case DoubleGreaterThan: + return DoubleLessThanOrEqualOrUnordered; + case DoubleGreaterThanOrEqual: + return DoubleLessThanOrUnordered; + case DoubleLessThan: + return DoubleGreaterThanOrEqualOrUnordered; + case DoubleLessThanOrEqual: + return DoubleGreaterThanOrUnordered; + case DoubleUnordered: + return DoubleOrdered; + case DoubleEqualOrUnordered: + return DoubleNotEqual; + case DoubleNotEqualOrUnordered: + return DoubleEqual; + case DoubleGreaterThanOrUnordered: + return DoubleLessThanOrEqual; + case DoubleGreaterThanOrEqualOrUnordered: + return DoubleLessThan; + case DoubleLessThanOrUnordered: + return DoubleGreaterThanOrEqual; + case DoubleLessThanOrEqualOrUnordered: + return DoubleGreaterThan; + default: + MOZ_ASSUME_UNREACHABLE("unexpected condition"); + return DoubleEqual; + } +} + +BOffImm16::BOffImm16(InstImm inst) + : data(inst.encode() & Imm16Mask) +{ +} + +bool +Assembler::oom() const +{ + return m_buffer.oom() || + !enoughMemory_ || + jumpRelocations_.oom() || + dataRelocations_.oom() || + preBarriers_.oom(); +} + +bool +Assembler::addCodeLabel(CodeLabel label) +{ + return codeLabels_.append(label); +} + +// Size of the instruction stream, in bytes. +size_t +Assembler::size() const +{ + return m_buffer.size(); +} + +// Size of the relocation table, in bytes. +size_t +Assembler::jumpRelocationTableBytes() const +{ + return jumpRelocations_.length(); +} + +size_t +Assembler::dataRelocationTableBytes() const +{ + return dataRelocations_.length(); +} + +size_t +Assembler::preBarrierTableBytes() const +{ + return preBarriers_.length(); +} + +// Size of the data table, in bytes. +size_t +Assembler::bytesNeeded() const +{ + return size() + + jumpRelocationTableBytes() + + dataRelocationTableBytes() + + preBarrierTableBytes(); +} + +// write a blob of binary into the instruction stream +BufferOffset +Assembler::writeInst(uint32_t x, uint32_t *dest) +{ + if (dest == nullptr) + return m_buffer.putInt(x); + + writeInstStatic(x, dest); + return BufferOffset(); +} + +void +Assembler::writeInstStatic(uint32_t x, uint32_t *dest) +{ + JS_ASSERT(dest != nullptr); + *dest = x; +} + +BufferOffset +Assembler::align(int alignment) +{ + BufferOffset ret; + JS_ASSERT(m_buffer.isAligned(4)); + if (alignment == 8) { + if (!m_buffer.isAligned(alignment)) { + BufferOffset tmp = as_nop(); + if (!ret.assigned()) + ret = tmp; + } + } else { + JS_ASSERT((alignment & (alignment - 1)) == 0); + while (size() & (alignment - 1)) { + BufferOffset tmp = as_nop(); + if (!ret.assigned()) + ret = tmp; + } + } + return ret; +} + +BufferOffset +Assembler::as_nop() +{ + return writeInst(op_special | ff_sll); +} + +// Logical operations. +BufferOffset +Assembler::as_and(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_and).encode()); +} + +BufferOffset +Assembler::as_or(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_or).encode()); +} + +BufferOffset +Assembler::as_xor(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_xor).encode()); +} + +BufferOffset +Assembler::as_nor(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_nor).encode()); +} + +BufferOffset +Assembler::as_andi(Register rd, Register rs, int32_t j) +{ + JS_ASSERT(Imm16::isInUnsignedRange(j)); + return writeInst(InstImm(op_andi, rs, rd, Imm16(j)).encode()); +} + +BufferOffset +Assembler::as_ori(Register rd, Register rs, int32_t j) +{ + JS_ASSERT(Imm16::isInUnsignedRange(j)); + return writeInst(InstImm(op_ori, rs, rd, Imm16(j)).encode()); +} + +BufferOffset +Assembler::as_xori(Register rd, Register rs, int32_t j) +{ + JS_ASSERT(Imm16::isInUnsignedRange(j)); + return writeInst(InstImm(op_xori, rs, rd, Imm16(j)).encode()); +} + +// Branch and jump instructions +BufferOffset +Assembler::as_bal(BOffImm16 off) +{ + BufferOffset bo = writeInst(InstImm(op_regimm, zero, rt_bgezal, off).encode()); + return bo; +} + +InstImm +Assembler::getBranchCode(JumpOrCall jumpOrCall) +{ + if (jumpOrCall == BranchIsCall) + return InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0)); + + return InstImm(op_beq, zero, zero, BOffImm16(0)); +} + +InstImm +Assembler::getBranchCode(Register s, Register t, Condition c) +{ + JS_ASSERT(c == Assembler::Equal || c == Assembler::NotEqual); + return InstImm(c == Assembler::Equal ? op_beq : op_bne, s, t, BOffImm16(0)); +} + +InstImm +Assembler::getBranchCode(Register s, Condition c) +{ + switch (c) { + case Assembler::Equal: + case Assembler::Zero: + case Assembler::BelowOrEqual: + return InstImm(op_beq, s, zero, BOffImm16(0)); + case Assembler::NotEqual: + case Assembler::NonZero: + case Assembler::Above: + return InstImm(op_bne, s, zero, BOffImm16(0)); + case Assembler::GreaterThan: + return InstImm(op_bgtz, s, zero, BOffImm16(0)); + case Assembler::GreaterThanOrEqual: + case Assembler::NotSigned: + return InstImm(op_regimm, s, rt_bgez, BOffImm16(0)); + case Assembler::LessThan: + case Assembler::Signed: + return InstImm(op_regimm, s, rt_bltz, BOffImm16(0)); + case Assembler::LessThanOrEqual: + return InstImm(op_blez, s, zero, BOffImm16(0)); + default: + MOZ_ASSUME_UNREACHABLE("Condition not supported."); + } +} + +InstImm +Assembler::getBranchCode(FloatTestKind testKind, FPConditionBit fcc) +{ + JS_ASSERT(!(fcc && FccMask)); + uint32_t rtField = ((testKind == TestForTrue ? 1 : 0) | (fcc << FccShift)) << RTShift; + + return InstImm(op_cop1, rs_bc1, rtField, BOffImm16(0)); +} + +BufferOffset +Assembler::as_j(JOffImm26 off) +{ + BufferOffset bo = writeInst(InstJump(op_j, off).encode()); + return bo; +} +BufferOffset +Assembler::as_jal(JOffImm26 off) +{ + BufferOffset bo = writeInst(InstJump(op_jal, off).encode()); + return bo; +} + +BufferOffset +Assembler::as_jr(Register rs) +{ + BufferOffset bo = writeInst(InstReg(op_special, rs, zero, zero, ff_jr).encode()); + return bo; +} +BufferOffset +Assembler::as_jalr(Register rs) +{ + BufferOffset bo = writeInst(InstReg(op_special, rs, zero, ra, ff_jalr).encode()); + return bo; +} + + +// Arithmetic instructions +BufferOffset +Assembler::as_addu(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_addu).encode()); +} + +BufferOffset +Assembler::as_addiu(Register rd, Register rs, int32_t j) +{ + JS_ASSERT(Imm16::isInSignedRange(j)); + return writeInst(InstImm(op_addiu, rs, rd, Imm16(j)).encode()); +} + +BufferOffset +Assembler::as_subu(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_subu).encode()); +} + +BufferOffset +Assembler::as_mult(Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, ff_mult).encode()); +} + +BufferOffset +Assembler::as_multu(Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, ff_multu).encode()); +} + +BufferOffset +Assembler::as_div(Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, ff_div).encode()); +} + +BufferOffset +Assembler::as_divu(Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, ff_divu).encode()); +} + +BufferOffset +Assembler::as_mul(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special2, rs, rt, rd, ff_mul).encode()); +} + +BufferOffset +Assembler::as_lui(Register rd, int32_t j) +{ + JS_ASSERT(Imm16::isInUnsignedRange(j)); + return writeInst(InstImm(op_lui, zero, rd, Imm16(j)).encode()); +} + +// Shift instructions +BufferOffset +Assembler::as_sll(Register rd, Register rt, uint16_t sa) +{ + JS_ASSERT(sa < 32); + return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_sll).encode()); +} + +BufferOffset +Assembler::as_sllv(Register rd, Register rt, Register rs) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_sllv).encode()); +} + +BufferOffset +Assembler::as_srl(Register rd, Register rt, uint16_t sa) +{ + JS_ASSERT(sa < 32); + return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_srl).encode()); +} + +BufferOffset +Assembler::as_srlv(Register rd, Register rt, Register rs) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_srlv).encode()); +} + +BufferOffset +Assembler::as_sra(Register rd, Register rt, uint16_t sa) +{ + JS_ASSERT(sa < 32); + return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_sra).encode()); +} + +BufferOffset +Assembler::as_srav(Register rd, Register rt, Register rs) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_srav).encode()); +} + +BufferOffset +Assembler::as_rotr(Register rd, Register rt, uint16_t sa) +{ + JS_ASSERT(sa < 32); + return writeInst(InstReg(op_special, rs_one, rt, rd, sa, ff_srl).encode()); +} + +BufferOffset +Assembler::as_rotrv(Register rd, Register rt, Register rs) +{ + return writeInst(InstReg(op_special, rs, rt, rd, 1, ff_srlv).encode()); +} + +// Load and store instructions +BufferOffset +Assembler::as_lb(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_lb, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +Assembler::as_lbu(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_lbu, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +Assembler::as_lh(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_lh, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +Assembler::as_lhu(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_lhu, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +Assembler::as_lw(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_lw, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +Assembler::as_lwl(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_lwl, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +Assembler::as_lwr(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_lwr, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +Assembler::as_sb(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_sb, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +Assembler::as_sh(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_sh, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +Assembler::as_sw(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_sw, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +Assembler::as_swl(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_swl, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +Assembler::as_swr(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_swr, rs, rd, Imm16(off)).encode()); +} + +// Move from HI/LO register. +BufferOffset +Assembler::as_mfhi(Register rd) +{ + return writeInst(InstReg(op_special, rd, ff_mfhi).encode()); +} + +BufferOffset +Assembler::as_mflo(Register rd) +{ + return writeInst(InstReg(op_special, rd, ff_mflo).encode()); +} + +// Set on less than. +BufferOffset +Assembler::as_slt(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_slt).encode()); +} + +BufferOffset +Assembler::as_sltu(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_sltu).encode()); +} + +BufferOffset +Assembler::as_slti(Register rd, Register rs, int32_t j) +{ + JS_ASSERT(Imm16::isInSignedRange(j)); + return writeInst(InstImm(op_slti, rs, rd, Imm16(j)).encode()); +} + +BufferOffset +Assembler::as_sltiu(Register rd, Register rs, uint32_t j) +{ + JS_ASSERT(Imm16::isInUnsignedRange(j)); + return writeInst(InstImm(op_sltiu, rs, rd, Imm16(j)).encode()); +} + +// Conditional move. +BufferOffset +Assembler::as_movz(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_movz).encode()); +} + +BufferOffset +Assembler::as_movn(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_movn).encode()); +} + +BufferOffset +Assembler::as_movt(Register rd, Register rs, uint16_t cc) +{ + Register rt; + rt = Register::FromCode((cc & 0x7) << 2 | 1); + return writeInst(InstReg(op_special, rs, rt, rd, ff_movci).encode()); +} + +BufferOffset +Assembler::as_movf(Register rd, Register rs, uint16_t cc) +{ + Register rt; + rt = Register::FromCode((cc & 0x7) << 2 | 0); + return writeInst(InstReg(op_special, rs, rt, rd, ff_movci).encode()); +} + +// Bit twiddling. +BufferOffset +Assembler::as_clz(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special2, rs, rt, rd, ff_clz).encode()); +} + +BufferOffset +Assembler::as_ins(Register rt, Register rs, uint16_t pos, uint16_t size) +{ + JS_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size >= 32); + Register rd; + rd = Register::FromCode(pos + size - 1); + return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_ins).encode()); +} + +BufferOffset +Assembler::as_ext(Register rt, Register rs, uint16_t pos, uint16_t size) +{ + JS_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size >= 32); + Register rd; + rd = Register::FromCode(size - 1); + return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_ext).encode()); +} + +// FP instructions +BufferOffset +Assembler::as_ld(FloatRegister fd, Register base, int32_t off) +{ + JS_ASSERT(Imm16::isInSignedRange(off)); + return writeInst(InstImm(op_ldc1, base, fd, Imm16(off)).encode()); +} + +BufferOffset +Assembler::as_sd(FloatRegister fd, Register base, int32_t off) +{ + JS_ASSERT(Imm16::isInSignedRange(off)); + return writeInst(InstImm(op_sdc1, base, fd, Imm16(off)).encode()); +} + +BufferOffset +Assembler::as_ls(FloatRegister fd, Register base, int32_t off) +{ + JS_ASSERT(Imm16::isInSignedRange(off)); + return writeInst(InstImm(op_lwc1, base, fd, Imm16(off)).encode()); +} + +BufferOffset +Assembler::as_ss(FloatRegister fd, Register base, int32_t off) +{ + JS_ASSERT(Imm16::isInSignedRange(off)); + return writeInst(InstImm(op_swc1, base, fd, Imm16(off)).encode()); +} + +BufferOffset +Assembler::as_movs(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_mov_fmt).encode()); +} + +BufferOffset +Assembler::as_movd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_mov_fmt).encode()); +} + +BufferOffset +Assembler::as_mtc1(Register rt, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_mtc1, rt, fs).encode()); +} + +BufferOffset +Assembler::as_mfc1(Register rt, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_mfc1, rt, fs).encode()); +} + +// FP convert instructions +BufferOffset +Assembler::as_ceilws(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_ceil_w_fmt).encode()); +} + +BufferOffset +Assembler::as_floorws(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_floor_w_fmt).encode()); +} + +BufferOffset +Assembler::as_roundws(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_round_w_fmt).encode()); +} + +BufferOffset +Assembler::as_truncws(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_trunc_w_fmt).encode()); +} + +BufferOffset +Assembler::as_ceilwd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_ceil_w_fmt).encode()); +} + +BufferOffset +Assembler::as_floorwd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_floor_w_fmt).encode()); +} + +BufferOffset +Assembler::as_roundwd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_round_w_fmt).encode()); +} + +BufferOffset +Assembler::as_truncwd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_trunc_w_fmt).encode()); +} + +BufferOffset +Assembler::as_cvtds(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_cvt_d_fmt).encode()); +} + +BufferOffset +Assembler::as_cvtdw(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_w, zero, fs, fd, ff_cvt_d_fmt).encode()); +} + +BufferOffset +Assembler::as_cvtsd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_cvt_s_fmt).encode()); +} + +BufferOffset +Assembler::as_cvtsw(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_w, zero, fs, fd, ff_cvt_s_fmt).encode()); +} + +BufferOffset +Assembler::as_cvtwd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_cvt_w_fmt).encode()); +} + +BufferOffset +Assembler::as_cvtws(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_cvt_w_fmt).encode()); +} + +// FP arithmetic instructions +BufferOffset +Assembler::as_adds(FloatRegister fd, FloatRegister fs, FloatRegister ft) +{ + return writeInst(InstReg(op_cop1, rs_s, ft, fs, fd, ff_add_fmt).encode()); +} + +BufferOffset +Assembler::as_addd(FloatRegister fd, FloatRegister fs, FloatRegister ft) +{ + return writeInst(InstReg(op_cop1, rs_d, ft, fs, fd, ff_add_fmt).encode()); +} + +BufferOffset +Assembler::as_subs(FloatRegister fd, FloatRegister fs, FloatRegister ft) +{ + return writeInst(InstReg(op_cop1, rs_s, ft, fs, fd, ff_sub_fmt).encode()); +} + +BufferOffset +Assembler::as_subd(FloatRegister fd, FloatRegister fs, FloatRegister ft) +{ + return writeInst(InstReg(op_cop1, rs_d, ft, fs, fd, ff_sub_fmt).encode()); +} + +BufferOffset +Assembler::as_abss(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_abs_fmt).encode()); +} + +BufferOffset +Assembler::as_absd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_abs_fmt).encode()); +} + +BufferOffset +Assembler::as_negd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_neg_fmt).encode()); +} + +BufferOffset +Assembler::as_muls(FloatRegister fd, FloatRegister fs, FloatRegister ft) +{ + return writeInst(InstReg(op_cop1, rs_s, ft, fs, fd, ff_mul_fmt).encode()); +} + +BufferOffset +Assembler::as_muld(FloatRegister fd, FloatRegister fs, FloatRegister ft) +{ + return writeInst(InstReg(op_cop1, rs_d, ft, fs, fd, ff_mul_fmt).encode()); +} + +BufferOffset +Assembler::as_divs(FloatRegister fd, FloatRegister fs, FloatRegister ft) +{ + return writeInst(InstReg(op_cop1, rs_s, ft, fs, fd, ff_div_fmt).encode()); +} + +BufferOffset +Assembler::as_divd(FloatRegister fd, FloatRegister fs, FloatRegister ft) +{ + return writeInst(InstReg(op_cop1, rs_d, ft, fs, fd, ff_div_fmt).encode()); +} + +BufferOffset +Assembler::as_sqrts(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_sqrt_fmt).encode()); +} + +BufferOffset +Assembler::as_sqrtd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_sqrt_fmt).encode()); +} + +// FP compare instructions +BufferOffset +Assembler::as_cf(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) +{ + RSField rs = fmt == DoubleFloat ? rs_d : rs_s; + return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_f_fmt).encode()); +} + +BufferOffset +Assembler::as_cun(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) +{ + RSField rs = fmt == DoubleFloat ? rs_d : rs_s; + return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_un_fmt).encode()); +} + +BufferOffset +Assembler::as_ceq(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) +{ + RSField rs = fmt == DoubleFloat ? rs_d : rs_s; + return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_eq_fmt).encode()); +} + +BufferOffset +Assembler::as_cueq(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) +{ + RSField rs = fmt == DoubleFloat ? rs_d : rs_s; + return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_ueq_fmt).encode()); +} + +BufferOffset +Assembler::as_colt(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) +{ + RSField rs = fmt == DoubleFloat ? rs_d : rs_s; + return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_olt_fmt).encode()); +} + +BufferOffset +Assembler::as_cult(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) +{ + RSField rs = fmt == DoubleFloat ? rs_d : rs_s; + return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_ult_fmt).encode()); +} + +BufferOffset +Assembler::as_cole(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) +{ + RSField rs = fmt == DoubleFloat ? rs_d : rs_s; + return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_ole_fmt).encode()); +} + +BufferOffset +Assembler::as_cule(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) +{ + RSField rs = fmt == DoubleFloat ? rs_d : rs_s; + return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_ule_fmt).encode()); +} + + +void +Assembler::bind(Label *label, BufferOffset boff) +{ + // If our caller didn't give us an explicit target to bind to + // then we want to bind to the location of the next instruction + BufferOffset dest = boff.assigned() ? boff : nextOffset(); + if (label->used()) { + int32_t next; + + // A used label holds a link to branch that uses it. + BufferOffset b(label); + do { + Instruction *inst = editSrc(b); + + // Second word holds a pointer to the next branch in label's chain. + next = inst[1].encode(); + bind(reinterpret_cast<InstImm *>(inst), b.getOffset(), dest.getOffset()); + + b = BufferOffset(next); + } while (next != LabelBase::INVALID_OFFSET); + } + label->bind(dest.getOffset()); +} + +void +Assembler::bind(InstImm *inst, uint32_t branch, uint32_t target) +{ + int32_t offset = target - branch; + InstImm inst_bgezal = InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0)); + InstImm inst_beq = InstImm(op_beq, zero, zero, BOffImm16(0)); + + // If encoded offset is 4, then the jump must be short + if (BOffImm16(inst[0]).decode() == 4) { + JS_ASSERT(BOffImm16::isInRange(offset)); + inst[0].setBOffImm16(BOffImm16(offset)); + inst[1].makeNop(); + return; + } + if (BOffImm16::isInRange(offset)) { + bool conditional = (inst[0].encode() != inst_bgezal.encode() && + inst[0].encode() != inst_beq.encode()); + + inst[0].setBOffImm16(BOffImm16(offset)); + inst[1].makeNop(); + + // Skip the trailing nops in conditional branches. + if (conditional) { + inst[2] = InstImm(op_regimm, zero, rt_bgez, BOffImm16(3 * sizeof(void *))).encode(); + // There are 2 nops after this + } + return; + } + + if (inst[0].encode() == inst_bgezal.encode()) { + // Handle long call. + addLongJump(BufferOffset(branch)); + writeLuiOriInstructions(inst, &inst[1], ScratchRegister, target); + inst[2] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr).encode(); + // There is 1 nop after this. + } else if (inst[0].encode() == inst_beq.encode()) { + // Handle long unconditional jump. + addLongJump(BufferOffset(branch)); + writeLuiOriInstructions(inst, &inst[1], ScratchRegister, target); + inst[2] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr).encode(); + // There is 1 nop after this. + } else { + // Handle long conditional jump. + inst[0] = invertBranch(inst[0], BOffImm16(5 * sizeof(void *))); + // No need for a "nop" here because we can clobber scratch. + addLongJump(BufferOffset(branch + sizeof(void *))); + writeLuiOriInstructions(&inst[1], &inst[2], ScratchRegister, target); + inst[3] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr).encode(); + // There is 1 nop after this. + } +} + +void +Assembler::bind(RepatchLabel *label) +{ + BufferOffset dest = nextOffset(); + if (label->used()) { + // If the label has a use, then change this use to refer to + // the bound label; + BufferOffset b(label->offset()); + Instruction *inst1 = editSrc(b); + Instruction *inst2 = inst1->next(); + + updateLuiOriValue(inst1, inst2, dest.getOffset()); + } + label->bind(dest.getOffset()); +} + +void +Assembler::retarget(Label *label, Label *target) +{ + if (label->used()) { + if (target->bound()) { + bind(label, BufferOffset(target)); + } else if (target->used()) { + // The target is not bound but used. Prepend label's branch list + // onto target's. + int32_t next; + BufferOffset labelBranchOffset(label); + + // Find the head of the use chain for label. + do { + Instruction *inst = editSrc(labelBranchOffset); + + // Second word holds a pointer to the next branch in chain. + next = inst[1].encode(); + labelBranchOffset = BufferOffset(next); + } while (next != LabelBase::INVALID_OFFSET); + + // Then patch the head of label's use chain to the tail of + // target's use chain, prepending the entire use chain of target. + Instruction *inst = editSrc(labelBranchOffset); + int32_t prev = target->use(label->offset()); + inst[1].setData(prev); + } else { + // The target is unbound and unused. We can just take the head of + // the list hanging off of label, and dump that into target. + DebugOnly<uint32_t> prev = target->use(label->offset()); + JS_ASSERT((int32_t)prev == Label::INVALID_OFFSET); + } + } + label->reset(); +} + +void dbg_break() {} +static int stopBKPT = -1; +void +Assembler::as_break(uint32_t code) +{ + JS_ASSERT(code <= MAX_BREAK_CODE); + writeInst(op_special | code << RTShift | ff_break); +} + +uint32_t +Assembler::patchWrite_NearCallSize() +{ + return 4 * sizeof(uint32_t); +} + +void +Assembler::patchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall) +{ + Instruction *inst = (Instruction *) start.raw(); + uint8_t *dest = toCall.raw(); + + // Overwrite whatever instruction used to be here with a call. + // Always use long jump for two reasons: + // - Jump has to be the same size because of patchWrite_NearCallSize. + // - Return address has to be at the end of replaced block. + // Short jump wouldn't be more efficient. + writeLuiOriInstructions(inst, &inst[1], ScratchRegister, (uint32_t)dest); + inst[2] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr); + inst[3] = InstNOP(); + + // Ensure everyone sees the code that was just written into memory. + AutoFlushICache::flush(uintptr_t(inst), patchWrite_NearCallSize()); +} + +uint32_t +Assembler::extractLuiOriValue(Instruction *inst0, Instruction *inst1) +{ + InstImm *i0 = (InstImm *) inst0; + InstImm *i1 = (InstImm *) inst1; + JS_ASSERT(i0->extractOpcode() == ((uint32_t)op_lui >> OpcodeShift)); + JS_ASSERT(i1->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift)); + + uint32_t value = i0->extractImm16Value() << 16; + value = value | i1->extractImm16Value(); + return value; +} + +void +Assembler::updateLuiOriValue(Instruction *inst0, Instruction *inst1, uint32_t value) +{ + JS_ASSERT(inst0->extractOpcode() == ((uint32_t)op_lui >> OpcodeShift)); + JS_ASSERT(inst1->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift)); + + ((InstImm *) inst0)->setImm16(Imm16::upper(Imm32(value))); + ((InstImm *) inst1)->setImm16(Imm16::lower(Imm32(value))); +} + +void +Assembler::writeLuiOriInstructions(Instruction *inst0, Instruction *inst1, + Register reg, uint32_t value) +{ + *inst0 = InstImm(op_lui, zero, reg, Imm16::upper(Imm32(value))); + *inst1 = InstImm(op_ori, reg, reg, Imm16::lower(Imm32(value))); +} + +void +Assembler::patchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue, + PatchedImmPtr expectedValue) +{ + Instruction *inst = (Instruction *) label.raw(); + + // Extract old Value + DebugOnly<uint32_t> value = Assembler::extractLuiOriValue(&inst[0], &inst[1]); + JS_ASSERT(value == uint32_t(expectedValue.value)); + + // Replace with new value + Assembler::updateLuiOriValue(inst, inst->next(), uint32_t(newValue.value)); + + AutoFlushICache::flush(uintptr_t(inst), 8); +} + +void +Assembler::patchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue, ImmPtr expectedValue) +{ + patchDataWithValueCheck(label, PatchedImmPtr(newValue.value), + PatchedImmPtr(expectedValue.value)); +} + +// This just stomps over memory with 32 bits of raw data. Its purpose is to +// overwrite the call of JITed code with 32 bits worth of an offset. This will +// is only meant to function on code that has been invalidated, so it should +// be totally safe. Since that instruction will never be executed again, a +// ICache flush should not be necessary +void +Assembler::patchWrite_Imm32(CodeLocationLabel label, Imm32 imm) +{ + // Raw is going to be the return address. + uint32_t *raw = (uint32_t*)label.raw(); + // Overwrite the 4 bytes before the return address, which will + // end up being the call instruction. + *(raw - 1) = imm.value; +} + +uint8_t * +Assembler::nextInstruction(uint8_t *inst_, uint32_t *count) +{ + Instruction *inst = reinterpret_cast<Instruction*>(inst_); + if (count != nullptr) + *count += sizeof(Instruction); + return reinterpret_cast<uint8_t*>(inst->next()); +} + +// Since there are no pools in MIPS implementation, this should be simple. +Instruction * +Instruction::next() +{ + return this + 1; +} + +InstImm Assembler::invertBranch(InstImm branch, BOffImm16 skipOffset) +{ + uint32_t rt = 0; + Opcode op = (Opcode) (branch.extractOpcode() << OpcodeShift); + switch(op) { + case op_beq: + branch.setBOffImm16(skipOffset); + branch.setOpcode(op_bne); + return branch; + case op_bne: + branch.setBOffImm16(skipOffset); + branch.setOpcode(op_beq); + return branch; + case op_bgtz: + branch.setBOffImm16(skipOffset); + branch.setOpcode(op_blez); + return branch; + case op_blez: + branch.setBOffImm16(skipOffset); + branch.setOpcode(op_bgtz); + return branch; + case op_regimm: + branch.setBOffImm16(skipOffset); + rt = branch.extractRT(); + if (rt == (rt_bltz >> RTShift)) { + branch.setRT(rt_bgez); + return branch; + } + if (rt == (rt_bgez >> RTShift)) { + branch.setRT(rt_bltz); + return branch; + } + + MOZ_ASSUME_UNREACHABLE("Error creating long branch."); + return branch; + + case op_cop1: + JS_ASSERT(branch.extractRS() == rs_bc1 >> RSShift); + + branch.setBOffImm16(skipOffset); + rt = branch.extractRT(); + if (rt & 0x1) + branch.setRT((RTField) ((rt & ~0x1) << RTShift)); + else + branch.setRT((RTField) ((rt | 0x1) << RTShift)); + return branch; + } + + MOZ_ASSUME_UNREACHABLE("Error creating long branch."); + return branch; +} + +void +Assembler::ToggleToJmp(CodeLocationLabel inst_) +{ + InstImm * inst = (InstImm *)inst_.raw(); + + JS_ASSERT(inst->extractOpcode() == ((uint32_t)op_andi >> OpcodeShift)); + // We converted beq to andi, so now we restore it. + inst->setOpcode(op_beq); + + AutoFlushICache::flush(uintptr_t(inst), 4); +} + +void +Assembler::ToggleToCmp(CodeLocationLabel inst_) +{ + InstImm * inst = (InstImm *)inst_.raw(); + + // toggledJump is allways used for short jumps. + JS_ASSERT(inst->extractOpcode() == ((uint32_t)op_beq >> OpcodeShift)); + // Replace "beq $zero, $zero, offset" with "andi $zero, $zero, offset" + inst->setOpcode(op_andi); + + AutoFlushICache::flush(uintptr_t(inst), 4); +} + +void +Assembler::ToggleCall(CodeLocationLabel inst_, bool enabled) +{ + Instruction *inst = (Instruction *)inst_.raw(); + InstImm *i0 = (InstImm *) inst; + InstImm *i1 = (InstImm *) i0->next(); + Instruction *i2 = (Instruction *) i1->next(); + + JS_ASSERT(i0->extractOpcode() == ((uint32_t)op_lui >> OpcodeShift)); + JS_ASSERT(i1->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift)); + + if (enabled) { + InstReg jalr = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr); + *i2 = jalr; + } else { + InstNOP nop; + *i2 = nop; + } + + AutoFlushICache::flush(uintptr_t(i2), 4); +} + +void Assembler::updateBoundsCheck(uint32_t heapSize, Instruction *inst) +{ + MOZ_ASSUME_UNREACHABLE("NYI"); +}
diff --git a/src/third_party/mozjs/js/src/jit/mips/Assembler-mips.h b/src/third_party/mozjs/js/src/jit/mips/Assembler-mips.h new file mode 100644 index 0000000..c5ce1f1 --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/Assembler-mips.h
@@ -0,0 +1,1255 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_mips_Assembler_mips_h +#define jit_mips_Assembler_mips_h + +#include "mozilla/ArrayUtils.h" +#include "mozilla/Attributes.h" +#include "mozilla/MathAlgorithms.h" + +#include "jit/CompactBuffer.h" +#include "jit/IonCode.h" +#include "jit/IonSpewer.h" +#include "jit/mips/Architecture-mips.h" +#include "jit/shared/Assembler-shared.h" +#include "jit/shared/IonAssemblerBuffer.h" + +namespace js { +namespace jit { + +static MOZ_CONSTEXPR_VAR Register zero = { Registers::zero }; +static MOZ_CONSTEXPR_VAR Register at = { Registers::at }; +static MOZ_CONSTEXPR_VAR Register v0 = { Registers::v0 }; +static MOZ_CONSTEXPR_VAR Register v1 = { Registers::v1 }; +static MOZ_CONSTEXPR_VAR Register a0 = { Registers::a0 }; +static MOZ_CONSTEXPR_VAR Register a1 = { Registers::a1 }; +static MOZ_CONSTEXPR_VAR Register a2 = { Registers::a2 }; +static MOZ_CONSTEXPR_VAR Register a3 = { Registers::a3 }; +static MOZ_CONSTEXPR_VAR Register t0 = { Registers::t0 }; +static MOZ_CONSTEXPR_VAR Register t1 = { Registers::t1 }; +static MOZ_CONSTEXPR_VAR Register t2 = { Registers::t2 }; +static MOZ_CONSTEXPR_VAR Register t3 = { Registers::t3 }; +static MOZ_CONSTEXPR_VAR Register t4 = { Registers::t4 }; +static MOZ_CONSTEXPR_VAR Register t5 = { Registers::t5 }; +static MOZ_CONSTEXPR_VAR Register t6 = { Registers::t6 }; +static MOZ_CONSTEXPR_VAR Register t7 = { Registers::t7 }; +static MOZ_CONSTEXPR_VAR Register s0 = { Registers::s0 }; +static MOZ_CONSTEXPR_VAR Register s1 = { Registers::s1 }; +static MOZ_CONSTEXPR_VAR Register s2 = { Registers::s2 }; +static MOZ_CONSTEXPR_VAR Register s3 = { Registers::s3 }; +static MOZ_CONSTEXPR_VAR Register s4 = { Registers::s4 }; +static MOZ_CONSTEXPR_VAR Register s5 = { Registers::s5 }; +static MOZ_CONSTEXPR_VAR Register s6 = { Registers::s6 }; +static MOZ_CONSTEXPR_VAR Register s7 = { Registers::s7 }; +static MOZ_CONSTEXPR_VAR Register t8 = { Registers::t8 }; +static MOZ_CONSTEXPR_VAR Register t9 = { Registers::t9 }; +static MOZ_CONSTEXPR_VAR Register k0 = { Registers::k0 }; +static MOZ_CONSTEXPR_VAR Register k1 = { Registers::k1 }; +static MOZ_CONSTEXPR_VAR Register gp = { Registers::gp }; +static MOZ_CONSTEXPR_VAR Register sp = { Registers::sp }; +static MOZ_CONSTEXPR_VAR Register fp = { Registers::fp }; +static MOZ_CONSTEXPR_VAR Register ra = { Registers::ra }; + +static MOZ_CONSTEXPR_VAR Register ScratchRegister = at; +static MOZ_CONSTEXPR_VAR Register SecondScratchReg = t8; + +// Use arg reg from EnterJIT function as OsrFrameReg. +static MOZ_CONSTEXPR_VAR Register OsrFrameReg = a3; +static MOZ_CONSTEXPR_VAR Register ArgumentsRectifierReg = s3; +static MOZ_CONSTEXPR_VAR Register CallTempReg0 = t0; +static MOZ_CONSTEXPR_VAR Register CallTempReg1 = t1; +static MOZ_CONSTEXPR_VAR Register CallTempReg2 = t2; +static MOZ_CONSTEXPR_VAR Register CallTempReg3 = t3; +static MOZ_CONSTEXPR_VAR Register CallTempReg4 = t4; +static MOZ_CONSTEXPR_VAR Register CallTempReg5 = t5; + +static MOZ_CONSTEXPR_VAR Register IntArgReg0 = a0; +static MOZ_CONSTEXPR_VAR Register IntArgReg1 = a1; +static MOZ_CONSTEXPR_VAR Register IntArgReg2 = a2; +static MOZ_CONSTEXPR_VAR Register IntArgReg3 = a3; +static MOZ_CONSTEXPR_VAR Register GlobalReg = s6; // used by Odin +static MOZ_CONSTEXPR_VAR Register HeapReg = s7; // used by Odin +static MOZ_CONSTEXPR_VAR Register CallTempNonArgRegs[] = { t0, t1, t2, t3, t4 }; +static const uint32_t NumCallTempNonArgRegs = mozilla::ArrayLength(CallTempNonArgRegs); + +class ABIArgGenerator +{ + unsigned usedArgSlots_; + bool firstArgFloat; + ABIArg current_; + + public: + ABIArgGenerator(); + ABIArg next(MIRType argType); + ABIArg ¤t() { return current_; } + + uint32_t stackBytesConsumedSoFar() const { + if (usedArgSlots_ <= 4) + return 4 * sizeof(intptr_t); + + return usedArgSlots_ * sizeof(intptr_t); + } + + static const Register NonArgReturnVolatileReg0; + static const Register NonArgReturnVolatileReg1; +}; + +static MOZ_CONSTEXPR_VAR Register PreBarrierReg = a1; + +static MOZ_CONSTEXPR_VAR Register InvalidReg = { Registers::invalid_reg }; +static MOZ_CONSTEXPR_VAR FloatRegister InvalidFloatReg = { FloatRegisters::invalid_freg }; + +static MOZ_CONSTEXPR_VAR Register JSReturnReg_Type = v1; +static MOZ_CONSTEXPR_VAR Register JSReturnReg_Data = v0; +static MOZ_CONSTEXPR_VAR Register StackPointer = sp; +static MOZ_CONSTEXPR_VAR Register FramePointer = fp; +static MOZ_CONSTEXPR_VAR Register ReturnReg = v0; +static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloatReg = { FloatRegisters::f0 }; +static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloatReg = { FloatRegisters::f18 }; +static MOZ_CONSTEXPR_VAR FloatRegister SecondScratchFloatReg = { FloatRegisters::f16 }; + +static MOZ_CONSTEXPR_VAR FloatRegister NANReg = { FloatRegisters::f30 }; + +static MOZ_CONSTEXPR_VAR FloatRegister f0 = {FloatRegisters::f0}; +static MOZ_CONSTEXPR_VAR FloatRegister f2 = {FloatRegisters::f2}; +static MOZ_CONSTEXPR_VAR FloatRegister f4 = {FloatRegisters::f4}; +static MOZ_CONSTEXPR_VAR FloatRegister f6 = {FloatRegisters::f6}; +static MOZ_CONSTEXPR_VAR FloatRegister f8 = {FloatRegisters::f8}; +static MOZ_CONSTEXPR_VAR FloatRegister f10 = {FloatRegisters::f10}; +static MOZ_CONSTEXPR_VAR FloatRegister f12 = {FloatRegisters::f12}; +static MOZ_CONSTEXPR_VAR FloatRegister f14 = {FloatRegisters::f14}; +static MOZ_CONSTEXPR_VAR FloatRegister f16 = {FloatRegisters::f16}; +static MOZ_CONSTEXPR_VAR FloatRegister f18 = {FloatRegisters::f18}; +static MOZ_CONSTEXPR_VAR FloatRegister f20 = {FloatRegisters::f20}; +static MOZ_CONSTEXPR_VAR FloatRegister f22 = {FloatRegisters::f22}; +static MOZ_CONSTEXPR_VAR FloatRegister f24 = {FloatRegisters::f24}; +static MOZ_CONSTEXPR_VAR FloatRegister f26 = {FloatRegisters::f26}; +static MOZ_CONSTEXPR_VAR FloatRegister f28 = {FloatRegisters::f28}; +static MOZ_CONSTEXPR_VAR FloatRegister f30 = {FloatRegisters::f30}; + +// MIPS CPUs can only load multibyte data that is "naturally" +// four-byte-aligned, sp register should be eight-byte-aligned. +static const uint32_t StackAlignment = 8; +static const uint32_t CodeAlignment = 4; +static const bool StackKeptAligned = true; +// NativeFrameSize is the size of return adress on stack in AsmJS functions. +static const uint32_t NativeFrameSize = sizeof(void*); +static const uint32_t AlignmentAtPrologue = 0; +static const uint32_t AlignmentMidPrologue = NativeFrameSize; + +static const Scale ScalePointer = TimesFour; + +// MIPS instruction types +// +---------------------------------------------------------------+ +// | 6 | 5 | 5 | 5 | 5 | 6 | +// +---------------------------------------------------------------+ +// Register type | Opcode | Rs | Rt | Rd | Sa | Function | +// +---------------------------------------------------------------+ +// | 6 | 5 | 5 | 16 | +// +---------------------------------------------------------------+ +// Immediate type | Opcode | Rs | Rt | 2's complement constant | +// +---------------------------------------------------------------+ +// | 6 | 26 | +// +---------------------------------------------------------------+ +// Jump type | Opcode | jump_target | +// +---------------------------------------------------------------+ +// 31 bit bit 0 + +// MIPS instruction encoding constants. +static const uint32_t OpcodeShift = 26; +static const uint32_t OpcodeBits = 6; +static const uint32_t RSShift = 21; +static const uint32_t RSBits = 5; +static const uint32_t RTShift = 16; +static const uint32_t RTBits = 5; +static const uint32_t RDShift = 11; +static const uint32_t RDBits = 5; +static const uint32_t SAShift = 6; +static const uint32_t SABits = 5; +static const uint32_t FunctionShift = 0; +static const uint32_t FunctionBits = 5; +static const uint32_t Imm16Shift = 0; +static const uint32_t Imm16Bits = 16; +static const uint32_t Imm26Shift = 0; +static const uint32_t Imm26Bits = 26; +static const uint32_t Imm28Shift = 0; +static const uint32_t Imm28Bits = 28; +static const uint32_t ImmFieldShift = 2; +static const uint32_t FccMask = 0x7; +static const uint32_t FccShift = 2; + + +// MIPS instruction field bit masks. +static const uint32_t OpcodeMask = ((1 << OpcodeBits) - 1) << OpcodeShift; +static const uint32_t Imm16Mask = ((1 << Imm16Bits) - 1) << Imm16Shift; +static const uint32_t Imm26Mask = ((1 << Imm26Bits) - 1) << Imm26Shift; +static const uint32_t Imm28Mask = ((1 << Imm28Bits) - 1) << Imm28Shift; +static const uint32_t RSMask = ((1 << RSBits) - 1) << RSShift; +static const uint32_t RTMask = ((1 << RTBits) - 1) << RTShift; +static const uint32_t RDMask = ((1 << RDBits) - 1) << RDShift; +static const uint32_t SAMask = ((1 << SABits) - 1) << SAShift; +static const uint32_t FunctionMask = ((1 << FunctionBits) - 1) << FunctionShift; +static const uint32_t RegMask = Registers::Total - 1; +static const uint32_t StackAlignmentMask = StackAlignment - 1; + +static const int32_t MAX_BREAK_CODE = 1024 - 1; + +class Instruction; +class InstReg; +class InstImm; +class InstJump; +class BranchInstBlock; + +uint32_t RS(Register r); +uint32_t RT(Register r); +uint32_t RT(uint32_t regCode); +uint32_t RT(FloatRegister r); +uint32_t RD(Register r); +uint32_t RD(FloatRegister r); +uint32_t RD(uint32_t regCode); +uint32_t SA(uint32_t value); +uint32_t SA(FloatRegister r); + +Register toRS (Instruction &i); +Register toRT (Instruction &i); +Register toRD (Instruction &i); +Register toR (Instruction &i); + +// MIPS enums for instruction fields +enum Opcode { + op_special = 0 << OpcodeShift, + op_regimm = 1 << OpcodeShift, + + op_j = 2 << OpcodeShift, + op_jal = 3 << OpcodeShift, + op_beq = 4 << OpcodeShift, + op_bne = 5 << OpcodeShift, + op_blez = 6 << OpcodeShift, + op_bgtz = 7 << OpcodeShift, + + op_addi = 8 << OpcodeShift, + op_addiu = 9 << OpcodeShift, + op_slti = 10 << OpcodeShift, + op_sltiu = 11 << OpcodeShift, + op_andi = 12 << OpcodeShift, + op_ori = 13 << OpcodeShift, + op_xori = 14 << OpcodeShift, + op_lui = 15 << OpcodeShift, + + op_cop1 = 17 << OpcodeShift, + op_cop1x = 19 << OpcodeShift, + + op_beql = 20 << OpcodeShift, + op_bnel = 21 << OpcodeShift, + op_blezl = 22 << OpcodeShift, + op_bgtzl = 23 << OpcodeShift, + + op_special2 = 28 << OpcodeShift, + op_special3 = 31 << OpcodeShift, + + op_lb = 32 << OpcodeShift, + op_lh = 33 << OpcodeShift, + op_lwl = 34 << OpcodeShift, + op_lw = 35 << OpcodeShift, + op_lbu = 36 << OpcodeShift, + op_lhu = 37 << OpcodeShift, + op_lwr = 38 << OpcodeShift, + op_sb = 40 << OpcodeShift, + op_sh = 41 << OpcodeShift, + op_swl = 42 << OpcodeShift, + op_sw = 43 << OpcodeShift, + op_swr = 46 << OpcodeShift, + + op_lwc1 = 49 << OpcodeShift, + op_ldc1 = 53 << OpcodeShift, + + op_swc1 = 57 << OpcodeShift, + op_sdc1 = 61 << OpcodeShift +}; + +enum RSField { + rs_zero = 0 << RSShift, + // cop1 encoding of RS field. + rs_mfc1 = 0 << RSShift, + rs_one = 1 << RSShift, + rs_cfc1 = 2 << RSShift, + rs_mfhc1 = 3 << RSShift, + rs_mtc1 = 4 << RSShift, + rs_ctc1 = 6 << RSShift, + rs_mthc1 = 7 << RSShift, + rs_bc1 = 8 << RSShift, + rs_s = 16 << RSShift, + rs_d = 17 << RSShift, + rs_w = 20 << RSShift, + rs_ps = 22 << RSShift +}; + +enum RTField { + rt_zero = 0 << RTShift, + // regimm encoding of RT field. + rt_bltz = 0 << RTShift, + rt_bgez = 1 << RTShift, + rt_bltzal = 16 << RTShift, + rt_bgezal = 17 << RTShift +}; + +enum FunctionField { + // special encoding of function field. + ff_sll = 0, + ff_movci = 1, + ff_srl = 2, + ff_sra = 3, + ff_sllv = 4, + ff_srlv = 6, + ff_srav = 7, + + ff_jr = 8, + ff_jalr = 9, + ff_movz = 10, + ff_movn = 11, + ff_break = 13, + + ff_mfhi = 16, + ff_mflo = 18, + + ff_mult = 24, + ff_multu = 25, + ff_div = 26, + ff_divu = 27, + + ff_add = 32, + ff_addu = 33, + ff_sub = 34, + ff_subu = 35, + ff_and = 36, + ff_or = 37, + ff_xor = 38, + ff_nor = 39, + + ff_slt = 42, + ff_sltu = 43, + + // special2 encoding of function field. + ff_mul = 2, + ff_clz = 32, + ff_clo = 33, + + // special3 encoding of function field. + ff_ext = 0, + ff_ins = 4, + + // cop1 encoding of function field. + ff_add_fmt = 0, + ff_sub_fmt = 1, + ff_mul_fmt = 2, + ff_div_fmt = 3, + ff_sqrt_fmt = 4, + ff_abs_fmt = 5, + ff_mov_fmt = 6, + ff_neg_fmt = 7, + + ff_round_w_fmt = 12, + ff_trunc_w_fmt = 13, + ff_ceil_w_fmt = 14, + ff_floor_w_fmt = 15, + + ff_cvt_s_fmt = 32, + ff_cvt_d_fmt = 33, + ff_cvt_w_fmt = 36, + + ff_c_f_fmt = 48, + ff_c_un_fmt = 49, + ff_c_eq_fmt = 50, + ff_c_ueq_fmt = 51, + ff_c_olt_fmt = 52, + ff_c_ult_fmt = 53, + ff_c_ole_fmt = 54, + ff_c_ule_fmt = 55, +}; + +class MacroAssemblerMIPS; +class Operand; + +// A BOffImm16 is a 16 bit immediate that is used for branches. +class BOffImm16 +{ + uint32_t data; + + public: + uint32_t encode() { + JS_ASSERT(!isInvalid()); + return data; + } + int32_t decode() { + JS_ASSERT(!isInvalid()); + return (int32_t(data << 18) >> 16) + 4; + } + + explicit BOffImm16(int offset) + : data ((offset - 4) >> 2 & Imm16Mask) + { + JS_ASSERT((offset & 0x3) == 0); + JS_ASSERT(isInRange(offset)); + } + static bool isInRange(int offset) { + if ((offset - 4) < (INT16_MIN << 2)) + return false; + if ((offset - 4) > (INT16_MAX << 2)) + return false; + return true; + } + static const uint32_t INVALID = 0x00020000; + BOffImm16() + : data(INVALID) + { } + + bool isInvalid() { + return data == INVALID; + } + Instruction *getDest(Instruction *src); + + BOffImm16(InstImm inst); +}; + +// A JOffImm26 is a 26 bit immediate that is used for unconditional jumps. +class JOffImm26 +{ + uint32_t data; + + public: + uint32_t encode() { + JS_ASSERT(!isInvalid()); + return data; + } + int32_t decode() { + JS_ASSERT(!isInvalid()); + return (int32_t(data << 8) >> 6) + 4; + } + + explicit JOffImm26(int offset) + : data ((offset - 4) >> 2 & Imm26Mask) + { + JS_ASSERT((offset & 0x3) == 0); + JS_ASSERT(isInRange(offset)); + } + static bool isInRange(int offset) { + if ((offset - 4) < -536870912) + return false; + if ((offset - 4) > 536870908) + return false; + return true; + } + static const uint32_t INVALID = 0x20000000; + JOffImm26() + : data(INVALID) + { } + + bool isInvalid() { + return data == INVALID; + } + Instruction *getDest(Instruction *src); + +}; + +class Imm16 +{ + uint16_t value; + + public: + Imm16(); + Imm16(uint32_t imm) + : value(imm) + { } + uint32_t encode() { + return value; + } + int32_t decodeSigned() { + return value; + } + uint32_t decodeUnsigned() { + return value; + } + static bool isInSignedRange(int32_t imm) { + return imm >= INT16_MIN && imm <= INT16_MAX; + } + static bool isInUnsignedRange(uint32_t imm) { + return imm <= UINT16_MAX ; + } + static Imm16 lower (Imm32 imm) { + return Imm16(imm.value & 0xffff); + } + static Imm16 upper (Imm32 imm) { + return Imm16((imm.value >> 16) & 0xffff); + } +}; + +class Operand +{ + public: + enum Tag { + REG, + FREG, + MEM + }; + + private: + Tag tag : 3; + uint32_t reg : 5; + int32_t offset; + + public: + Operand (Register reg_) + : tag(REG), reg(reg_.code()) + { } + + Operand (FloatRegister freg) + : tag(FREG), reg(freg.code()) + { } + + Operand (Register base, Imm32 off) + : tag(MEM), reg(base.code()), offset(off.value) + { } + + Operand (Register base, int32_t off) + : tag(MEM), reg(base.code()), offset(off) + { } + + Operand (const Address &addr) + : tag(MEM), reg(addr.base.code()), offset(addr.offset) + { } + + Tag getTag() const { + return tag; + } + + Register toReg() const { + JS_ASSERT(tag == REG); + return Register::FromCode(reg); + } + + FloatRegister toFReg() const { + JS_ASSERT(tag == FREG); + return FloatRegister::FromCode(reg); + } + + void toAddr(Register *r, Imm32 *dest) const { + JS_ASSERT(tag == MEM); + *r = Register::FromCode(reg); + *dest = Imm32(offset); + } + Address toAddress() const { + JS_ASSERT(tag == MEM); + return Address(Register::FromCode(reg), offset); + } + int32_t disp() const { + JS_ASSERT(tag == MEM); + return offset; + } + + int32_t base() const { + JS_ASSERT(tag == MEM); + return reg; + } + Register baseReg() const { + JS_ASSERT(tag == MEM); + return Register::FromCode(reg); + } +}; + +void +PatchJump(CodeLocationJump &jump_, CodeLocationLabel label); +class Assembler; +typedef js::jit::AssemblerBuffer<1024, Instruction> MIPSBuffer; + +class Assembler : public AssemblerShared +{ + public: + + enum Condition { + Equal, + NotEqual, + Above, + AboveOrEqual, + Below, + BelowOrEqual, + GreaterThan, + GreaterThanOrEqual, + LessThan, + LessThanOrEqual, + Overflow, + Signed, + NotSigned, + Zero, + NonZero, + Always, + }; + + enum DoubleCondition { + // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. + DoubleOrdered, + DoubleEqual, + DoubleNotEqual, + DoubleGreaterThan, + DoubleGreaterThanOrEqual, + DoubleLessThan, + DoubleLessThanOrEqual, + // If either operand is NaN, these conditions always evaluate to true. + DoubleUnordered, + DoubleEqualOrUnordered, + DoubleNotEqualOrUnordered, + DoubleGreaterThanOrUnordered, + DoubleGreaterThanOrEqualOrUnordered, + DoubleLessThanOrUnordered, + DoubleLessThanOrEqualOrUnordered + }; + + enum FPConditionBit { + FCC0 = 0, + FCC1, + FCC2, + FCC3, + FCC4, + FCC5, + FCC6, + FCC7 + }; + + enum FloatFormat { + SingleFloat, + DoubleFloat + }; + + enum JumpOrCall { + BranchIsJump, + BranchIsCall + }; + + enum FloatTestKind { + TestForTrue, + TestForFalse + }; + + // :( this should be protected, but since CodeGenerator + // wants to use it, It needs to go out here :( + + BufferOffset nextOffset() { + return m_buffer.nextOffset(); + } + + protected: + Instruction * editSrc (BufferOffset bo) { + return m_buffer.getInst(bo); + } + public: + uint32_t actualOffset(uint32_t) const; + uint32_t actualIndex(uint32_t) const; + static uint8_t *PatchableJumpAddress(JitCode *code, uint32_t index); + protected: + + // structure for fixing up pc-relative loads/jumps when a the machine code + // gets moved (executable copy, gc, etc.) + struct RelativePatch + { + // the offset within the code buffer where the value is loaded that + // we want to fix-up + BufferOffset offset; + void *target; + Relocation::Kind kind; + + RelativePatch(BufferOffset offset, void *target, Relocation::Kind kind) + : offset(offset), + target(target), + kind(kind) + { } + }; + + js::Vector<CodeLabel, 0, SystemAllocPolicy> codeLabels_; + js::Vector<RelativePatch, 8, SystemAllocPolicy> jumps_; + js::Vector<uint32_t, 8, SystemAllocPolicy> longJumps_; + + CompactBufferWriter jumpRelocations_; + CompactBufferWriter dataRelocations_; + CompactBufferWriter relocations_; + CompactBufferWriter preBarriers_; + + bool enoughMemory_; + + MIPSBuffer m_buffer; + + public: + Assembler() + : enoughMemory_(true), + m_buffer(), + isFinished(false) + { } + + static Condition InvertCondition(Condition cond); + static DoubleCondition InvertCondition(DoubleCondition cond); + + // MacroAssemblers hold onto gcthings, so they are traced by the GC. + void trace(JSTracer *trc); + void writeRelocation(BufferOffset src) { + jumpRelocations_.writeUnsigned(src.getOffset()); + } + + // As opposed to x86/x64 version, the data relocation has to be executed + // before to recover the pointer, and not after. + void writeDataRelocation(const ImmGCPtr &ptr) { + if (ptr.value) + dataRelocations_.writeUnsigned(nextOffset().getOffset()); + } + void writePrebarrierOffset(CodeOffsetLabel label) { + preBarriers_.writeUnsigned(label.offset()); + } + + public: + static uintptr_t getPointer(uint8_t *); + + bool oom() const; + + void setPrinter(Sprinter *sp) { + } + + private: + bool isFinished; + public: + void finish(); + void executableCopy(void *buffer); + void copyJumpRelocationTable(uint8_t *dest); + void copyDataRelocationTable(uint8_t *dest); + void copyPreBarrierTable(uint8_t *dest); + + bool addCodeLabel(CodeLabel label); + size_t numCodeLabels() const { + return codeLabels_.length(); + } + CodeLabel codeLabel(size_t i) { + return codeLabels_[i]; + } + + // Size of the instruction stream, in bytes. + size_t size() const; + // Size of the jump relocation table, in bytes. + size_t jumpRelocationTableBytes() const; + size_t dataRelocationTableBytes() const; + size_t preBarrierTableBytes() const; + + // Size of the data table, in bytes. + size_t bytesNeeded() const; + + // Write a blob of binary into the instruction stream *OR* + // into a destination address. If dest is nullptr (the default), then the + // instruction gets written into the instruction stream. If dest is not null + // it is interpreted as a pointer to the location that we want the + // instruction to be written. + BufferOffset writeInst(uint32_t x, uint32_t *dest = nullptr); + // A static variant for the cases where we don't want to have an assembler + // object at all. Normally, you would use the dummy (nullptr) object. + static void writeInstStatic(uint32_t x, uint32_t *dest); + + public: + BufferOffset align(int alignment); + BufferOffset as_nop(); + + // Branch and jump instructions + BufferOffset as_bal(BOffImm16 off); + + InstImm getBranchCode(JumpOrCall jumpOrCall); + InstImm getBranchCode(Register s, Register t, Condition c); + InstImm getBranchCode(Register s, Condition c); + InstImm getBranchCode(FloatTestKind testKind, FPConditionBit fcc); + + BufferOffset as_j(JOffImm26 off); + BufferOffset as_jal(JOffImm26 off); + + BufferOffset as_jr(Register rs); + BufferOffset as_jalr(Register rs); + + // Arithmetic instructions + BufferOffset as_addu(Register rd, Register rs, Register rt); + BufferOffset as_addiu(Register rd, Register rs, int32_t j); + BufferOffset as_subu(Register rd, Register rs, Register rt); + BufferOffset as_mult(Register rs, Register rt); + BufferOffset as_multu(Register rs, Register rt); + BufferOffset as_div(Register rs, Register rt); + BufferOffset as_divu(Register rs, Register rt); + BufferOffset as_mul(Register rd, Register rs, Register rt); + + // Logical instructions + BufferOffset as_and(Register rd, Register rs, Register rt); + BufferOffset as_or(Register rd, Register rs, Register rt); + BufferOffset as_xor(Register rd, Register rs, Register rt); + BufferOffset as_nor(Register rd, Register rs, Register rt); + + BufferOffset as_andi(Register rd, Register rs, int32_t j); + BufferOffset as_ori(Register rd, Register rs, int32_t j); + BufferOffset as_xori(Register rd, Register rs, int32_t j); + BufferOffset as_lui(Register rd, int32_t j); + + // Shift instructions + // as_sll(zero, zero, x) instructions are reserved as nop + BufferOffset as_sll(Register rd, Register rt, uint16_t sa); + BufferOffset as_sllv(Register rd, Register rt, Register rs); + BufferOffset as_srl(Register rd, Register rt, uint16_t sa); + BufferOffset as_srlv(Register rd, Register rt, Register rs); + BufferOffset as_sra(Register rd, Register rt, uint16_t sa); + BufferOffset as_srav(Register rd, Register rt, Register rs); + BufferOffset as_rotr(Register rd, Register rt, uint16_t sa); + BufferOffset as_rotrv(Register rd, Register rt, Register rs); + + // Load and store instructions + BufferOffset as_lb(Register rd, Register rs, int16_t off); + BufferOffset as_lbu(Register rd, Register rs, int16_t off); + BufferOffset as_lh(Register rd, Register rs, int16_t off); + BufferOffset as_lhu(Register rd, Register rs, int16_t off); + BufferOffset as_lw(Register rd, Register rs, int16_t off); + BufferOffset as_lwl(Register rd, Register rs, int16_t off); + BufferOffset as_lwr(Register rd, Register rs, int16_t off); + BufferOffset as_sb(Register rd, Register rs, int16_t off); + BufferOffset as_sh(Register rd, Register rs, int16_t off); + BufferOffset as_sw(Register rd, Register rs, int16_t off); + BufferOffset as_swl(Register rd, Register rs, int16_t off); + BufferOffset as_swr(Register rd, Register rs, int16_t off); + + // Move from HI/LO register. + BufferOffset as_mfhi(Register rd); + BufferOffset as_mflo(Register rd); + + // Set on less than. + BufferOffset as_slt(Register rd, Register rs, Register rt); + BufferOffset as_sltu(Register rd, Register rs, Register rt); + BufferOffset as_slti(Register rd, Register rs, int32_t j); + BufferOffset as_sltiu(Register rd, Register rs, uint32_t j); + + // Conditional move. + BufferOffset as_movz(Register rd, Register rs, Register rt); + BufferOffset as_movn(Register rd, Register rs, Register rt); + BufferOffset as_movt(Register rd, Register rs, uint16_t cc = 0); + BufferOffset as_movf(Register rd, Register rs, uint16_t cc = 0); + + // Bit twiddling. + BufferOffset as_clz(Register rd, Register rs, Register rt = Register::FromCode(0)); + BufferOffset as_ins(Register rt, Register rs, uint16_t pos, uint16_t size); + BufferOffset as_ext(Register rt, Register rs, uint16_t pos, uint16_t size); + + // FP instructions + + // Use these two functions only when you are sure address is aligned. + // Otherwise, use ma_ld and ma_sd. + BufferOffset as_ld(FloatRegister fd, Register base, int32_t off); + BufferOffset as_sd(FloatRegister fd, Register base, int32_t off); + + BufferOffset as_ls(FloatRegister fd, Register base, int32_t off); + BufferOffset as_ss(FloatRegister fd, Register base, int32_t off); + + BufferOffset as_movs(FloatRegister fd, FloatRegister fs); + BufferOffset as_movd(FloatRegister fd, FloatRegister fs); + + BufferOffset as_mtc1(Register rt, FloatRegister fs); + BufferOffset as_mfc1(Register rt, FloatRegister fs); + + protected: + // This is used to access the odd regiter form the pair of single + // precision registers that make one double register. + FloatRegister getOddPair(FloatRegister reg) { + JS_ASSERT(reg.code() % 2 == 0); + return FloatRegister::FromCode(reg.code() + 1); + } + + public: + // FP convert instructions + BufferOffset as_ceilws(FloatRegister fd, FloatRegister fs); + BufferOffset as_floorws(FloatRegister fd, FloatRegister fs); + BufferOffset as_roundws(FloatRegister fd, FloatRegister fs); + BufferOffset as_truncws(FloatRegister fd, FloatRegister fs); + + BufferOffset as_ceilwd(FloatRegister fd, FloatRegister fs); + BufferOffset as_floorwd(FloatRegister fd, FloatRegister fs); + BufferOffset as_roundwd(FloatRegister fd, FloatRegister fs); + BufferOffset as_truncwd(FloatRegister fd, FloatRegister fs); + + BufferOffset as_cvtdl(FloatRegister fd, FloatRegister fs); + BufferOffset as_cvtds(FloatRegister fd, FloatRegister fs); + BufferOffset as_cvtdw(FloatRegister fd, FloatRegister fs); + BufferOffset as_cvtld(FloatRegister fd, FloatRegister fs); + BufferOffset as_cvtls(FloatRegister fd, FloatRegister fs); + BufferOffset as_cvtsd(FloatRegister fd, FloatRegister fs); + BufferOffset as_cvtsl(FloatRegister fd, FloatRegister fs); + BufferOffset as_cvtsw(FloatRegister fd, FloatRegister fs); + BufferOffset as_cvtwd(FloatRegister fd, FloatRegister fs); + BufferOffset as_cvtws(FloatRegister fd, FloatRegister fs); + + // FP arithmetic instructions + BufferOffset as_adds(FloatRegister fd, FloatRegister fs, FloatRegister ft); + BufferOffset as_addd(FloatRegister fd, FloatRegister fs, FloatRegister ft); + BufferOffset as_subs(FloatRegister fd, FloatRegister fs, FloatRegister ft); + BufferOffset as_subd(FloatRegister fd, FloatRegister fs, FloatRegister ft); + + BufferOffset as_abss(FloatRegister fd, FloatRegister fs); + BufferOffset as_absd(FloatRegister fd, FloatRegister fs); + BufferOffset as_negd(FloatRegister fd, FloatRegister fs); + + BufferOffset as_muls(FloatRegister fd, FloatRegister fs, FloatRegister ft); + BufferOffset as_muld(FloatRegister fd, FloatRegister fs, FloatRegister ft); + BufferOffset as_divs(FloatRegister fd, FloatRegister fs, FloatRegister ft); + BufferOffset as_divd(FloatRegister fd, FloatRegister fs, FloatRegister ft); + BufferOffset as_sqrts(FloatRegister fd, FloatRegister fs); + BufferOffset as_sqrtd(FloatRegister fd, FloatRegister fs); + + // FP compare instructions + BufferOffset as_cf(FloatFormat fmt, FloatRegister fs, FloatRegister ft, + FPConditionBit fcc = FCC0); + BufferOffset as_cun(FloatFormat fmt, FloatRegister fs, FloatRegister ft, + FPConditionBit fcc = FCC0); + BufferOffset as_ceq(FloatFormat fmt, FloatRegister fs, FloatRegister ft, + FPConditionBit fcc = FCC0); + BufferOffset as_cueq(FloatFormat fmt, FloatRegister fs, FloatRegister ft, + FPConditionBit fcc = FCC0); + BufferOffset as_colt(FloatFormat fmt, FloatRegister fs, FloatRegister ft, + FPConditionBit fcc = FCC0); + BufferOffset as_cult(FloatFormat fmt, FloatRegister fs, FloatRegister ft, + FPConditionBit fcc = FCC0); + BufferOffset as_cole(FloatFormat fmt, FloatRegister fs, FloatRegister ft, + FPConditionBit fcc = FCC0); + BufferOffset as_cule(FloatFormat fmt, FloatRegister fs, FloatRegister ft, + FPConditionBit fcc = FCC0); + + // label operations + void bind(Label *label, BufferOffset boff = BufferOffset()); + void bind(RepatchLabel *label); + uint32_t currentOffset() { + return nextOffset().getOffset(); + } + void retarget(Label *label, Label *target); + void Bind(uint8_t *rawCode, AbsoluteLabel *label, const void *address); + + // See Bind + size_t labelOffsetToPatchOffset(size_t offset) { + return actualOffset(offset); + } + + void call(Label *label); + void call(void *target); + + void as_break(uint32_t code); + + public: + static void TraceJumpRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader); + static void TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader); + + protected: + InstImm invertBranch(InstImm branch, BOffImm16 skipOffset); + void bind(InstImm *inst, uint32_t branch, uint32_t target); + void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind) { + enoughMemory_ &= jumps_.append(RelativePatch(src, target.value, kind)); + if (kind == Relocation::JITCODE) + writeRelocation(src); + } + + void addLongJump(BufferOffset src) { + enoughMemory_ &= longJumps_.append(src.getOffset()); + } + + public: + size_t numLongJumps() const { + return longJumps_.length(); + } + uint32_t longJump(size_t i) { + return longJumps_[i]; + } + + // Copy the assembly code to the given buffer, and perform any pending + // relocations relying on the target address. + void executableCopy(uint8_t *buffer); + + void flushBuffer() { + } + + static uint32_t patchWrite_NearCallSize(); + static uint32_t nopSize() { return 4; } + + static uint32_t extractLuiOriValue(Instruction *inst0, Instruction *inst1); + static void updateLuiOriValue(Instruction *inst0, Instruction *inst1, uint32_t value); + static void writeLuiOriInstructions(Instruction *inst, Instruction *inst1, + Register reg, uint32_t value); + + static void patchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall); + static void patchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue, + PatchedImmPtr expectedValue); + static void patchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue, + ImmPtr expectedValue); + static void patchWrite_Imm32(CodeLocationLabel label, Imm32 imm); + static uint32_t alignDoubleArg(uint32_t offset) { + return (offset + 1U) &~ 1U; + } + + static uint8_t *nextInstruction(uint8_t *instruction, uint32_t *count = nullptr); + + static void ToggleToJmp(CodeLocationLabel inst_); + static void ToggleToCmp(CodeLocationLabel inst_); + + static void ToggleCall(CodeLocationLabel inst_, bool enabled); + + static void updateBoundsCheck(uint32_t logHeapSize, Instruction *inst); + void processCodeLabels(uint8_t *rawCode); + + bool bailed() { + return m_buffer.bail(); + } +}; // Assembler + +// An Instruction is a structure for both encoding and decoding any and all +// MIPS instructions. +class Instruction +{ + protected: + // sll zero, zero, 0 + static const uint32_t NopInst = 0x00000000; + + uint32_t data; + + // Standard constructor + Instruction (uint32_t data_) : data(data_) { } + + // You should never create an instruction directly. You should create a + // more specific instruction which will eventually call one of these + // constructors for you. + public: + uint32_t encode() const { + return data; + } + + void makeNop() { + data = NopInst; + } + + void setData(uint32_t data) { + this->data = data; + } + + const Instruction & operator=(const Instruction &src) { + data = src.data; + return *this; + } + + // Extract the one particular bit. + uint32_t extractBit(uint32_t bit) { + return (encode() >> bit) & 1; + } + // Extract a bit field out of the instruction + uint32_t extractBitField(uint32_t hi, uint32_t lo) { + return (encode() >> lo) & ((2 << (hi - lo)) - 1); + } + // Since all MIPS instructions have opcode, the opcode + // extractor resides in the base class. + uint32_t extractOpcode() { + return extractBitField(OpcodeShift + OpcodeBits - 1, OpcodeShift); + } + // Return the fields at their original place in the instruction encoding. + Opcode OpcodeFieldRaw() const { + return static_cast<Opcode>(encode() & OpcodeMask); + } + + // Get the next instruction in the instruction stream. + // This does neat things like ignoreconstant pools and their guards. + Instruction *next(); + + // Sometimes, an api wants a uint32_t (or a pointer to it) rather than + // an instruction. raw() just coerces this into a pointer to a uint32_t + const uint32_t *raw() const { return &data; } + uint32_t size() const { return 4; } +}; // Instruction + +// make sure that it is the right size +static_assert(sizeof(Instruction) == 4, "Size of Instruction class has to be 4 bytes."); + +class InstNOP : public Instruction +{ + public: + InstNOP() + : Instruction(NopInst) + { } + +}; + +// Class for register type instructions. +class InstReg : public Instruction +{ + public: + InstReg(Opcode op, Register rd, FunctionField ff) + : Instruction(op | RD(rd) | ff) + { } + InstReg(Opcode op, Register rs, Register rt, FunctionField ff) + : Instruction(op | RS(rs) | RT(rt) | ff) + { } + InstReg(Opcode op, Register rs, Register rt, Register rd, FunctionField ff) + : Instruction(op | RS(rs) | RT(rt) | RD(rd) | ff) + { } + InstReg(Opcode op, Register rs, Register rt, Register rd, uint32_t sa, FunctionField ff) + : Instruction(op | RS(rs) | RT(rt) | RD(rd) | SA(sa) | ff) + { } + InstReg(Opcode op, RSField rs, Register rt, Register rd, uint32_t sa, FunctionField ff) + : Instruction(op | rs | RT(rt) | RD(rd) | SA(sa) | ff) + { } + InstReg(Opcode op, Register rs, RTField rt, Register rd, uint32_t sa, FunctionField ff) + : Instruction(op | RS(rs) | rt | RD(rd) | SA(sa) | ff) + { } + InstReg(Opcode op, Register rs, uint32_t cc, Register rd, uint32_t sa, FunctionField ff) + : Instruction(op | RS(rs) | cc | RD(rd) | SA(sa) | ff) + { } + InstReg(Opcode op, uint32_t code, FunctionField ff) + : Instruction(op | code | ff) + { } + // for float point + InstReg(Opcode op, RSField rs, Register rt, FloatRegister rd) + : Instruction(op | rs | RT(rt) | RD(rd)) + { } + InstReg(Opcode op, RSField rs, Register rt, FloatRegister rd, uint32_t sa, FunctionField ff) + : Instruction(op | rs | RT(rt) | RD(rd) | SA(sa) | ff) + { } + InstReg(Opcode op, RSField rs, Register rt, FloatRegister fs, FloatRegister fd, FunctionField ff) + : Instruction(op | rs | RT(rt) | RD(fs) | SA(fd) | ff) + { } + InstReg(Opcode op, RSField rs, FloatRegister ft, FloatRegister fs, FloatRegister fd, FunctionField ff) + : Instruction(op | rs | RT(ft) | RD(fs) | SA(fd) | ff) + { } + InstReg(Opcode op, RSField rs, FloatRegister ft, FloatRegister fd, uint32_t sa, FunctionField ff) + : Instruction(op | rs | RT(ft) | RD(fd) | SA(sa) | ff) + { } + + uint32_t extractRS () { + return extractBitField(RSShift + RSBits - 1, RSShift); + } + uint32_t extractRT () { + return extractBitField(RTShift + RTBits - 1, RTShift); + } + uint32_t extractRD () { + return extractBitField(RDShift + RDBits - 1, RDShift); + } + uint32_t extractSA () { + return extractBitField(SAShift + SABits - 1, SAShift); + } + uint32_t extractFunctionField () { + return extractBitField(FunctionShift + FunctionBits - 1, FunctionShift); + } +}; + +// Class for branch, load and store instructions with immediate offset. +class InstImm : public Instruction +{ + public: + void extractImm16(BOffImm16 *dest); + + InstImm(Opcode op, Register rs, Register rt, BOffImm16 off) + : Instruction(op | RS(rs) | RT(rt) | off.encode()) + { } + InstImm(Opcode op, Register rs, RTField rt, BOffImm16 off) + : Instruction(op | RS(rs) | rt | off.encode()) + { } + InstImm(Opcode op, RSField rs, uint32_t cc, BOffImm16 off) + : Instruction(op | rs | cc | off.encode()) + { } + InstImm(Opcode op, Register rs, Register rt, Imm16 off) + : Instruction(op | RS(rs) | RT(rt) | off.encode()) + { } + InstImm(uint32_t raw) + : Instruction(raw) + { } + // For floating-point loads and stores. + InstImm(Opcode op, Register rs, FloatRegister rt, Imm16 off) + : Instruction(op | RS(rs) | RT(rt) | off.encode()) + { } + + uint32_t extractOpcode() { + return extractBitField(OpcodeShift + OpcodeBits - 1, OpcodeShift); + } + void setOpcode(Opcode op) { + data = (data & ~OpcodeMask) | op; + } + uint32_t extractRS() { + return extractBitField(RSShift + RSBits - 1, RSShift); + } + uint32_t extractRT() { + return extractBitField(RTShift + RTBits - 1, RTShift); + } + void setRT(RTField rt) { + data = (data & ~RTMask) | rt; + } + uint32_t extractImm16Value() { + return extractBitField(Imm16Shift + Imm16Bits - 1, Imm16Shift); + } + void setBOffImm16(BOffImm16 off) { + // Reset immediate field and replace it + data = (data & ~Imm16Mask) | off.encode(); + } + void setImm16(Imm16 off) { + // Reset immediate field and replace it + data = (data & ~Imm16Mask) | off.encode(); + } +}; + +// Class for Jump type instructions. +class InstJump : public Instruction +{ + public: + InstJump(Opcode op, JOffImm26 off) + : Instruction(op | off.encode()) + { } + + uint32_t extractImm26Value() { + return extractBitField(Imm26Shift + Imm26Bits - 1, Imm26Shift); + } +}; + +static const uint32_t NumIntArgRegs = 4; + +static inline bool +GetIntArgReg(uint32_t usedArgSlots, Register *out) +{ + if (usedArgSlots < NumIntArgRegs) { + *out = Register::FromCode(a0.code() + usedArgSlots); + return true; + } + return false; +} + +// Get a register in which we plan to put a quantity that will be used as an +// integer argument. This differs from GetIntArgReg in that if we have no more +// actual argument registers to use we will fall back on using whatever +// CallTempReg* don't overlap the argument registers, and only fail once those +// run out too. +static inline bool +GetTempRegForIntArg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register *out) +{ + // NOTE: We can't properly determine which regs are used if there are + // float arguments. If this is needed, we will have to guess. + JS_ASSERT(usedFloatArgs == 0); + + if (GetIntArgReg(usedIntArgs, out)) + return true; + // Unfortunately, we have to assume things about the point at which + // GetIntArgReg returns false, because we need to know how many registers it + // can allocate. + usedIntArgs -= NumIntArgRegs; + if (usedIntArgs >= NumCallTempNonArgRegs) + return false; + *out = CallTempNonArgRegs[usedIntArgs]; + return true; +} + +static inline uint32_t +GetArgStackDisp(uint32_t usedArgSlots) +{ + JS_ASSERT(usedArgSlots >= NumIntArgRegs); + // Even register arguments have place reserved on stack. + return usedArgSlots * sizeof(intptr_t); +} + +} // namespace jit +} // namespace js + +#endif /* jit_mips_Assembler_mips_h */
diff --git a/src/third_party/mozjs/js/src/jit/mips/Bailouts-mips.cpp b/src/third_party/mozjs/js/src/jit/mips/Bailouts-mips.cpp new file mode 100644 index 0000000..4cde9d7 --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/Bailouts-mips.cpp
@@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/mips/Bailouts-mips.h" + +#include "jscntxt.h" +#include "jscompartment.h" + +using namespace js; +using namespace js::jit; + +IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations, + BailoutStack *bailout) + : IonFrameIterator(activations), + machine_(bailout->machine()) +{ + uint8_t *sp = bailout->parentStackPointer(); + uint8_t *fp = sp + bailout->frameSize(); + + current_ = fp; + type_ = JitFrame_IonJS; + topFrameSize_ = current_ - sp; + topIonScript_ = script()->ionScript(); + + if (bailout->frameClass() == FrameSizeClass::None()) { + snapshotOffset_ = bailout->snapshotOffset(); + return; + } + + // Compute the snapshot offset from the bailout ID. + JitActivation *activation = activations.activation()->asJit(); + JSRuntime *rt = activation->compartment()->runtimeFromMainThread(); + JitCode *code = rt->jitRuntime()->getBailoutTable(bailout->frameClass()); + uintptr_t tableOffset = bailout->tableOffset(); + uintptr_t tableStart = reinterpret_cast<uintptr_t>(code->raw()); + + MOZ_ASSERT(tableOffset >= tableStart && + tableOffset < tableStart + code->instructionsSize()); + MOZ_ASSERT((tableOffset - tableStart) % BAILOUT_TABLE_ENTRY_SIZE == 0); + + uint32_t bailoutId = ((tableOffset - tableStart) / BAILOUT_TABLE_ENTRY_SIZE) - 1; + MOZ_ASSERT(bailoutId < BAILOUT_TABLE_SIZE); + + snapshotOffset_ = topIonScript_->bailoutToSnapshot(bailoutId); +} + +IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations, + InvalidationBailoutStack *bailout) + : IonFrameIterator(activations), + machine_(bailout->machine()) +{ + returnAddressToFp_ = bailout->osiPointReturnAddress(); + topIonScript_ = bailout->ionScript(); + const OsiIndex *osiIndex = topIonScript_->getOsiIndex(returnAddressToFp_); + + current_ = (uint8_t*) bailout->fp(); + type_ = JitFrame_IonJS; + topFrameSize_ = current_ - bailout->sp(); + snapshotOffset_ = osiIndex->snapshotOffset(); +}
diff --git a/src/third_party/mozjs/js/src/jit/mips/Bailouts-mips.h b/src/third_party/mozjs/js/src/jit/mips/Bailouts-mips.h new file mode 100644 index 0000000..dd14d1e --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/Bailouts-mips.h
@@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_mips_Bailouts_mips_h +#define jit_mips_Bailouts_mips_h + +#include "jit/Bailouts.h" +#include "jit/JitCompartment.h" + +namespace js { +namespace jit { + +class BailoutStack +{ + uintptr_t frameClassId_; + // This is pushed in the bailout handler. Both entry points into the + // handler inserts their own value int lr, which is then placed onto the + // stack along with frameClassId_ above. This should be migrated to ip. + public: + union { + uintptr_t frameSize_; + uintptr_t tableOffset_; + }; + + protected: + mozilla::Array<double, FloatRegisters::Total> fpregs_; + mozilla::Array<uintptr_t, Registers::Total> regs_; + + uintptr_t snapshotOffset_; + uintptr_t padding_; + + public: + FrameSizeClass frameClass() const { + return FrameSizeClass::FromClass(frameClassId_); + } + uintptr_t tableOffset() const { + MOZ_ASSERT(frameClass() != FrameSizeClass::None()); + return tableOffset_; + } + uint32_t frameSize() const { + if (frameClass() == FrameSizeClass::None()) + return frameSize_; + return frameClass().frameSize(); + } + MachineState machine() { + return MachineState::FromBailout(regs_, fpregs_); + } + SnapshotOffset snapshotOffset() const { + MOZ_ASSERT(frameClass() == FrameSizeClass::None()); + return snapshotOffset_; + } + uint8_t *parentStackPointer() const { + if (frameClass() == FrameSizeClass::None()) + return (uint8_t *)this + sizeof(BailoutStack); + return (uint8_t *)this + offsetof(BailoutStack, snapshotOffset_); + } + static size_t offsetOfFrameClass() { + return offsetof(BailoutStack, frameClassId_); + } + static size_t offsetOfFrameSize() { + return offsetof(BailoutStack, frameSize_); + } + static size_t offsetOfFpRegs() { + return offsetof(BailoutStack, fpregs_); + } + static size_t offsetOfRegs() { + return offsetof(BailoutStack, regs_); + } +}; + +} // namespace jit +} // namespace js + +#endif /* jit_mips_Bailouts_mips_h */
diff --git a/src/third_party/mozjs/js/src/jit/mips/BaselineCompiler-mips.cpp b/src/third_party/mozjs/js/src/jit/mips/BaselineCompiler-mips.cpp new file mode 100644 index 0000000..69c935b --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/BaselineCompiler-mips.cpp
@@ -0,0 +1,16 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/mips/BaselineCompiler-mips.h" + +using namespace js; +using namespace js::jit; + +BaselineCompilerMIPS::BaselineCompilerMIPS(JSContext *cx, TempAllocator &alloc, + HandleScript script) + : BaselineCompilerShared(cx, alloc, script) +{ +}
diff --git a/src/third_party/mozjs/js/src/jit/mips/BaselineCompiler-mips.h b/src/third_party/mozjs/js/src/jit/mips/BaselineCompiler-mips.h new file mode 100644 index 0000000..7db49b3 --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/BaselineCompiler-mips.h
@@ -0,0 +1,26 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_mips_BaselineCompiler_mips_h +#define jit_mips_BaselineCompiler_mips_h + +#include "jit/shared/BaselineCompiler-shared.h" + +namespace js { +namespace jit { + +class BaselineCompilerMIPS : public BaselineCompilerShared +{ + protected: + BaselineCompilerMIPS(JSContext *cx, TempAllocator &alloc, HandleScript script); +}; + +typedef BaselineCompilerMIPS BaselineCompilerSpecific; + +} // namespace jit +} // namespace js + +#endif /* jit_mips_BaselineCompiler_mips_h */
diff --git a/src/third_party/mozjs/js/src/jit/mips/BaselineHelpers-mips.h b/src/third_party/mozjs/js/src/jit/mips/BaselineHelpers-mips.h new file mode 100644 index 0000000..565e8df --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/BaselineHelpers-mips.h
@@ -0,0 +1,333 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_mips_BaselineHelpers_mips_h +#define jit_mips_BaselineHelpers_mips_h + +#ifdef JS_ION +#include "jit/BaselineFrame.h" +#include "jit/BaselineIC.h" +#include "jit/BaselineRegisters.h" +#include "jit/IonMacroAssembler.h" + +namespace js { +namespace jit { + +// Distance from sp to the top Value inside an IC stub (no return address on +// the stack on MIPS). +static const size_t ICStackValueOffset = 0; + +inline void +EmitRestoreTailCallReg(MacroAssembler &masm) +{ + // No-op on MIPS because ra register is always holding the return address. +} + +inline void +EmitRepushTailCallReg(MacroAssembler &masm) +{ + // No-op on MIPS because ra register is always holding the return address. +} + +inline void +EmitCallIC(CodeOffsetLabel *patchOffset, MacroAssembler &masm) +{ + // Move ICEntry offset into BaselineStubReg. + CodeOffsetLabel offset = masm.movWithPatch(ImmWord(-1), BaselineStubReg); + *patchOffset = offset; + + // Load stub pointer into BaselineStubReg. + masm.loadPtr(Address(BaselineStubReg, ICEntry::offsetOfFirstStub()), BaselineStubReg); + + // Load stubcode pointer from BaselineStubEntry. + // R2 won't be active when we call ICs, so we can use it as scratch. + masm.loadPtr(Address(BaselineStubReg, ICStub::offsetOfStubCode()), R2.scratchReg()); + + // Call the stubcode via a direct jump-and-link + masm.call(R2.scratchReg()); +} + +inline void +EmitEnterTypeMonitorIC(MacroAssembler &masm, + size_t monitorStubOffset = ICMonitoredStub::offsetOfFirstMonitorStub()) +{ + // This is expected to be called from within an IC, when BaselineStubReg + // is properly initialized to point to the stub. + masm.loadPtr(Address(BaselineStubReg, (uint32_t) monitorStubOffset), BaselineStubReg); + + // Load stubcode pointer from BaselineStubEntry. + // R2 won't be active when we call ICs, so we can use it. + masm.loadPtr(Address(BaselineStubReg, ICStub::offsetOfStubCode()), R2.scratchReg()); + + // Jump to the stubcode. + masm.branch(R2.scratchReg()); +} + +inline void +EmitReturnFromIC(MacroAssembler &masm) +{ + masm.branch(ra); +} + +inline void +EmitChangeICReturnAddress(MacroAssembler &masm, Register reg) +{ + masm.movePtr(reg, ra); +} + +inline void +EmitTailCallVM(JitCode *target, MacroAssembler &masm, uint32_t argSize) +{ + // We assume during this that R0 and R1 have been pushed, and that R2 is + // unused. + MOZ_ASSERT(R2 == ValueOperand(t7, t6)); + + // Compute frame size. + masm.movePtr(BaselineFrameReg, t6); + masm.addPtr(Imm32(BaselineFrame::FramePointerOffset), t6); + masm.subPtr(BaselineStackReg, t6); + + // Store frame size without VMFunction arguments for GC marking. + masm.ma_subu(t7, t6, Imm32(argSize)); + masm.storePtr(t7, Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize())); + + // Push frame descriptor and perform the tail call. + // BaselineTailCallReg (ra) already contains the return address (as we + // keep it there through the stub calls), but the VMWrapper code being + // called expects the return address to also be pushed on the stack. + MOZ_ASSERT(BaselineTailCallReg == ra); + masm.makeFrameDescriptor(t6, JitFrame_BaselineJS); + masm.subPtr(Imm32(sizeof(IonCommonFrameLayout)), StackPointer); + masm.storePtr(t6, Address(StackPointer, IonCommonFrameLayout::offsetOfDescriptor())); + masm.storePtr(ra, Address(StackPointer, IonCommonFrameLayout::offsetOfReturnAddress())); + + masm.branch(target); +} + +inline void +EmitCreateStubFrameDescriptor(MacroAssembler &masm, Register reg) +{ + // Compute stub frame size. We have to add two pointers: the stub reg and + // previous frame pointer pushed by EmitEnterStubFrame. + masm.movePtr(BaselineFrameReg, reg); + masm.addPtr(Imm32(sizeof(intptr_t) * 2), reg); + masm.subPtr(BaselineStackReg, reg); + + masm.makeFrameDescriptor(reg, JitFrame_BaselineStub); +} + +inline void +EmitCallVM(JitCode *target, MacroAssembler &masm) +{ + EmitCreateStubFrameDescriptor(masm, t6); + masm.push(t6); + masm.call(target); +} + +struct BaselineStubFrame { + uintptr_t savedFrame; + uintptr_t savedStub; + uintptr_t returnAddress; + uintptr_t descriptor; +}; + +static const uint32_t STUB_FRAME_SIZE = sizeof(BaselineStubFrame); +static const uint32_t STUB_FRAME_SAVED_STUB_OFFSET = offsetof(BaselineStubFrame, savedStub); + +inline void +EmitEnterStubFrame(MacroAssembler &masm, Register scratch) +{ + MOZ_ASSERT(scratch != BaselineTailCallReg); + + // Compute frame size. + masm.movePtr(BaselineFrameReg, scratch); + masm.addPtr(Imm32(BaselineFrame::FramePointerOffset), scratch); + masm.subPtr(BaselineStackReg, scratch); + + masm.storePtr(scratch, Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize())); + + // Note: when making changes here, don't forget to update + // BaselineStubFrame if needed. + + // Push frame descriptor and return address. + masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS); + masm.subPtr(Imm32(STUB_FRAME_SIZE), StackPointer); + masm.storePtr(scratch, Address(StackPointer, offsetof(BaselineStubFrame, descriptor))); + masm.storePtr(BaselineTailCallReg, Address(StackPointer, + offsetof(BaselineStubFrame, returnAddress))); + + // Save old frame pointer, stack pointer and stub reg. + masm.storePtr(BaselineStubReg, Address(StackPointer, + offsetof(BaselineStubFrame, savedStub))); + masm.storePtr(BaselineFrameReg, Address(StackPointer, + offsetof(BaselineStubFrame, savedFrame))); + masm.movePtr(BaselineStackReg, BaselineFrameReg); + + // We pushed 4 words, so the stack is still aligned to 8 bytes. + masm.checkStackAlignment(); +} + +inline void +EmitLeaveStubFrame(MacroAssembler &masm, bool calledIntoIon = false) +{ + // Ion frames do not save and restore the frame pointer. If we called + // into Ion, we have to restore the stack pointer from the frame descriptor. + // If we performed a VM call, the descriptor has been popped already so + // in that case we use the frame pointer. + if (calledIntoIon) { + masm.pop(ScratchRegister); + masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), ScratchRegister); + masm.addPtr(ScratchRegister, BaselineStackReg); + } else { + masm.movePtr(BaselineFrameReg, BaselineStackReg); + } + + masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, savedFrame)), + BaselineFrameReg); + masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, savedStub)), + BaselineStubReg); + + // Load the return address. + masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, returnAddress)), + BaselineTailCallReg); + + // Discard the frame descriptor. + masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, descriptor)), ScratchRegister); + masm.addPtr(Imm32(STUB_FRAME_SIZE), StackPointer); +} + +inline void +EmitStowICValues(MacroAssembler &masm, int values) +{ + MOZ_ASSERT(values >= 0 && values <= 2); + switch(values) { + case 1: + // Stow R0 + masm.pushValue(R0); + break; + case 2: + // Stow R0 and R1 + masm.pushValue(R0); + masm.pushValue(R1); + break; + } +} + +inline void +EmitUnstowICValues(MacroAssembler &masm, int values, bool discard = false) +{ + MOZ_ASSERT(values >= 0 && values <= 2); + switch(values) { + case 1: + // Unstow R0. + if (discard) + masm.addPtr(Imm32(sizeof(Value)), BaselineStackReg); + else + masm.popValue(R0); + break; + case 2: + // Unstow R0 and R1. + if (discard) { + masm.addPtr(Imm32(sizeof(Value) * 2), BaselineStackReg); + } else { + masm.popValue(R1); + masm.popValue(R0); + } + break; + } +} + +inline void +EmitCallTypeUpdateIC(MacroAssembler &masm, JitCode *code, uint32_t objectOffset) +{ + // R0 contains the value that needs to be typechecked. + // The object we're updating is a boxed Value on the stack, at offset + // objectOffset from $sp, excluding the return address. + + // Save the current BaselineStubReg to stack, as well as the TailCallReg, + // since on mips, the $ra is live. + masm.subPtr(Imm32(2 * sizeof(intptr_t)), StackPointer); + masm.storePtr(BaselineStubReg, Address(StackPointer, sizeof(intptr_t))); + masm.storePtr(BaselineTailCallReg, Address(StackPointer, 0)); + + // This is expected to be called from within an IC, when BaselineStubReg + // is properly initialized to point to the stub. + masm.loadPtr(Address(BaselineStubReg, ICUpdatedStub::offsetOfFirstUpdateStub()), + BaselineStubReg); + + // Load stubcode pointer from BaselineStubReg into BaselineTailCallReg. + masm.loadPtr(Address(BaselineStubReg, ICStub::offsetOfStubCode()), R2.scratchReg()); + + // Call the stubcode. + masm.call(R2.scratchReg()); + + // Restore the old stub reg and tailcall reg. + masm.loadPtr(Address(StackPointer, 0), BaselineTailCallReg); + masm.loadPtr(Address(StackPointer, sizeof(intptr_t)), BaselineStubReg); + masm.addPtr(Imm32(2 * sizeof(intptr_t)), StackPointer); + + // The update IC will store 0 or 1 in R1.scratchReg() reflecting if the + // value in R0 type-checked properly or not. + Label success; + masm.ma_b(R1.scratchReg(), Imm32(1), &success, Assembler::Equal, ShortJump); + + // If the IC failed, then call the update fallback function. + EmitEnterStubFrame(masm, R1.scratchReg()); + + masm.loadValue(Address(BaselineStackReg, STUB_FRAME_SIZE + objectOffset), R1); + + masm.pushValue(R0); + masm.pushValue(R1); + masm.push(BaselineStubReg); + + // Load previous frame pointer, push BaselineFrame *. + masm.loadPtr(Address(BaselineFrameReg, 0), R0.scratchReg()); + masm.pushBaselineFramePtr(R0.scratchReg(), R0.scratchReg()); + + EmitCallVM(code, masm); + EmitLeaveStubFrame(masm); + + // Success at end. + masm.bind(&success); +} + +template <typename AddrType> +inline void +EmitPreBarrier(MacroAssembler &masm, const AddrType &addr, MIRType type) +{ + // On MIPS, $ra is clobbered by patchableCallPreBarrier. Save it first. + masm.push(ra); + masm.patchableCallPreBarrier(addr, type); + masm.pop(ra); +} + +inline void +EmitStubGuardFailure(MacroAssembler &masm) +{ + // NOTE: This routine assumes that the stub guard code left the stack in + // the same state it was in when it was entered. + + // BaselineStubEntry points to the current stub. + + // Load next stub into BaselineStubReg + masm.loadPtr(Address(BaselineStubReg, ICStub::offsetOfNext()), BaselineStubReg); + + // Load stubcode pointer from BaselineStubEntry into scratch register. + masm.loadPtr(Address(BaselineStubReg, ICStub::offsetOfStubCode()), R2.scratchReg()); + + // Return address is already loaded, just jump to the next stubcode. + MOZ_ASSERT(BaselineTailCallReg == ra); + masm.branch(R2.scratchReg()); +} + + +} // namespace jit +} // namespace js + +#endif // JS_ION + +#endif /* jit_mips_BaselineHelpers_mips_h */ +
diff --git a/src/third_party/mozjs/js/src/jit/mips/BaselineIC-mips.cpp b/src/third_party/mozjs/js/src/jit/mips/BaselineIC-mips.cpp new file mode 100644 index 0000000..777f5a6 --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/BaselineIC-mips.cpp
@@ -0,0 +1,223 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jsiter.h" + +#include "jit/BaselineCompiler.h" +#include "jit/BaselineHelpers.h" +#include "jit/BaselineIC.h" +#include "jit/BaselineJIT.h" +#include "jit/IonLinker.h" + +#include "jsboolinlines.h" + +using namespace js; +using namespace js::jit; + +namespace js { +namespace jit { + +// ICCompare_Int32 + +bool +ICCompare_Int32::Compiler::generateStubCode(MacroAssembler &masm) +{ + // Guard that R0 is an integer and R1 is an integer. + Label failure; + Label conditionTrue; + masm.branchTestInt32(Assembler::NotEqual, R0, &failure); + masm.branchTestInt32(Assembler::NotEqual, R1, &failure); + + // Compare payload regs of R0 and R1. + Assembler::Condition cond = JSOpToCondition(op, /* signed = */true); + masm.ma_cmp_set(R0.payloadReg(), R0.payloadReg(), R1.payloadReg(), cond); + + masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.payloadReg(), R0); + EmitReturnFromIC(masm); + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + + return true; +} + +bool +ICCompare_Double::Compiler::generateStubCode(MacroAssembler &masm) +{ + Label failure, isNaN; + masm.ensureDouble(R0, FloatReg0, &failure); + masm.ensureDouble(R1, FloatReg1, &failure); + + Register dest = R0.scratchReg(); + + Assembler::DoubleCondition doubleCond = JSOpToDoubleCondition(op); + + masm.ma_cmp_set_double(dest, FloatReg0, FloatReg1, doubleCond); + + masm.tagValue(JSVAL_TYPE_BOOLEAN, dest, R0); + EmitReturnFromIC(masm); + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +// ICBinaryArith_Int32 + +bool +ICBinaryArith_Int32::Compiler::generateStubCode(MacroAssembler &masm) +{ + // Guard that R0 is an integer and R1 is an integer. + Label failure; + masm.branchTestInt32(Assembler::NotEqual, R0, &failure); + masm.branchTestInt32(Assembler::NotEqual, R1, &failure); + + // Add R0 and R1. Don't need to explicitly unbox, just use R2's payloadReg. + Register scratchReg = R2.payloadReg(); + + // DIV and MOD need an extra non-volatile ValueOperand to hold R0. + GeneralRegisterSet savedRegs = availableGeneralRegs(2); + savedRegs = GeneralRegisterSet::Intersect(GeneralRegisterSet::NonVolatile(), savedRegs); + ValueOperand savedValue = savedRegs.takeAnyValue(); + + Label goodMul, divTest1, divTest2; + switch(op_) { + case JSOP_ADD: + // We know R0.typeReg() already contains the integer tag. No boxing + // required. + masm.ma_addTestOverflow(R0.payloadReg(), R0.payloadReg(), R1.payloadReg(), &failure); + break; + case JSOP_SUB: + masm.ma_subTestOverflow(R0.payloadReg(), R0.payloadReg(), R1.payloadReg(), &failure); + break; + case JSOP_MUL: { + masm.ma_mul_branch_overflow(scratchReg, R0.payloadReg(), R1.payloadReg(), &failure); + + masm.ma_b(scratchReg, Imm32(0), &goodMul, Assembler::NotEqual, ShortJump); + + // Result is -0 if operands have different signs. + masm.as_xor(t8, R0.payloadReg(), R1.payloadReg()); + masm.ma_b(t8, Imm32(0), &failure, Assembler::LessThan, ShortJump); + + masm.bind(&goodMul); + masm.move32(scratchReg, R0.payloadReg()); + break; + } + case JSOP_DIV: + case JSOP_MOD: { + // Check for INT_MIN / -1, it results in a double. + masm.ma_b(R0.payloadReg(), Imm32(INT_MIN), &divTest1, Assembler::NotEqual, ShortJump); + masm.ma_b(R1.payloadReg(), Imm32(-1), &failure, Assembler::Equal, ShortJump); + masm.bind(&divTest1); + + // Check for division by zero + masm.ma_b(R1.payloadReg(), Imm32(0), &failure, Assembler::Equal, ShortJump); + + // Check for 0 / X with X < 0 (results in -0). + masm.ma_b(R0.payloadReg(), Imm32(0), &divTest2, Assembler::NotEqual, ShortJump); + masm.ma_b(R1.payloadReg(), Imm32(0), &failure, Assembler::LessThan, ShortJump); + masm.bind(&divTest2); + + masm.as_div(R0.payloadReg(), R1.payloadReg()); + + if (op_ == JSOP_DIV) { + // Result is a double if the remainder != 0. + masm.as_mfhi(scratchReg); + masm.ma_b(scratchReg, Imm32(0), &failure, Assembler::NotEqual, ShortJump); + masm.as_mflo(scratchReg); + masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0); + } else { + Label done; + // If X % Y == 0 and X < 0, the result is -0. + masm.as_mfhi(scratchReg); + masm.ma_b(scratchReg, Imm32(0), &done, Assembler::NotEqual, ShortJump); + masm.ma_b(R0.payloadReg(), Imm32(0), &failure, Assembler::LessThan, ShortJump); + masm.bind(&done); + masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0); + } + break; + } + case JSOP_BITOR: + masm.ma_or(R0.payloadReg() , R0.payloadReg(), R1.payloadReg()); + break; + case JSOP_BITXOR: + masm.ma_xor(R0.payloadReg() , R0.payloadReg(), R1.payloadReg()); + break; + case JSOP_BITAND: + masm.ma_and(R0.payloadReg() , R0.payloadReg(), R1.payloadReg()); + break; + case JSOP_LSH: + // MIPS will only use 5 lowest bits in R1 as shift offset. + masm.ma_sll(R0.payloadReg(), R0.payloadReg(), R1.payloadReg()); + break; + case JSOP_RSH: + masm.ma_sra(R0.payloadReg(), R0.payloadReg(), R1.payloadReg()); + break; + case JSOP_URSH: + masm.ma_srl(scratchReg, R0.payloadReg(), R1.payloadReg()); + if (allowDouble_) { + Label toUint; + masm.ma_b(scratchReg, Imm32(0), &toUint, Assembler::LessThan, ShortJump); + + // Move result and box for return. + masm.move32(scratchReg, R0.payloadReg()); + EmitReturnFromIC(masm); + + masm.bind(&toUint); + masm.convertUInt32ToDouble(scratchReg, FloatReg1); + masm.boxDouble(FloatReg1, R0); + } else { + masm.ma_b(scratchReg, Imm32(0), &failure, Assembler::LessThan, ShortJump); + // Move result for return. + masm.move32(scratchReg, R0.payloadReg()); + } + break; + default: + MOZ_ASSUME_UNREACHABLE("Unhandled op for BinaryArith_Int32."); + } + + EmitReturnFromIC(masm); + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + + return true; +} + +bool +ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler &masm) +{ + Label failure; + masm.branchTestInt32(Assembler::NotEqual, R0, &failure); + + switch (op) { + case JSOP_BITNOT: + masm.not32(R0.payloadReg()); + break; + case JSOP_NEG: + // Guard against 0 and MIN_INT, both result in a double. + masm.branchTest32(Assembler::Zero, R0.payloadReg(), Imm32(INT32_MAX), &failure); + + masm.neg32(R0.payloadReg()); + break; + default: + MOZ_ASSUME_UNREACHABLE("Unexpected op"); + return false; + } + + EmitReturnFromIC(masm); + + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + + +} // namespace jit +} // namespace js
diff --git a/src/third_party/mozjs/js/src/jit/mips/BaselineRegisters-mips.h b/src/third_party/mozjs/js/src/jit/mips/BaselineRegisters-mips.h new file mode 100644 index 0000000..6ecdfea --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/BaselineRegisters-mips.h
@@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_mips_BaselineRegisters_mips_h +#define jit_mips_BaselineRegisters_mips_h + +#ifdef JS_ION + +#include "jit/IonMacroAssembler.h" + +namespace js { +namespace jit { + +static MOZ_CONSTEXPR_VAR Register BaselineFrameReg = s5; +static MOZ_CONSTEXPR_VAR Register BaselineStackReg = sp; + +static MOZ_CONSTEXPR_VAR ValueOperand R0(v1, v0); +static MOZ_CONSTEXPR_VAR ValueOperand R1(s7, s6); +static MOZ_CONSTEXPR_VAR ValueOperand R2(t7, t6); + +// BaselineTailCallReg and BaselineStubReg +// These use registers that are not preserved across calls. +static MOZ_CONSTEXPR_VAR Register BaselineTailCallReg = ra; +static MOZ_CONSTEXPR_VAR Register BaselineStubReg = t5; + +static MOZ_CONSTEXPR_VAR Register ExtractTemp0 = InvalidReg; +static MOZ_CONSTEXPR_VAR Register ExtractTemp1 = InvalidReg; + +// Register used internally by MacroAssemblerMIPS. +static MOZ_CONSTEXPR_VAR Register BaselineSecondScratchReg = SecondScratchReg; + +// Note that BaselineTailCallReg is actually just the link register. +// In MIPS code emission, we do not clobber BaselineTailCallReg since we keep +// the return address for calls there. + +// FloatReg0 must be equal to ReturnFloatReg. +static MOZ_CONSTEXPR_VAR FloatRegister FloatReg0 = f0; +static MOZ_CONSTEXPR_VAR FloatRegister FloatReg1 = f2; + +} // namespace jit +} // namespace js + +#endif // JS_ION + +#endif /* jit_mips_BaselineRegisters_mips_h */ +
diff --git a/src/third_party/mozjs/js/src/jit/mips/CodeGenerator-mips.cpp b/src/third_party/mozjs/js/src/jit/mips/CodeGenerator-mips.cpp new file mode 100644 index 0000000..fd9be18 --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/CodeGenerator-mips.cpp
@@ -0,0 +1,2343 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/mips/CodeGenerator-mips.h" + +#include "mozilla/MathAlgorithms.h" + +#include "jscntxt.h" +#include "jscompartment.h" +#include "jsnum.h" + +#include "jit/CodeGenerator.h" +#include "jit/IonFrames.h" +#include "jit/JitCompartment.h" +#include "jit/MIR.h" +#include "jit/MIRGraph.h" +#include "vm/Shape.h" + +#include "jsscriptinlines.h" + +#include "jit/shared/CodeGenerator-shared-inl.h" + +using namespace js; +using namespace js::jit; + +using mozilla::FloorLog2; +using mozilla::NegativeInfinity; +using JS::GenericNaN; + +// shared +CodeGeneratorMIPS::CodeGeneratorMIPS(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm) + : CodeGeneratorShared(gen, graph, masm) +{ +} + +bool +CodeGeneratorMIPS::generatePrologue() +{ + if (gen->compilingAsmJS()) { + masm.Push(ra); + // Note that this automatically sets MacroAssembler::framePushed(). + masm.reserveStack(frameDepth_); + } else { + // Note that this automatically sets MacroAssembler::framePushed(). + masm.reserveStack(frameSize()); + masm.checkStackAlignment(); + } + + return true; +} + +bool +CodeGeneratorMIPS::generateEpilogue() +{ + masm.bind(&returnLabel_); +#if JS_TRACE_LOGGING + masm.tracelogStop(); +#endif + if (gen->compilingAsmJS()) { + // Pop the stack we allocated at the start of the function. + masm.freeStack(frameDepth_); + masm.Pop(ra); + masm.abiret(); + MOZ_ASSERT(masm.framePushed() == 0); + } else { + // Pop the stack we allocated at the start of the function. + masm.freeStack(frameSize()); + MOZ_ASSERT(masm.framePushed() == 0); + masm.ret(); + } + return true; +} + +void +CodeGeneratorMIPS::branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs, + MBasicBlock *mir, Assembler::DoubleCondition cond) +{ + Label *label = mir->lir()->label(); + if (Label *oolEntry = labelForBackedgeWithImplicitCheck(mir)) { + // Note: the backedge is initially a jump to the next instruction. + // It will be patched to the target block's label during link(). + RepatchLabel rejoin; + + CodeOffsetJump backedge; + Label skip; + if (fmt == Assembler::DoubleFloat) + masm.ma_bc1d(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump); + else + masm.ma_bc1s(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump); + + backedge = masm.jumpWithPatch(&rejoin); + masm.bind(&rejoin); + masm.bind(&skip); + + if (!patchableBackedges_.append(PatchableBackedgeInfo(backedge, label, oolEntry))) + MOZ_CRASH(); + } else { + if (fmt == Assembler::DoubleFloat) + masm.branchDouble(cond, lhs, rhs, mir->lir()->label()); + else + masm.branchFloat(cond, lhs, rhs, mir->lir()->label()); + } +} + +bool +OutOfLineBailout::accept(CodeGeneratorMIPS *codegen) +{ + return codegen->visitOutOfLineBailout(this); +} + +bool +CodeGeneratorMIPS::visitTestIAndBranch(LTestIAndBranch *test) +{ + const LAllocation *opd = test->getOperand(0); + MBasicBlock *ifTrue = test->ifTrue(); + MBasicBlock *ifFalse = test->ifFalse(); + + emitBranch(ToRegister(opd), Imm32(0), Assembler::NonZero, ifTrue, ifFalse); + return true; +} + +bool +CodeGeneratorMIPS::visitCompare(LCompare *comp) +{ + Assembler::Condition cond = JSOpToCondition(comp->mir()->compareType(), comp->jsop()); + const LAllocation *left = comp->getOperand(0); + const LAllocation *right = comp->getOperand(1); + const LDefinition *def = comp->getDef(0); + + if (right->isConstant()) + masm.cmp32Set(cond, ToRegister(left), Imm32(ToInt32(right)), ToRegister(def)); + else if (right->isGeneralReg()) + masm.cmp32Set(cond, ToRegister(left), ToRegister(right), ToRegister(def)); + else + masm.cmp32Set(cond, ToRegister(left), ToAddress(right), ToRegister(def)); + + return true; +} + +bool +CodeGeneratorMIPS::visitCompareAndBranch(LCompareAndBranch *comp) +{ + Assembler::Condition cond = JSOpToCondition(comp->cmpMir()->compareType(), comp->jsop()); + if (comp->right()->isConstant()) { + emitBranch(ToRegister(comp->left()), Imm32(ToInt32(comp->right())), cond, + comp->ifTrue(), comp->ifFalse()); + } else if (comp->right()->isGeneralReg()) { + emitBranch(ToRegister(comp->left()), ToRegister(comp->right()), cond, + comp->ifTrue(), comp->ifFalse()); + } else { + emitBranch(ToRegister(comp->left()), ToAddress(comp->right()), cond, + comp->ifTrue(), comp->ifFalse()); + } + + return true; +} + +bool +CodeGeneratorMIPS::generateOutOfLineCode() +{ + if (!CodeGeneratorShared::generateOutOfLineCode()) + return false; + + if (deoptLabel_.used()) { + // All non-table-based bailouts will go here. + masm.bind(&deoptLabel_); + + // Push the frame size, so the handler can recover the IonScript. + // Frame size is stored in 'ra' and pushed by GenerateBailoutThunk + // We have to use 'ra' because generateBailoutTable will implicitly do + // the same. + masm.move32(Imm32(frameSize()), ra); + + JitCode *handler = gen->jitRuntime()->getGenericBailoutHandler(); + + masm.branch(handler); + } + + return true; +} + +bool +CodeGeneratorMIPS::bailoutFrom(Label *label, LSnapshot *snapshot) +{ + if (masm.bailed()) + return false; + MOZ_ASSERT(label->used()); + MOZ_ASSERT(!label->bound()); + + CompileInfo &info = snapshot->mir()->block()->info(); + switch (info.executionMode()) { + case ParallelExecution: { + // in parallel mode, make no attempt to recover, just signal an error. + OutOfLineAbortPar *ool = oolAbortPar(ParallelBailoutUnsupported, + snapshot->mir()->block(), + snapshot->mir()->pc()); + masm.retarget(label, ool->entry()); + return true; + } + case SequentialExecution: + break; + default: + MOZ_ASSUME_UNREACHABLE("No such execution mode"); + } + + if (!encode(snapshot)) + return false; + + // Though the assembler doesn't track all frame pushes, at least make sure + // the known value makes sense. We can't use bailout tables if the stack + // isn't properly aligned to the static frame size. + MOZ_ASSERT_IF(frameClass_ != FrameSizeClass::None(), + frameClass_.frameSize() == masm.framePushed()); + + // We don't use table bailouts because retargeting is easier this way. + OutOfLineBailout *ool = new(alloc()) OutOfLineBailout(snapshot, masm.framePushed()); + if (!addOutOfLineCode(ool)) { + return false; + } + + masm.retarget(label, ool->entry()); + + return true; +} + +bool +CodeGeneratorMIPS::bailout(LSnapshot *snapshot) +{ + Label label; + masm.jump(&label); + return bailoutFrom(&label, snapshot); +} + +bool +CodeGeneratorMIPS::visitOutOfLineBailout(OutOfLineBailout *ool) +{ + // Push snapshotOffset and make sure stack is aligned. + masm.subPtr(Imm32(2 * sizeof(void *)), StackPointer); + masm.storePtr(ImmWord(ool->snapshot()->snapshotOffset()), Address(StackPointer, 0)); + + masm.jump(&deoptLabel_); + return true; +} + +bool +CodeGeneratorMIPS::visitMinMaxD(LMinMaxD *ins) +{ + FloatRegister first = ToFloatRegister(ins->first()); + FloatRegister second = ToFloatRegister(ins->second()); + FloatRegister output = ToFloatRegister(ins->output()); + + MOZ_ASSERT(first == output); + + Assembler::DoubleCondition cond = ins->mir()->isMax() + ? Assembler::DoubleLessThanOrEqual + : Assembler::DoubleGreaterThanOrEqual; + Label nan, equal, returnSecond, done; + + // First or second is NaN, result is NaN. + masm.ma_bc1d(first, second, &nan, Assembler::DoubleUnordered, ShortJump); + // Make sure we handle -0 and 0 right. + masm.ma_bc1d(first, second, &equal, Assembler::DoubleEqual, ShortJump); + masm.ma_bc1d(first, second, &returnSecond, cond, ShortJump); + masm.ma_b(&done, ShortJump); + + // Check for zero. + masm.bind(&equal); + masm.loadConstantDouble(0.0, ScratchFloatReg); + // First wasn't 0 or -0, so just return it. + masm.ma_bc1d(first, ScratchFloatReg, &done, Assembler::DoubleNotEqualOrUnordered, ShortJump); + + // So now both operands are either -0 or 0. + if (ins->mir()->isMax()) { + // -0 + -0 = -0 and -0 + 0 = 0. + masm.addDouble(second, first); + } else { + masm.negateDouble(first); + masm.subDouble(second, first); + masm.negateDouble(first); + } + masm.ma_b(&done, ShortJump); + + masm.bind(&nan); + masm.loadConstantDouble(GenericNaN(), output); + masm.ma_b(&done, ShortJump); + + masm.bind(&returnSecond); + masm.moveDouble(second, output); + + masm.bind(&done); + return true; +} + +bool +CodeGeneratorMIPS::visitAbsD(LAbsD *ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + MOZ_ASSERT(input == ToFloatRegister(ins->output())); + masm.as_absd(input, input); + return true; +} + +bool +CodeGeneratorMIPS::visitAbsF(LAbsF *ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + MOZ_ASSERT(input == ToFloatRegister(ins->output())); + masm.as_abss(input, input); + return true; +} + +bool +CodeGeneratorMIPS::visitSqrtD(LSqrtD *ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + FloatRegister output = ToFloatRegister(ins->output()); + masm.as_sqrtd(output, input); + return true; +} + +bool +CodeGeneratorMIPS::visitSqrtF(LSqrtF *ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + FloatRegister output = ToFloatRegister(ins->output()); + masm.as_sqrts(output, input); + return true; +} + +bool +CodeGeneratorMIPS::visitAddI(LAddI *ins) +{ + const LAllocation *lhs = ins->getOperand(0); + const LAllocation *rhs = ins->getOperand(1); + const LDefinition *dest = ins->getDef(0); + + MOZ_ASSERT(rhs->isConstant() || rhs->isGeneralReg()); + + // If there is no snapshot, we don't need to check for overflow + if (!ins->snapshot()) { + if (rhs->isConstant()) + masm.ma_addu(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); + else + masm.as_addu(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); + return true; + } + + Label overflow; + if (rhs->isConstant()) + masm.ma_addTestOverflow(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)), &overflow); + else + masm.ma_addTestOverflow(ToRegister(dest), ToRegister(lhs), ToRegister(rhs), &overflow); + + if (!bailoutFrom(&overflow, ins->snapshot())) + return false; + + return true; +} + +bool +CodeGeneratorMIPS::visitSubI(LSubI *ins) +{ + const LAllocation *lhs = ins->getOperand(0); + const LAllocation *rhs = ins->getOperand(1); + const LDefinition *dest = ins->getDef(0); + + MOZ_ASSERT(rhs->isConstant() || rhs->isGeneralReg()); + + // If there is no snapshot, we don't need to check for overflow + if (!ins->snapshot()) { + if (rhs->isConstant()) + masm.ma_subu(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); + else + masm.as_subu(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); + return true; + } + + Label overflow; + if (rhs->isConstant()) + masm.ma_subTestOverflow(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)), &overflow); + else + masm.ma_subTestOverflow(ToRegister(dest), ToRegister(lhs), ToRegister(rhs), &overflow); + + if (!bailoutFrom(&overflow, ins->snapshot())) + return false; + + return true; +} + +bool +CodeGeneratorMIPS::visitMulI(LMulI *ins) +{ + const LAllocation *lhs = ins->lhs(); + const LAllocation *rhs = ins->rhs(); + Register dest = ToRegister(ins->output()); + MMul *mul = ins->mir(); + + MOZ_ASSERT_IF(mul->mode() == MMul::Integer, !mul->canBeNegativeZero() && !mul->canOverflow()); + + if (rhs->isConstant()) { + int32_t constant = ToInt32(rhs); + Register src = ToRegister(lhs); + + // Bailout on -0.0 + if (mul->canBeNegativeZero() && constant <= 0) { + Assembler::Condition cond = (constant == 0) ? Assembler::LessThan : Assembler::Equal; + if (!bailoutCmp32(cond, src, Imm32(0), ins->snapshot())) + return false; + } + + switch (constant) { + case -1: + if (mul->canOverflow()) { + if (!bailoutCmp32(Assembler::Equal, src, Imm32(INT32_MIN), ins->snapshot())) + return false; + } + masm.ma_negu(dest, src); + break; + case 0: + masm.move32(Imm32(0), dest); + break; + case 1: + masm.move32(src, dest); + break; + case 2: + if (mul->canOverflow()) { + Label mulTwoOverflow; + masm.ma_addTestOverflow(dest, src, src, &mulTwoOverflow); + + if (!bailoutFrom(&mulTwoOverflow, ins->snapshot())) + return false; + } else { + masm.as_addu(dest, src, src); + } + break; + default: + uint32_t shift = FloorLog2(constant); + + if (!mul->canOverflow() && (constant > 0)) { + // If it cannot overflow, we can do lots of optimizations. + uint32_t rest = constant - (1 << shift); + + // See if the constant has one bit set, meaning it can be + // encoded as a bitshift. + if ((1 << shift) == constant) { + masm.ma_sll(dest, src, Imm32(shift)); + return true; + } + + // If the constant cannot be encoded as (1<<C1), see if it can + // be encoded as (1<<C1) | (1<<C2), which can be computed + // using an add and a shift. + uint32_t shift_rest = FloorLog2(rest); + if (src != dest && (1u << shift_rest) == rest) { + masm.ma_sll(dest, src, Imm32(shift - shift_rest)); + masm.add32(src, dest); + if (shift_rest != 0) + masm.ma_sll(dest, dest, Imm32(shift_rest)); + return true; + } + } + + if (mul->canOverflow() && (constant > 0) && (src != dest)) { + // To stay on the safe side, only optimize things that are a + // power of 2. + + if ((1 << shift) == constant) { + // dest = lhs * pow(2, shift) + masm.ma_sll(dest, src, Imm32(shift)); + // At runtime, check (lhs == dest >> shift), if this does + // not hold, some bits were lost due to overflow, and the + // computation should be resumed as a double. + masm.ma_sra(ScratchRegister, dest, Imm32(shift)); + if (!bailoutCmp32(Assembler::NotEqual, src, ScratchRegister, ins->snapshot())) + return false; + return true; + } + } + + if (mul->canOverflow()) { + Label mulConstOverflow; + masm.ma_mul_branch_overflow(dest, ToRegister(lhs), Imm32(ToInt32(rhs)), + &mulConstOverflow); + + if (!bailoutFrom(&mulConstOverflow, ins->snapshot())) + return false; + } else { + masm.ma_mult(src, Imm32(ToInt32(rhs))); + masm.as_mflo(dest); + } + break; + } + } else { + Label multRegOverflow; + + if (mul->canOverflow()) { + masm.ma_mul_branch_overflow(dest, ToRegister(lhs), ToRegister(rhs), &multRegOverflow); + if (!bailoutFrom(&multRegOverflow, ins->snapshot())) + return false; + } else { + masm.as_mult(ToRegister(lhs), ToRegister(rhs)); + masm.as_mflo(dest); + } + + if (mul->canBeNegativeZero()) { + Label done; + masm.ma_b(dest, dest, &done, Assembler::NonZero, ShortJump); + + // Result is -0 if lhs or rhs is negative. + // In that case result must be double value so bailout + Register scratch = SecondScratchReg; + masm.ma_or(scratch, ToRegister(lhs), ToRegister(rhs)); + if (!bailoutCmp32(Assembler::Signed, scratch, scratch, ins->snapshot())) + return false; + + masm.bind(&done); + } + } + + return true; +} + +bool +CodeGeneratorMIPS::visitDivI(LDivI *ins) +{ + // Extract the registers from this instruction + Register lhs = ToRegister(ins->lhs()); + Register rhs = ToRegister(ins->rhs()); + Register dest = ToRegister(ins->output()); + Register temp = ToRegister(ins->getTemp(0)); + MDiv *mir = ins->mir(); + + Label done; + + // Handle divide by zero. + if (mir->canBeDivideByZero()) { + if (mir->canTruncateInfinities()) { + // Truncated division by zero is zero (Infinity|0 == 0) + Label notzero; + masm.ma_b(rhs, rhs, ¬zero, Assembler::NonZero, ShortJump); + masm.move32(Imm32(0), dest); + masm.ma_b(&done, ShortJump); + masm.bind(¬zero); + } else { + MOZ_ASSERT(mir->fallible()); + if (!bailoutCmp32(Assembler::Zero, rhs, rhs, ins->snapshot())) + return false; + } + } + + // Handle an integer overflow exception from -2147483648 / -1. + if (mir->canBeNegativeOverflow()) { + Label notMinInt; + masm.move32(Imm32(INT32_MIN), temp); + masm.ma_b(lhs, temp, ¬MinInt, Assembler::NotEqual, ShortJump); + + masm.move32(Imm32(-1), temp); + if (mir->canTruncateOverflow()) { + // (-INT32_MIN)|0 == INT32_MIN + Label skip; + masm.ma_b(rhs, temp, &skip, Assembler::NotEqual, ShortJump); + masm.move32(Imm32(INT32_MIN), dest); + masm.ma_b(&done, ShortJump); + masm.bind(&skip); + } else { + MOZ_ASSERT(mir->fallible()); + if (!bailoutCmp32(Assembler::Equal, rhs, temp, ins->snapshot())) + return false; + } + masm.bind(¬MinInt); + } + + // Handle negative 0. (0/-Y) + if (!mir->canTruncateNegativeZero() && mir->canBeNegativeZero()) { + Label nonzero; + masm.ma_b(lhs, lhs, &nonzero, Assembler::NonZero, ShortJump); + if (!bailoutCmp32(Assembler::LessThan, rhs, Imm32(0), ins->snapshot())) + return false; + masm.bind(&nonzero); + } + // Note: above safety checks could not be verified as Ion seems to be + // smarter and requires double arithmetic in such cases. + + // All regular. Lets call div. + if (mir->canTruncateRemainder()) { + masm.as_div(lhs, rhs); + masm.as_mflo(dest); + } else { + MOZ_ASSERT(mir->fallible()); + + Label remainderNonZero; + masm.ma_div_branch_overflow(dest, lhs, rhs, &remainderNonZero); + if (!bailoutFrom(&remainderNonZero, ins->snapshot())) + return false; + } + + masm.bind(&done); + + return true; +} + +bool +CodeGeneratorMIPS::visitDivPowTwoI(LDivPowTwoI *ins) +{ + Register lhs = ToRegister(ins->numerator()); + Register dest = ToRegister(ins->output()); + Register tmp = ToRegister(ins->getTemp(0)); + int32_t shift = ins->shift(); + + if (shift != 0) { + MDiv *mir = ins->mir(); + if (!mir->isTruncated()) { + // If the remainder is going to be != 0, bailout since this must + // be a double. + masm.ma_sll(tmp, lhs, Imm32(32 - shift)); + if (!bailoutCmp32(Assembler::NonZero, tmp, tmp, ins->snapshot())) + return false; + } + + if (!mir->canBeNegativeDividend()) { + // Numerator is unsigned, so needs no adjusting. Do the shift. + masm.ma_sra(dest, lhs, Imm32(shift)); + return true; + } + + // Adjust the value so that shifting produces a correctly rounded result + // when the numerator is negative. See 10-1 "Signed Division by a Known + // Power of 2" in Henry S. Warren, Jr.'s Hacker's Delight. + if (shift > 1) { + masm.ma_sra(tmp, lhs, Imm32(31)); + masm.ma_srl(tmp, tmp, Imm32(32 - shift)); + masm.add32(lhs, tmp); + } else { + masm.ma_srl(tmp, lhs, Imm32(32 - shift)); + masm.add32(lhs, tmp); + } + + // Do the shift. + masm.ma_sra(dest, tmp, Imm32(shift)); + } else { + masm.move32(lhs, dest); + } + + return true; + +} + +bool +CodeGeneratorMIPS::visitModI(LModI *ins) +{ + // Extract the registers from this instruction + Register lhs = ToRegister(ins->lhs()); + Register rhs = ToRegister(ins->rhs()); + Register dest = ToRegister(ins->output()); + Register callTemp = ToRegister(ins->callTemp()); + MMod *mir = ins->mir(); + Label done, prevent; + + masm.move32(lhs, callTemp); + + // Prevent INT_MIN % -1; + // The integer division will give INT_MIN, but we want -(double)INT_MIN. + if (mir->canBeNegativeDividend()) { + masm.ma_b(lhs, Imm32(INT_MIN), &prevent, Assembler::NotEqual, ShortJump); + if (mir->isTruncated()) { + // (INT_MIN % -1)|0 == 0 + Label skip; + masm.ma_b(rhs, Imm32(-1), &skip, Assembler::NotEqual, ShortJump); + masm.move32(Imm32(0), dest); + masm.ma_b(&done, ShortJump); + masm.bind(&skip); + } else { + MOZ_ASSERT(mir->fallible()); + if (!bailoutCmp32(Assembler::Equal, rhs, Imm32(-1), ins->snapshot())) + return false; + } + masm.bind(&prevent); + } + + // 0/X (with X < 0) is bad because both of these values *should* be + // doubles, and the result should be -0.0, which cannot be represented in + // integers. X/0 is bad because it will give garbage (or abort), when it + // should give either \infty, -\infty or NAN. + + // Prevent 0 / X (with X < 0) and X / 0 + // testing X / Y. Compare Y with 0. + // There are three cases: (Y < 0), (Y == 0) and (Y > 0) + // If (Y < 0), then we compare X with 0, and bail if X == 0 + // If (Y == 0), then we simply want to bail. + // if (Y > 0), we don't bail. + + if (mir->canBeDivideByZero()) { + if (mir->isTruncated()) { + Label skip; + masm.ma_b(rhs, Imm32(0), &skip, Assembler::NotEqual, ShortJump); + masm.move32(Imm32(0), dest); + masm.ma_b(&done, ShortJump); + masm.bind(&skip); + } else { + MOZ_ASSERT(mir->fallible()); + if (!bailoutCmp32(Assembler::Equal, rhs, Imm32(0), ins->snapshot())) + return false; + } + } + + if (mir->canBeNegativeDividend()) { + Label notNegative; + masm.ma_b(rhs, Imm32(0), ¬Negative, Assembler::GreaterThan, ShortJump); + if (mir->isTruncated()) { + // NaN|0 == 0 and (0 % -X)|0 == 0 + Label skip; + masm.ma_b(lhs, Imm32(0), &skip, Assembler::NotEqual, ShortJump); + masm.move32(Imm32(0), dest); + masm.ma_b(&done, ShortJump); + masm.bind(&skip); + } else { + MOZ_ASSERT(mir->fallible()); + if (!bailoutCmp32(Assembler::Equal, lhs, Imm32(0), ins->snapshot())) + return false; + } + masm.bind(¬Negative); + } + + masm.as_div(lhs, rhs); + masm.as_mfhi(dest); + + // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0 + if (mir->canBeNegativeDividend()) { + if (mir->isTruncated()) { + // -0.0|0 == 0 + } else { + MOZ_ASSERT(mir->fallible()); + // See if X < 0 + masm.ma_b(dest, Imm32(0), &done, Assembler::NotEqual, ShortJump); + if (!bailoutCmp32(Assembler::Signed, callTemp, Imm32(0), ins->snapshot())) + return false; + } + } + masm.bind(&done); + return true; +} + +bool +CodeGeneratorMIPS::visitModPowTwoI(LModPowTwoI *ins) +{ + Register in = ToRegister(ins->getOperand(0)); + Register out = ToRegister(ins->getDef(0)); + MMod *mir = ins->mir(); + Label negative, done; + + masm.move32(in, out); + masm.ma_b(in, in, &done, Assembler::Zero, ShortJump); + // Switch based on sign of the lhs. + // Positive numbers are just a bitmask + masm.ma_b(in, in, &negative, Assembler::Signed, ShortJump); + { + masm.and32(Imm32((1 << ins->shift()) - 1), out); + masm.ma_b(&done, ShortJump); + } + + // Negative numbers need a negate, bitmask, negate + { + masm.bind(&negative); + masm.neg32(out); + masm.and32(Imm32((1 << ins->shift()) - 1), out); + masm.neg32(out); + } + if (mir->canBeNegativeDividend()) { + if (!mir->isTruncated()) { + MOZ_ASSERT(mir->fallible()); + if (!bailoutCmp32(Assembler::Equal, out, zero, ins->snapshot())) + return false; + } else { + // -0|0 == 0 + } + } + masm.bind(&done); + return true; +} + +bool +CodeGeneratorMIPS::visitModMaskI(LModMaskI *ins) +{ + Register src = ToRegister(ins->getOperand(0)); + Register dest = ToRegister(ins->getDef(0)); + Register tmp = ToRegister(ins->getTemp(0)); + MMod *mir = ins->mir(); + + if (!mir->isTruncated() && mir->canBeNegativeDividend()) { + MOZ_ASSERT(mir->fallible()); + + Label bail; + masm.ma_mod_mask(src, dest, tmp, ins->shift(), &bail); + if (!bailoutFrom(&bail, ins->snapshot())) + return false; + } else { + masm.ma_mod_mask(src, dest, tmp, ins->shift(), nullptr); + } + return true; +} +bool +CodeGeneratorMIPS::visitBitNotI(LBitNotI *ins) +{ + const LAllocation *input = ins->getOperand(0); + const LDefinition *dest = ins->getDef(0); + MOZ_ASSERT(!input->isConstant()); + + masm.ma_not(ToRegister(dest), ToRegister(input)); + return true; +} + +bool +CodeGeneratorMIPS::visitBitOpI(LBitOpI *ins) +{ + const LAllocation *lhs = ins->getOperand(0); + const LAllocation *rhs = ins->getOperand(1); + const LDefinition *dest = ins->getDef(0); + // all of these bitops should be either imm32's, or integer registers. + switch (ins->bitop()) { + case JSOP_BITOR: + if (rhs->isConstant()) + masm.ma_or(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); + else + masm.ma_or(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); + break; + case JSOP_BITXOR: + if (rhs->isConstant()) + masm.ma_xor(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); + else + masm.ma_xor(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); + break; + case JSOP_BITAND: + if (rhs->isConstant()) + masm.ma_and(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); + else + masm.ma_and(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); + break; + default: + MOZ_ASSUME_UNREACHABLE("unexpected binary opcode"); + } + + return true; +} + +bool +CodeGeneratorMIPS::visitShiftI(LShiftI *ins) +{ + Register lhs = ToRegister(ins->lhs()); + const LAllocation *rhs = ins->rhs(); + Register dest = ToRegister(ins->output()); + + if (rhs->isConstant()) { + int32_t shift = ToInt32(rhs) & 0x1F; + switch (ins->bitop()) { + case JSOP_LSH: + if (shift) + masm.ma_sll(dest, lhs, Imm32(shift)); + else + masm.move32(lhs, dest); + break; + case JSOP_RSH: + if (shift) + masm.ma_sra(dest, lhs, Imm32(shift)); + else + masm.move32(lhs, dest); + break; + case JSOP_URSH: + if (shift) { + masm.ma_srl(dest, lhs, Imm32(shift)); + } else { + // x >>> 0 can overflow. + masm.move32(lhs, dest); + if (ins->mir()->toUrsh()->fallible()) { + if (!bailoutCmp32(Assembler::LessThan, dest, Imm32(0), ins->snapshot())) + return false; + } + } + break; + default: + MOZ_ASSUME_UNREACHABLE("Unexpected shift op"); + } + } else { + // The shift amounts should be AND'ed into the 0-31 range + masm.ma_and(dest, ToRegister(rhs), Imm32(0x1F)); + + switch (ins->bitop()) { + case JSOP_LSH: + masm.ma_sll(dest, lhs, dest); + break; + case JSOP_RSH: + masm.ma_sra(dest, lhs, dest); + break; + case JSOP_URSH: + masm.ma_srl(dest, lhs, dest); + if (ins->mir()->toUrsh()->fallible()) { + // x >>> 0 can overflow. + if (!bailoutCmp32(Assembler::LessThan, dest, Imm32(0), ins->snapshot())) + return false; + } + break; + default: + MOZ_ASSUME_UNREACHABLE("Unexpected shift op"); + } + } + + return true; +} + +bool +CodeGeneratorMIPS::visitUrshD(LUrshD *ins) +{ + Register lhs = ToRegister(ins->lhs()); + Register temp = ToRegister(ins->temp()); + + const LAllocation *rhs = ins->rhs(); + FloatRegister out = ToFloatRegister(ins->output()); + + if (rhs->isConstant()) { + masm.ma_srl(temp, lhs, Imm32(ToInt32(rhs))); + } else { + masm.ma_srl(temp, lhs, ToRegister(rhs)); + } + + masm.convertUInt32ToDouble(temp, out); + return true; +} + +bool +CodeGeneratorMIPS::visitPowHalfD(LPowHalfD *ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + FloatRegister output = ToFloatRegister(ins->output()); + + Label done, skip; + + // Masm.pow(-Infinity, 0.5) == Infinity. + masm.loadConstantDouble(NegativeInfinity<double>(), ScratchFloatReg); + masm.ma_bc1d(input, ScratchFloatReg, &skip, Assembler::DoubleNotEqualOrUnordered, ShortJump); + masm.as_negd(output, ScratchFloatReg); + masm.ma_b(&done, ShortJump); + + masm.bind(&skip); + // Math.pow(-0, 0.5) == 0 == Math.pow(0, 0.5). + // Adding 0 converts any -0 to 0. + masm.loadConstantDouble(0.0, ScratchFloatReg); + masm.as_addd(output, input, ScratchFloatReg); + masm.as_sqrtd(output, output); + + masm.bind(&done); + return true; +} + +MoveOperand +CodeGeneratorMIPS::toMoveOperand(const LAllocation *a) const +{ + if (a->isGeneralReg()) + return MoveOperand(ToRegister(a)); + if (a->isFloatReg()) { + return MoveOperand(ToFloatRegister(a)); + } + MOZ_ASSERT((ToStackOffset(a) & 3) == 0); + int32_t offset = ToStackOffset(a); + + // The way the stack slots work, we assume that everything from + // depth == 0 downwards is writable. However, since our frame is included + // in this, ensure that the frame gets skipped. + if (gen->compilingAsmJS()) + offset -= AlignmentMidPrologue; + + return MoveOperand(StackPointer, offset); +} + +class js::jit::OutOfLineTableSwitch : public OutOfLineCodeBase<CodeGeneratorMIPS> +{ + MTableSwitch *mir_; + CodeLabel jumpLabel_; + + bool accept(CodeGeneratorMIPS *codegen) { + return codegen->visitOutOfLineTableSwitch(this); + } + + public: + OutOfLineTableSwitch(MTableSwitch *mir) + : mir_(mir) + {} + + MTableSwitch *mir() const { + return mir_; + } + + CodeLabel *jumpLabel() { + return &jumpLabel_; + } +}; + +bool +CodeGeneratorMIPS::visitOutOfLineTableSwitch(OutOfLineTableSwitch *ool) +{ + MTableSwitch *mir = ool->mir(); + + masm.align(sizeof(void*)); + masm.bind(ool->jumpLabel()->src()); + if (!masm.addCodeLabel(*ool->jumpLabel())) + return false; + + for (size_t i = 0; i < mir->numCases(); i++) { + LBlock *caseblock = mir->getCase(i)->lir(); + Label *caseheader = caseblock->label(); + uint32_t caseoffset = caseheader->offset(); + + // The entries of the jump table need to be absolute addresses and thus + // must be patched after codegen is finished. + CodeLabel cl; + masm.ma_li(ScratchRegister, cl.dest()); + masm.branch(ScratchRegister); + cl.src()->bind(caseoffset); + if (!masm.addCodeLabel(cl)) + return false; + } + + return true; +} + +bool +CodeGeneratorMIPS::emitTableSwitchDispatch(MTableSwitch *mir, const Register &index, + const Register &address) +{ + Label *defaultcase = mir->getDefault()->lir()->label(); + + // Lower value with low value + if (mir->low() != 0) + masm.subPtr(Imm32(mir->low()), index); + + // Jump to default case if input is out of range + int32_t cases = mir->numCases(); + masm.branchPtr(Assembler::AboveOrEqual, index, ImmWord(cases), defaultcase); + + // To fill in the CodeLabels for the case entries, we need to first + // generate the case entries (we don't yet know their offsets in the + // instruction stream). + OutOfLineTableSwitch *ool = new(alloc()) OutOfLineTableSwitch(mir); + if (!addOutOfLineCode(ool)) + return false; + + // Compute the position where a pointer to the right case stands. + masm.ma_li(address, ool->jumpLabel()->dest()); + masm.lshiftPtr(Imm32(4), index); + masm.addPtr(index, address); + + masm.branch(address); + return true; +} + +bool +CodeGeneratorMIPS::visitMathD(LMathD *math) +{ + const LAllocation *src1 = math->getOperand(0); + const LAllocation *src2 = math->getOperand(1); + const LDefinition *output = math->getDef(0); + + switch (math->jsop()) { + case JSOP_ADD: + masm.as_addd(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + case JSOP_SUB: + masm.as_subd(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + case JSOP_MUL: + masm.as_muld(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + case JSOP_DIV: + masm.as_divd(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + default: + MOZ_ASSUME_UNREACHABLE("unexpected opcode"); + } + return true; +} + +bool +CodeGeneratorMIPS::visitMathF(LMathF *math) +{ + const LAllocation *src1 = math->getOperand(0); + const LAllocation *src2 = math->getOperand(1); + const LDefinition *output = math->getDef(0); + + switch (math->jsop()) { + case JSOP_ADD: + masm.as_adds(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + case JSOP_SUB: + masm.as_subs(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + case JSOP_MUL: + masm.as_muls(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + case JSOP_DIV: + masm.as_divs(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + default: + MOZ_ASSUME_UNREACHABLE("unexpected opcode"); + } + return true; +} + +bool +CodeGeneratorMIPS::visitFloor(LFloor *lir) +{ + FloatRegister input = ToFloatRegister(lir->input()); + FloatRegister scratch = ScratchFloatReg; + Register output = ToRegister(lir->output()); + + Label skipCheck, done; + + // If Nan, 0 or -0 check for bailout + masm.loadConstantDouble(0.0, scratch); + masm.ma_bc1d(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); + + // If high part is not zero, it is NaN or -0, so we bail. + masm.moveFromDoubleHi(input, SecondScratchReg); + if (!bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot())) + return false; + + // Input was zero, so return zero. + masm.move32(Imm32(0), output); + masm.ma_b(&done, ShortJump); + + masm.bind(&skipCheck); + masm.as_floorwd(scratch, input); + masm.moveFromDoubleLo(scratch, output); + + if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot())) + return false; + + if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot())) + return false; + + masm.bind(&done); + + return true; +} + +bool +CodeGeneratorMIPS::visitFloorF(LFloorF *lir) +{ + FloatRegister input = ToFloatRegister(lir->input()); + FloatRegister scratch = ScratchFloatReg; + Register output = ToRegister(lir->output()); + + Label skipCheck, done; + + // If Nan, 0 or -0 check for bailout + masm.loadConstantFloat32(0.0, scratch); + masm.ma_bc1s(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); + + // If binary value is not zero, it is NaN or -0, so we bail. + masm.moveFromDoubleLo(input, SecondScratchReg); + if (!bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot())) + return false; + + // Input was zero, so return zero. + masm.move32(Imm32(0), output); + masm.ma_b(&done, ShortJump); + + masm.bind(&skipCheck); + masm.as_floorws(scratch, input); + masm.moveFromDoubleLo(scratch, output); + + if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot())) + return false; + + if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot())) + return false; + + masm.bind(&done); + + return true; +} + +bool +CodeGeneratorMIPS::visitRound(LRound *lir) +{ + FloatRegister input = ToFloatRegister(lir->input()); + FloatRegister temp = ToFloatRegister(lir->temp()); + FloatRegister scratch = ScratchFloatReg; + Register output = ToRegister(lir->output()); + + Label bail, negative, end, skipCheck; + + // Load 0.5 in the temp register. + masm.loadConstantDouble(0.5, temp); + + // Branch to a slow path for negative inputs. Doesn't catch NaN or -0. + masm.loadConstantDouble(0.0, scratch); + masm.ma_bc1d(input, scratch, &negative, Assembler::DoubleLessThan, ShortJump); + + // If Nan, 0 or -0 check for bailout + masm.ma_bc1d(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); + + // If high part is not zero, it is NaN or -0, so we bail. + masm.moveFromDoubleHi(input, SecondScratchReg); + if (!bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot())) + return false; + + // Input was zero, so return zero. + masm.move32(Imm32(0), output); + masm.ma_b(&end, ShortJump); + + masm.bind(&skipCheck); + masm.loadConstantDouble(0.5, scratch); + masm.addDouble(input, scratch); + masm.as_floorwd(scratch, scratch); + + masm.moveFromDoubleLo(scratch, output); + + if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot())) + return false; + + if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot())) + return false; + + masm.jump(&end); + + // Input is negative, but isn't -0. + masm.bind(&negative); + masm.addDouble(input, temp); + + // If input + 0.5 >= 0, input is a negative number >= -0.5 and the + // result is -0. + masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, temp, scratch, &bail); + if (!bailoutFrom(&bail, lir->snapshot())) + return false; + + // Truncate and round toward zero. + // This is off-by-one for everything but integer-valued inputs. + masm.as_floorwd(scratch, temp); + masm.moveFromDoubleLo(scratch, output); + + if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot())) + return false; + + masm.bind(&end); + return true; +} + +bool +CodeGeneratorMIPS::visitRoundF(LRoundF *lir) +{ + FloatRegister input = ToFloatRegister(lir->input()); + FloatRegister temp = ToFloatRegister(lir->temp()); + FloatRegister scratch = ScratchFloatReg; + Register output = ToRegister(lir->output()); + + Label bail, negative, end, skipCheck; + + // Load 0.5 in the temp register. + masm.loadConstantFloat32(0.5, temp); + + // Branch to a slow path for negative inputs. Doesn't catch NaN or -0. + masm.loadConstantFloat32(0.0, scratch); + masm.ma_bc1s(input, scratch, &negative, Assembler::DoubleLessThan, ShortJump); + + // If Nan, 0 or -0 check for bailout + masm.ma_bc1s(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); + + // If binary value is not zero, it is NaN or -0, so we bail. + masm.moveFromFloat32(input, SecondScratchReg); + if (!bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot())) + return false; + + // Input was zero, so return zero. + masm.move32(Imm32(0), output); + masm.ma_b(&end, ShortJump); + + masm.bind(&skipCheck); + masm.loadConstantFloat32(0.5, scratch); + masm.as_adds(scratch, input, scratch); + masm.as_floorws(scratch, scratch); + + masm.moveFromFloat32(scratch, output); + + if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot())) + return false; + + if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot())) + return false; + + masm.jump(&end); + + // Input is negative, but isn't -0. + masm.bind(&negative); + masm.as_adds(temp, input, temp); + + // If input + 0.5 >= 0, input is a negative number >= -0.5 and the + // result is -0. + masm.branchFloat(Assembler::DoubleGreaterThanOrEqual, temp, scratch, &bail); + if (!bailoutFrom(&bail, lir->snapshot())) + return false; + + // Truncate and round toward zero. + // This is off-by-one for everything but integer-valued inputs. + masm.as_floorws(scratch, temp); + masm.moveFromFloat32(scratch, output); + + if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot())) + return false; + + masm.bind(&end); + return true; +} + +bool +CodeGeneratorMIPS::visitTruncateDToInt32(LTruncateDToInt32 *ins) +{ + return emitTruncateDouble(ToFloatRegister(ins->input()), ToRegister(ins->output())); +} + +bool +CodeGeneratorMIPS::visitTruncateFToInt32(LTruncateFToInt32 *ins) +{ + return emitTruncateFloat32(ToFloatRegister(ins->input()), ToRegister(ins->output())); +} + +static const uint32_t FrameSizes[] = { 128, 256, 512, 1024 }; + +FrameSizeClass +FrameSizeClass::FromDepth(uint32_t frameDepth) +{ + for (uint32_t i = 0; i < JS_ARRAY_LENGTH(FrameSizes); i++) { + if (frameDepth < FrameSizes[i]) + return FrameSizeClass(i); + } + + return FrameSizeClass::None(); +} + +FrameSizeClass +FrameSizeClass::ClassLimit() +{ + return FrameSizeClass(JS_ARRAY_LENGTH(FrameSizes)); +} + +uint32_t +FrameSizeClass::frameSize() const +{ + MOZ_ASSERT(class_ != NO_FRAME_SIZE_CLASS_ID); + MOZ_ASSERT(class_ < JS_ARRAY_LENGTH(FrameSizes)); + + return FrameSizes[class_]; +} + +ValueOperand +CodeGeneratorMIPS::ToValue(LInstruction *ins, size_t pos) +{ + Register typeReg = ToRegister(ins->getOperand(pos + TYPE_INDEX)); + Register payloadReg = ToRegister(ins->getOperand(pos + PAYLOAD_INDEX)); + return ValueOperand(typeReg, payloadReg); +} + +ValueOperand +CodeGeneratorMIPS::ToOutValue(LInstruction *ins) +{ + Register typeReg = ToRegister(ins->getDef(TYPE_INDEX)); + Register payloadReg = ToRegister(ins->getDef(PAYLOAD_INDEX)); + return ValueOperand(typeReg, payloadReg); +} + +ValueOperand +CodeGeneratorMIPS::ToTempValue(LInstruction *ins, size_t pos) +{ + Register typeReg = ToRegister(ins->getTemp(pos + TYPE_INDEX)); + Register payloadReg = ToRegister(ins->getTemp(pos + PAYLOAD_INDEX)); + return ValueOperand(typeReg, payloadReg); +} + +bool +CodeGeneratorMIPS::visitValue(LValue *value) +{ + const ValueOperand out = ToOutValue(value); + + masm.moveValue(value->value(), out); + return true; +} + +bool +CodeGeneratorMIPS::visitBox(LBox *box) +{ + const LDefinition *type = box->getDef(TYPE_INDEX); + + MOZ_ASSERT(!box->getOperand(0)->isConstant()); + + // For NUNBOX32, the input operand and the output payload have the same + // virtual register. All that needs to be written is the type tag for + // the type definition. + masm.move32(Imm32(MIRTypeToTag(box->type())), ToRegister(type)); + return true; +} + +bool +CodeGeneratorMIPS::visitBoxFloatingPoint(LBoxFloatingPoint *box) +{ + const LDefinition *payload = box->getDef(PAYLOAD_INDEX); + const LDefinition *type = box->getDef(TYPE_INDEX); + const LAllocation *in = box->getOperand(0); + + FloatRegister reg = ToFloatRegister(in); + if (box->type() == MIRType_Float32) { + masm.convertFloat32ToDouble(reg, ScratchFloatReg); + reg = ScratchFloatReg; + } + masm.ma_mv(reg, ValueOperand(ToRegister(type), ToRegister(payload))); + return true; +} + +bool +CodeGeneratorMIPS::visitUnbox(LUnbox *unbox) +{ + // Note that for unbox, the type and payload indexes are switched on the + // inputs. + MUnbox *mir = unbox->mir(); + Register type = ToRegister(unbox->type()); + + if (mir->fallible()) { + if (!bailoutCmp32(Assembler::NotEqual, type, Imm32(MIRTypeToTag(mir->type())), + unbox->snapshot())) + return false; + } + return true; +} + +bool +CodeGeneratorMIPS::visitDouble(LDouble *ins) +{ + const LDefinition *out = ins->getDef(0); + + masm.loadConstantDouble(ins->getDouble(), ToFloatRegister(out)); + return true; +} + +bool +CodeGeneratorMIPS::visitFloat32(LFloat32 *ins) +{ + const LDefinition *out = ins->getDef(0); + masm.loadConstantFloat32(ins->getFloat(), ToFloatRegister(out)); + return true; +} + +Register +CodeGeneratorMIPS::splitTagForTest(const ValueOperand &value) +{ + return value.typeReg(); +} + +bool +CodeGeneratorMIPS::visitTestDAndBranch(LTestDAndBranch *test) +{ + FloatRegister input = ToFloatRegister(test->input()); + + MBasicBlock *ifTrue = test->ifTrue(); + MBasicBlock *ifFalse = test->ifFalse(); + + masm.loadConstantDouble(0.0, ScratchFloatReg); + // If 0, or NaN, the result is false. + + if (isNextBlock(ifFalse->lir())) { + branchToBlock(Assembler::DoubleFloat, input, ScratchFloatReg, ifTrue, + Assembler::DoubleNotEqual); + } else { + branchToBlock(Assembler::DoubleFloat, input, ScratchFloatReg, ifFalse, + Assembler::DoubleEqualOrUnordered); + jumpToBlock(ifTrue); + } + + return true; +} + +bool +CodeGeneratorMIPS::visitTestFAndBranch(LTestFAndBranch *test) +{ + FloatRegister input = ToFloatRegister(test->input()); + + MBasicBlock *ifTrue = test->ifTrue(); + MBasicBlock *ifFalse = test->ifFalse(); + + masm.loadConstantFloat32(0.0, ScratchFloatReg); + // If 0, or NaN, the result is false. + + if (isNextBlock(ifFalse->lir())) { + branchToBlock(Assembler::SingleFloat, input, ScratchFloatReg, ifTrue, + Assembler::DoubleNotEqual); + } else { + branchToBlock(Assembler::SingleFloat, input, ScratchFloatReg, ifFalse, + Assembler::DoubleEqualOrUnordered); + jumpToBlock(ifTrue); + } + + return true; +} + +bool +CodeGeneratorMIPS::visitCompareD(LCompareD *comp) +{ + FloatRegister lhs = ToFloatRegister(comp->left()); + FloatRegister rhs = ToFloatRegister(comp->right()); + Register dest = ToRegister(comp->output()); + + Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop()); + masm.ma_cmp_set_double(dest, lhs, rhs, cond); + return true; +} + +bool +CodeGeneratorMIPS::visitCompareF(LCompareF *comp) +{ + FloatRegister lhs = ToFloatRegister(comp->left()); + FloatRegister rhs = ToFloatRegister(comp->right()); + Register dest = ToRegister(comp->output()); + + Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop()); + masm.ma_cmp_set_float32(dest, lhs, rhs, cond); + return true; +} + + +bool +CodeGeneratorMIPS::visitCompareDAndBranch(LCompareDAndBranch *comp) +{ + FloatRegister lhs = ToFloatRegister(comp->left()); + FloatRegister rhs = ToFloatRegister(comp->right()); + + Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop()); + MBasicBlock *ifTrue = comp->ifTrue(); + MBasicBlock *ifFalse = comp->ifFalse(); + + if (isNextBlock(ifFalse->lir())) { + branchToBlock(Assembler::DoubleFloat, lhs, rhs, ifTrue, cond); + } else { + branchToBlock(Assembler::DoubleFloat, lhs, rhs, ifFalse, + Assembler::InvertCondition(cond)); + jumpToBlock(ifTrue); + } + + return true; +} + +bool +CodeGeneratorMIPS::visitCompareFAndBranch(LCompareFAndBranch *comp) +{ + FloatRegister lhs = ToFloatRegister(comp->left()); + FloatRegister rhs = ToFloatRegister(comp->right()); + + Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop()); + MBasicBlock *ifTrue = comp->ifTrue(); + MBasicBlock *ifFalse = comp->ifFalse(); + + if (isNextBlock(ifFalse->lir())) { + branchToBlock(Assembler::SingleFloat, lhs, rhs, ifTrue, cond); + } else { + branchToBlock(Assembler::SingleFloat, lhs, rhs, ifFalse, + Assembler::InvertCondition(cond)); + jumpToBlock(ifTrue); + } + + return true; +} + +bool +CodeGeneratorMIPS::visitCompareB(LCompareB *lir) +{ + MCompare *mir = lir->mir(); + + const ValueOperand lhs = ToValue(lir, LCompareB::Lhs); + const LAllocation *rhs = lir->rhs(); + const Register output = ToRegister(lir->output()); + + MOZ_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE); + Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop()); + + Label notBoolean, done; + masm.branchTestBoolean(Assembler::NotEqual, lhs, ¬Boolean); + { + if (rhs->isConstant()) + masm.cmp32Set(cond, lhs.payloadReg(), Imm32(rhs->toConstant()->toBoolean()), output); + else + masm.cmp32Set(cond, lhs.payloadReg(), ToRegister(rhs), output); + masm.jump(&done); + } + + masm.bind(¬Boolean); + { + masm.move32(Imm32(mir->jsop() == JSOP_STRICTNE), output); + } + + masm.bind(&done); + return true; +} + +bool +CodeGeneratorMIPS::visitCompareBAndBranch(LCompareBAndBranch *lir) +{ + MCompare *mir = lir->cmpMir(); + const ValueOperand lhs = ToValue(lir, LCompareBAndBranch::Lhs); + const LAllocation *rhs = lir->rhs(); + + MOZ_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE); + + MBasicBlock *mirNotBoolean = (mir->jsop() == JSOP_STRICTEQ) ? lir->ifFalse() : lir->ifTrue(); + branchToBlock(lhs.typeReg(), ImmType(JSVAL_TYPE_BOOLEAN), mirNotBoolean, Assembler::NotEqual); + + Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop()); + if (rhs->isConstant()) + emitBranch(lhs.payloadReg(), Imm32(rhs->toConstant()->toBoolean()), cond, lir->ifTrue(), + lir->ifFalse()); + else + emitBranch(lhs.payloadReg(), ToRegister(rhs), cond, lir->ifTrue(), lir->ifFalse()); + + return true; +} + +bool +CodeGeneratorMIPS::visitCompareV(LCompareV *lir) +{ + MCompare *mir = lir->mir(); + Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop()); + const ValueOperand lhs = ToValue(lir, LCompareV::LhsInput); + const ValueOperand rhs = ToValue(lir, LCompareV::RhsInput); + const Register output = ToRegister(lir->output()); + + MOZ_ASSERT(IsEqualityOp(mir->jsop())); + + Label notEqual, done; + masm.ma_b(lhs.typeReg(), rhs.typeReg(), ¬Equal, Assembler::NotEqual, ShortJump); + { + masm.cmp32Set(cond, lhs.payloadReg(), rhs.payloadReg(), output); + masm.ma_b(&done, ShortJump); + } + masm.bind(¬Equal); + { + masm.move32(Imm32(cond == Assembler::NotEqual), output); + } + + masm.bind(&done); + return true; +} + +bool +CodeGeneratorMIPS::visitCompareVAndBranch(LCompareVAndBranch *lir) +{ + MCompare *mir = lir->cmpMir(); + Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop()); + const ValueOperand lhs = ToValue(lir, LCompareVAndBranch::LhsInput); + const ValueOperand rhs = ToValue(lir, LCompareVAndBranch::RhsInput); + + MOZ_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ || + mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE); + + MBasicBlock *notEqual = (cond == Assembler::Equal) ? lir->ifFalse() : lir->ifTrue(); + + branchToBlock(lhs.typeReg(), rhs.typeReg(), notEqual, Assembler::NotEqual); + emitBranch(lhs.payloadReg(), rhs.payloadReg(), cond, lir->ifTrue(), lir->ifFalse()); + + return true; +} + +bool +CodeGeneratorMIPS::visitBitAndAndBranch(LBitAndAndBranch *lir) +{ + if (lir->right()->isConstant()) + masm.ma_and(ScratchRegister, ToRegister(lir->left()), Imm32(ToInt32(lir->right()))); + else + masm.ma_and(ScratchRegister, ToRegister(lir->left()), ToRegister(lir->right())); + emitBranch(ScratchRegister, ScratchRegister, Assembler::NonZero, lir->ifTrue(), + lir->ifFalse()); + return true; +} + +bool +CodeGeneratorMIPS::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble *lir) +{ + masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output())); + return true; +} + +bool +CodeGeneratorMIPS::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32 *lir) +{ + masm.convertUInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output())); + return true; +} + +bool +CodeGeneratorMIPS::visitNotI(LNotI *ins) +{ + masm.cmp32Set(Assembler::Equal, ToRegister(ins->input()), Imm32(0), + ToRegister(ins->output())); + return true; +} + +bool +CodeGeneratorMIPS::visitNotD(LNotD *ins) +{ + // Since this operation is not, we want to set a bit if + // the double is falsey, which means 0.0, -0.0 or NaN. + FloatRegister in = ToFloatRegister(ins->input()); + Register dest = ToRegister(ins->output()); + + Label falsey, done; + masm.loadConstantDouble(0.0, ScratchFloatReg); + masm.ma_bc1d(in, ScratchFloatReg, &falsey, Assembler::DoubleEqualOrUnordered, ShortJump); + + masm.move32(Imm32(0), dest); + masm.ma_b(&done, ShortJump); + + masm.bind(&falsey); + masm.move32(Imm32(1), dest); + + masm.bind(&done); + return true; +} + +bool +CodeGeneratorMIPS::visitNotF(LNotF *ins) +{ + // Since this operation is not, we want to set a bit if + // the float32 is falsey, which means 0.0, -0.0 or NaN. + FloatRegister in = ToFloatRegister(ins->input()); + Register dest = ToRegister(ins->output()); + + Label falsey, done; + masm.loadConstantFloat32(0.0, ScratchFloatReg); + masm.ma_bc1s(in, ScratchFloatReg, &falsey, Assembler::DoubleEqualOrUnordered, ShortJump); + + masm.move32(Imm32(0), dest); + masm.ma_b(&done, ShortJump); + + masm.bind(&falsey); + masm.move32(Imm32(1), dest); + + masm.bind(&done); + return true; +} + +bool +CodeGeneratorMIPS::visitLoadSlotV(LLoadSlotV *load) +{ + const ValueOperand out = ToOutValue(load); + Register base = ToRegister(load->input()); + int32_t offset = load->mir()->slot() * sizeof(js::Value); + + masm.loadValue(Address(base, offset), out); + return true; +} + +bool +CodeGeneratorMIPS::visitLoadSlotT(LLoadSlotT *load) +{ + Register base = ToRegister(load->input()); + int32_t offset = load->mir()->slot() * sizeof(js::Value); + + if (load->mir()->type() == MIRType_Double) + masm.loadInt32OrDouble(Address(base, offset), ToFloatRegister(load->output())); + else + masm.load32(Address(base, offset + NUNBOX32_PAYLOAD_OFFSET), ToRegister(load->output())); + return true; +} + +bool +CodeGeneratorMIPS::visitStoreSlotT(LStoreSlotT *store) +{ + Register base = ToRegister(store->slots()); + int32_t offset = store->mir()->slot() * sizeof(js::Value); + + const LAllocation *value = store->value(); + MIRType valueType = store->mir()->value()->type(); + + if (store->mir()->needsBarrier()) + emitPreBarrier(Address(base, offset), store->mir()->slotType()); + + if (valueType == MIRType_Double) { + masm.storeDouble(ToFloatRegister(value), Address(base, offset)); + return true; + } + + // Store the type tag if needed. + if (valueType != store->mir()->slotType()) + masm.storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), Address(base, offset)); + + // Store the payload. + if (value->isConstant()) + masm.storePayload(*value->toConstant(), Address(base, offset)); + else + masm.storePayload(ToRegister(value), Address(base, offset)); + + return true; +} + +bool +CodeGeneratorMIPS::visitLoadElementT(LLoadElementT *load) +{ + Register base = ToRegister(load->elements()); + if (load->mir()->type() == MIRType_Double) { + FloatRegister fpreg = ToFloatRegister(load->output()); + if (load->index()->isConstant()) { + Address source(base, ToInt32(load->index()) * sizeof(Value)); + if (load->mir()->loadDoubles()) + masm.loadDouble(source, fpreg); + else + masm.loadInt32OrDouble(source, fpreg); + } else { + Register index = ToRegister(load->index()); + if (load->mir()->loadDoubles()) + masm.loadDouble(BaseIndex(base, index, TimesEight), fpreg); + else + masm.loadInt32OrDouble(base, index, fpreg); + } + } else { + if (load->index()->isConstant()) { + Address source(base, ToInt32(load->index()) * sizeof(Value)); + masm.load32(source, ToRegister(load->output())); + } else { + BaseIndex source(base, ToRegister(load->index()), TimesEight); + masm.load32(source, ToRegister(load->output())); + } + } + MOZ_ASSERT(!load->mir()->needsHoleCheck()); + return true; +} + +void +CodeGeneratorMIPS::storeElementTyped(const LAllocation *value, MIRType valueType, + MIRType elementType, const Register &elements, + const LAllocation *index) +{ + if (index->isConstant()) { + Address dest = Address(elements, ToInt32(index) * sizeof(Value)); + if (valueType == MIRType_Double) { + masm.storeDouble(ToFloatRegister(value), Address(dest.base, dest.offset)); + return; + } + + // Store the type tag if needed. + if (valueType != elementType) + masm.storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), dest); + + // Store the payload. + if (value->isConstant()) + masm.storePayload(*value->toConstant(), dest); + else + masm.storePayload(ToRegister(value), dest); + } else { + Register indexReg = ToRegister(index); + if (valueType == MIRType_Double) { + masm.storeDouble(ToFloatRegister(value), BaseIndex(elements, indexReg, TimesEight)); + return; + } + + // Store the type tag if needed. + if (valueType != elementType) + masm.storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), elements, indexReg); + + // Store the payload. + if (value->isConstant()) + masm.storePayload(*value->toConstant(), elements, indexReg); + else + masm.storePayload(ToRegister(value), elements, indexReg); + } +} + +bool +CodeGeneratorMIPS::visitGuardShape(LGuardShape *guard) +{ + Register obj = ToRegister(guard->input()); + Register tmp = ToRegister(guard->tempInt()); + + masm.loadPtr(Address(obj, JSObject::offsetOfShape()), tmp); + return bailoutCmpPtr(Assembler::NotEqual, tmp, ImmGCPtr(guard->mir()->shape()), + guard->snapshot()); +} + +bool +CodeGeneratorMIPS::visitGuardObjectType(LGuardObjectType *guard) +{ + Register obj = ToRegister(guard->input()); + Register tmp = ToRegister(guard->tempInt()); + + masm.loadPtr(Address(obj, JSObject::offsetOfType()), tmp); + Assembler::Condition cond = guard->mir()->bailOnEquality() + ? Assembler::Equal + : Assembler::NotEqual; + return bailoutCmpPtr(cond, tmp, ImmGCPtr(guard->mir()->typeObject()), guard->snapshot()); +} + +bool +CodeGeneratorMIPS::visitGuardClass(LGuardClass *guard) +{ + Register obj = ToRegister(guard->input()); + Register tmp = ToRegister(guard->tempInt()); + + masm.loadObjClass(obj, tmp); + if (!bailoutCmpPtr(Assembler::NotEqual, tmp, Imm32((uint32_t)guard->mir()->getClass()), + guard->snapshot())) + return false; + return true; +} + +bool +CodeGeneratorMIPS::visitImplicitThis(LImplicitThis *lir) +{ + Register callee = ToRegister(lir->callee()); + const ValueOperand out = ToOutValue(lir); + + // The implicit |this| is always |undefined| if the function's environment + // is the current global. + masm.loadPtr(Address(callee, JSFunction::offsetOfEnvironment()), out.typeReg()); + GlobalObject *global = &gen->info().script()->global(); + + // TODO: OOL stub path. + if (!bailoutCmpPtr(Assembler::NotEqual, out.typeReg(), ImmGCPtr(global), lir->snapshot())) + return false; + + masm.moveValue(UndefinedValue(), out); + return true; +} + +bool +CodeGeneratorMIPS::visitInterruptCheck(LInterruptCheck *lir) +{ + OutOfLineCode *ool = oolCallVM(InterruptCheckInfo, lir, (ArgList()), StoreNothing()); + if (!ool) + return false; + + masm.branch32(Assembler::NotEqual, + AbsoluteAddress(GetIonContext()->runtime->addressOfInterrupt()), Imm32(0), + ool->entry()); + masm.bind(ool->rejoin()); + return true; +} + +bool +CodeGeneratorMIPS::generateInvalidateEpilogue() +{ + // Ensure that there is enough space in the buffer for the OsiPoint + // patching to occur. Otherwise, we could overwrite the invalidation + // epilogue. + for (size_t i = 0; i < sizeof(void *); i += Assembler::nopSize()) + masm.nop(); + + masm.bind(&invalidate_); + + // Push the return address of the point that we bailed out at to the stack + masm.Push(ra); + + // Push the Ion script onto the stack (when we determine what that + // pointer is). + invalidateEpilogueData_ = masm.pushWithPatch(ImmWord(uintptr_t(-1))); + JitCode *thunk = gen->jitRuntime()->getInvalidationThunk(); + + masm.branch(thunk); + + // We should never reach this point in JIT code -- the invalidation thunk + // should pop the invalidated JS frame and return directly to its caller. + masm.assumeUnreachable("Should have returned directly to its caller instead of here."); + return true; +} + +void +DispatchIonCache::initializeAddCacheState(LInstruction *ins, AddCacheState *addState) +{ + // Can always use the scratch register on MIPS. + addState->dispatchScratch = ScratchRegister; +} + +bool +CodeGeneratorMIPS::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins) +{ + MOZ_ASSUME_UNREACHABLE("NYI"); +} + +bool +CodeGeneratorMIPS::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins) +{ + MOZ_ASSUME_UNREACHABLE("NYI"); +} + +bool +CodeGeneratorMIPS::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) +{ + const MAsmJSLoadHeap *mir = ins->mir(); + const LAllocation *ptr = ins->ptr(); + const LDefinition *out = ins->output(); + + bool isSigned; + int size; + bool isFloat = false; + switch (mir->viewType()) { + case ArrayBufferView::TYPE_INT8: isSigned = true; size = 8; break; + case ArrayBufferView::TYPE_UINT8: isSigned = false; size = 8; break; + case ArrayBufferView::TYPE_INT16: isSigned = true; size = 16; break; + case ArrayBufferView::TYPE_UINT16: isSigned = false; size = 16; break; + case ArrayBufferView::TYPE_INT32: isSigned = true; size = 32; break; + case ArrayBufferView::TYPE_UINT32: isSigned = false; size = 32; break; + case ArrayBufferView::TYPE_FLOAT64: isFloat = true; size = 64; break; + case ArrayBufferView::TYPE_FLOAT32: isFloat = true; size = 32; break; + default: MOZ_ASSUME_UNREACHABLE("unexpected array type"); + } + + if (ptr->isConstant()) { + MOZ_ASSERT(mir->skipBoundsCheck()); + int32_t ptrImm = ptr->toConstant()->toInt32(); + MOZ_ASSERT(ptrImm >= 0); + if (isFloat) { + if (size == 32) { + masm.loadFloat32(Address(HeapReg, ptrImm), ToFloatRegister(out)); + } else { + masm.loadDouble(Address(HeapReg, ptrImm), ToFloatRegister(out)); + } + } else { + masm.ma_load(ToRegister(out), Address(HeapReg, ptrImm), + static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend); + } + return true; + } + + Register ptrReg = ToRegister(ptr); + + if (mir->skipBoundsCheck()) { + if (isFloat) { + if (size == 32) { + masm.loadFloat32(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); + } else { + masm.loadDouble(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); + } + } else { + masm.ma_load(ToRegister(out), BaseIndex(HeapReg, ptrReg, TimesOne), + static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend); + } + return true; + } + + BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister); + + Label outOfRange; + Label done; + masm.ma_b(ptrReg, ScratchRegister, &outOfRange, Assembler::AboveOrEqual, ShortJump); + // Offset is ok, let's load value. + if (isFloat) { + if (size == 32) + masm.loadFloat32(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); + else + masm.loadDouble(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); + } else { + masm.ma_load(ToRegister(out), BaseIndex(HeapReg, ptrReg, TimesOne), + static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend); + } + masm.ma_b(&done, ShortJump); + masm.bind(&outOfRange); + // Offset is out of range. Load default values. + if (isFloat) { + if (size == 32) + masm.convertDoubleToFloat32(NANReg, ToFloatRegister(out)); + else + masm.moveDouble(NANReg, ToFloatRegister(out)); + } else { + masm.move32(Imm32(0), ToRegister(out)); + } + masm.bind(&done); + + return gen->noteHeapAccess(AsmJSHeapAccess(bo.getOffset())); +} + +bool +CodeGeneratorMIPS::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) +{ + const MAsmJSStoreHeap *mir = ins->mir(); + const LAllocation *value = ins->value(); + const LAllocation *ptr = ins->ptr(); + + bool isSigned; + int size; + bool isFloat = false; + switch (mir->viewType()) { + case ArrayBufferView::TYPE_INT8: isSigned = true; size = 8; break; + case ArrayBufferView::TYPE_UINT8: isSigned = false; size = 8; break; + case ArrayBufferView::TYPE_INT16: isSigned = true; size = 16; break; + case ArrayBufferView::TYPE_UINT16: isSigned = false; size = 16; break; + case ArrayBufferView::TYPE_INT32: isSigned = true; size = 32; break; + case ArrayBufferView::TYPE_UINT32: isSigned = false; size = 32; break; + case ArrayBufferView::TYPE_FLOAT64: isFloat = true; size = 64; break; + case ArrayBufferView::TYPE_FLOAT32: isFloat = true; size = 32; break; + default: MOZ_ASSUME_UNREACHABLE("unexpected array type"); + } + + if (ptr->isConstant()) { + MOZ_ASSERT(mir->skipBoundsCheck()); + int32_t ptrImm = ptr->toConstant()->toInt32(); + MOZ_ASSERT(ptrImm >= 0); + + if (isFloat) { + if (size == 32) { + masm.storeFloat32(ToFloatRegister(value), Address(HeapReg, ptrImm)); + } else { + masm.storeDouble(ToFloatRegister(value), Address(HeapReg, ptrImm)); + } + } else { + masm.ma_store(ToRegister(value), Address(HeapReg, ptrImm), + static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend); + } + return true; + } + + Register ptrReg = ToRegister(ptr); + Address dstAddr(ptrReg, 0); + + if (mir->skipBoundsCheck()) { + if (isFloat) { + if (size == 32) { + masm.storeFloat32(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); + } else + masm.storeDouble(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); + } else { + masm.ma_store(ToRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne), + static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend); + } + return true; + } + + BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister); + + Label rejoin; + masm.ma_b(ptrReg, ScratchRegister, &rejoin, Assembler::AboveOrEqual, ShortJump); + + // Offset is ok, let's store value. + if (isFloat) { + if (size == 32) { + masm.storeFloat32(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); + } else + masm.storeDouble(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); + } else { + masm.ma_store(ToRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne), + static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend); + } + masm.bind(&rejoin); + + return gen->noteHeapAccess(AsmJSHeapAccess(bo.getOffset())); +} + +bool +CodeGeneratorMIPS::visitAsmJSPassStackArg(LAsmJSPassStackArg *ins) +{ + const MAsmJSPassStackArg *mir = ins->mir(); + if (ins->arg()->isConstant()) { + masm.storePtr(ImmWord(ToInt32(ins->arg())), Address(StackPointer, mir->spOffset())); + } else { + if (ins->arg()->isGeneralReg()) { + masm.storePtr(ToRegister(ins->arg()), Address(StackPointer, mir->spOffset())); + } else { + masm.storeDouble(ToFloatRegister(ins->arg()), Address(StackPointer, mir->spOffset())); + } + } + + return true; +} + +bool +CodeGeneratorMIPS::visitUDiv(LUDiv *ins) +{ + Register lhs = ToRegister(ins->lhs()); + Register rhs = ToRegister(ins->rhs()); + Register output = ToRegister(ins->output()); + + Label done; + if (ins->mir()->canBeDivideByZero()) { + if (ins->mir()->isTruncated()) { + Label notzero; + masm.ma_b(rhs, rhs, ¬zero, Assembler::NonZero, ShortJump); + masm.move32(Imm32(0), output); + masm.ma_b(&done, ShortJump); + masm.bind(¬zero); + } else { + MOZ_ASSERT(ins->mir()->fallible()); + if (!bailoutCmp32(Assembler::Equal, rhs, Imm32(0), ins->snapshot())) + return false; + } + } + + masm.as_divu(lhs, rhs); + masm.as_mflo(output); + + if (!ins->mir()->isTruncated()) { + if (!bailoutCmp32(Assembler::LessThan, output, Imm32(0), ins->snapshot())) + return false; + } + + masm.bind(&done); + return true; +} + +bool +CodeGeneratorMIPS::visitUMod(LUMod *ins) +{ + Register lhs = ToRegister(ins->lhs()); + Register rhs = ToRegister(ins->rhs()); + Register output = ToRegister(ins->output()); + Label done; + + if (ins->mir()->canBeDivideByZero()) { + if (ins->mir()->isTruncated()) { + // Infinity|0 == 0 + Label notzero; + masm.ma_b(rhs, rhs, ¬zero, Assembler::NonZero, ShortJump); + masm.move32(Imm32(0), output); + masm.ma_b(&done, ShortJump); + masm.bind(¬zero); + } else { + MOZ_ASSERT(ins->mir()->fallible()); + if (!bailoutCmp32(Assembler::Equal, rhs, Imm32(0), ins->snapshot())) + return false; + } + } + + masm.as_divu(lhs, rhs); + masm.as_mfhi(output); + + if (!ins->mir()->isTruncated()) { + if (!bailoutCmp32(Assembler::LessThan, output, Imm32(0), ins->snapshot())) + return false; + } + + masm.bind(&done); + return true; +} + +bool +CodeGeneratorMIPS::visitEffectiveAddress(LEffectiveAddress *ins) +{ + const MEffectiveAddress *mir = ins->mir(); + Register base = ToRegister(ins->base()); + Register index = ToRegister(ins->index()); + Register output = ToRegister(ins->output()); + + BaseIndex address(base, index, mir->scale(), mir->displacement()); + masm.computeEffectiveAddress(address, output); + return true; +} + +bool +CodeGeneratorMIPS::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins) +{ + const MAsmJSLoadGlobalVar *mir = ins->mir(); + unsigned addr = mir->globalDataOffset(); + if (mir->type() == MIRType_Int32) + masm.load32(Address(GlobalReg, addr), ToRegister(ins->output())); + else if (mir->type() == MIRType_Float32) + masm.loadFloat32(Address(GlobalReg, addr), ToFloatRegister(ins->output())); + else + masm.loadDouble(Address(GlobalReg, addr), ToFloatRegister(ins->output())); + return true; +} + +bool +CodeGeneratorMIPS::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins) +{ + const MAsmJSStoreGlobalVar *mir = ins->mir(); + + MIRType type = mir->value()->type(); + MOZ_ASSERT(IsNumberType(type)); + unsigned addr = mir->globalDataOffset(); + if (mir->value()->type() == MIRType_Int32) + masm.store32(ToRegister(ins->value()), Address(GlobalReg, addr)); + else if (mir->value()->type() == MIRType_Float32) + masm.storeFloat32(ToFloatRegister(ins->value()), Address(GlobalReg, addr)); + else + masm.storeDouble(ToFloatRegister(ins->value()), Address(GlobalReg, addr)); + return true; +} + +bool +CodeGeneratorMIPS::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins) +{ + const MAsmJSLoadFuncPtr *mir = ins->mir(); + + Register index = ToRegister(ins->index()); + Register tmp = ToRegister(ins->temp()); + Register out = ToRegister(ins->output()); + unsigned addr = mir->globalDataOffset(); + + BaseIndex source(GlobalReg, index, TimesFour, addr); + masm.load32(source, out); + return true; +} + +bool +CodeGeneratorMIPS::visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins) +{ + const MAsmJSLoadFFIFunc *mir = ins->mir(); + masm.loadPtr(Address(GlobalReg, mir->globalDataOffset()), ToRegister(ins->output())); + return true; +} + +bool +CodeGeneratorMIPS::visitNegI(LNegI *ins) +{ + Register input = ToRegister(ins->input()); + Register output = ToRegister(ins->output()); + + masm.ma_negu(output, input); + return true; +} + +bool +CodeGeneratorMIPS::visitNegD(LNegD *ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + FloatRegister output = ToFloatRegister(ins->output()); + + masm.as_negd(output, input); + return true; +} + +bool +CodeGeneratorMIPS::visitNegF(LNegF *ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + FloatRegister output = ToFloatRegister(ins->output()); + + masm.as_negs(output, input); + return true; +} + +bool +CodeGeneratorMIPS::visitForkJoinGetSlice(LForkJoinGetSlice *ins) +{ + MOZ_ASSUME_UNREACHABLE("NYI"); +} + +JitCode * +JitRuntime::generateForkJoinGetSliceStub(JSContext *cx) +{ + MOZ_ASSUME_UNREACHABLE("NYI"); +}
diff --git a/src/third_party/mozjs/js/src/jit/mips/CodeGenerator-mips.h b/src/third_party/mozjs/js/src/jit/mips/CodeGenerator-mips.h new file mode 100644 index 0000000..95933a4 --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/CodeGenerator-mips.h
@@ -0,0 +1,308 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_mips_CodeGenerator_mips_h +#define jit_mips_CodeGenerator_mips_h + +#include "jit/mips/Assembler-mips.h" +#include "jit/shared/CodeGenerator-shared.h" + +namespace js { +namespace jit { + +class OutOfLineBailout; +class OutOfLineTableSwitch; + +class CodeGeneratorMIPS : public CodeGeneratorShared +{ + friend class MoveResolverMIPS; + + CodeGeneratorMIPS *thisFromCtor() { + return this; + } + + protected: + // Label for the common return path. + NonAssertingLabel returnLabel_; + NonAssertingLabel deoptLabel_; + + inline Address ToAddress(const LAllocation &a) { + MOZ_ASSERT(a.isMemory()); + int32_t offset = ToStackOffset(&a); + + // The way the stack slots work, we assume that everything from + // depth == 0 downwards is writable however, since our frame is + // included in this, ensure that the frame gets skipped. + if (gen->compilingAsmJS()) + offset -= AlignmentMidPrologue; + + return Address(StackPointer, offset); + } + + inline Address ToAddress(const LAllocation *a) { + return ToAddress(*a); + } + + inline Operand ToOperand(const LAllocation &a) { + if (a.isGeneralReg()) + return Operand(a.toGeneralReg()->reg()); + if (a.isFloatReg()) + return Operand(a.toFloatReg()->reg()); + + MOZ_ASSERT(a.isMemory()); + int32_t offset = ToStackOffset(&a); + + // The way the stack slots work, we assume that everything from + // depth == 0 downwards is writable however, since our frame is + // included in this, ensure that the frame gets skipped. + if (gen->compilingAsmJS()) + offset -= AlignmentMidPrologue; + + return Operand(StackPointer, offset); + } + inline Operand ToOperand(const LAllocation *a) { + return ToOperand(*a); + } + inline Operand ToOperand(const LDefinition *def) { + return ToOperand(def->output()); + } + + MoveOperand toMoveOperand(const LAllocation *a) const; + + template <typename T1, typename T2> + bool bailoutCmp32(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot *snapshot) { + bool goodBailout; + Label skip; + masm.ma_b(lhs, rhs, &skip, Assembler::InvertCondition(c), ShortJump); + goodBailout = bailout(snapshot); + masm.bind(&skip); + return goodBailout; + } + template<typename T> + bool bailoutCmp32(Assembler::Condition c, Operand lhs, T rhs, LSnapshot *snapshot) { + if (lhs.getTag() == Operand::REG) + return bailoutCmp32(c, lhs.toReg(), rhs, snapshot); + if (lhs.getTag() == Operand::MEM) + return bailoutCmp32(c, lhs.toAddress(), rhs, snapshot); + MOZ_ASSUME_UNREACHABLE("Invalid operand tag."); + return false; + } + template<typename T> + bool bailoutTest32(Assembler::Condition c, Register lhs, T rhs, LSnapshot *snapshot) { + Label bail; + masm.branchTest32(c, lhs, rhs, &bail); + return bailoutFrom(&bail, snapshot); + } + template <typename T1, typename T2> + bool bailoutCmpPtr(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot *snapshot) { + return bailoutCmp32(c, lhs, rhs, snapshot); + } + bool bailoutTestPtr(Assembler::Condition c, Register lhs, Register rhs, LSnapshot *snapshot) { + Label bail; + masm.branchTestPtr(c, lhs, rhs, &bail); + return bailoutFrom(&bail, snapshot); + } + + bool bailoutFrom(Label *label, LSnapshot *snapshot); + bool bailout(LSnapshot *snapshot); + + protected: + bool generatePrologue(); + bool generateEpilogue(); + bool generateOutOfLineCode(); + + template <typename T> + void branchToBlock(Register lhs, T rhs, MBasicBlock *mir, Assembler::Condition cond) + { + Label *label = mir->lir()->label(); + if (Label *oolEntry = labelForBackedgeWithImplicitCheck(mir)) { + // Note: the backedge is initially a jump to the next instruction. + // It will be patched to the target block's label during link(). + RepatchLabel rejoin; + CodeOffsetJump backedge; + Label skip; + + masm.ma_b(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump); + backedge = masm.jumpWithPatch(&rejoin); + masm.bind(&rejoin); + masm.bind(&skip); + + if (!patchableBackedges_.append(PatchableBackedgeInfo(backedge, label, oolEntry))) + MOZ_CRASH(); + } else { + masm.ma_b(lhs, rhs, label, cond); + } + } + void branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs, + MBasicBlock *mir, Assembler::DoubleCondition cond); + + // Emits a branch that directs control flow to the true block if |cond| is + // true, and the false block if |cond| is false. + template <typename T> + void emitBranch(Register lhs, T rhs, Assembler::Condition cond, + MBasicBlock *mirTrue, MBasicBlock *mirFalse) + { + if (isNextBlock(mirFalse->lir())) { + branchToBlock(lhs, rhs, mirTrue, cond); + } else { + branchToBlock(lhs, rhs, mirFalse, Assembler::InvertCondition(cond)); + jumpToBlock(mirTrue); + } + } + void testNullEmitBranch(Assembler::Condition cond, const ValueOperand &value, + MBasicBlock *ifTrue, MBasicBlock *ifFalse) + { + emitBranch(value.typeReg(), (Imm32)ImmType(JSVAL_TYPE_NULL), cond, ifTrue, ifFalse); + } + void testUndefinedEmitBranch(Assembler::Condition cond, const ValueOperand &value, + MBasicBlock *ifTrue, MBasicBlock *ifFalse) + { + emitBranch(value.typeReg(), (Imm32)ImmType(JSVAL_TYPE_UNDEFINED), cond, ifTrue, ifFalse); + } + + bool emitTableSwitchDispatch(MTableSwitch *mir, const Register &index, const Register &base); + + public: + // Instruction visitors. + virtual bool visitMinMaxD(LMinMaxD *ins); + virtual bool visitAbsD(LAbsD *ins); + virtual bool visitAbsF(LAbsF *ins); + virtual bool visitSqrtD(LSqrtD *ins); + virtual bool visitSqrtF(LSqrtF *ins); + virtual bool visitAddI(LAddI *ins); + virtual bool visitSubI(LSubI *ins); + virtual bool visitBitNotI(LBitNotI *ins); + virtual bool visitBitOpI(LBitOpI *ins); + + virtual bool visitMulI(LMulI *ins); + + virtual bool visitDivI(LDivI *ins); + virtual bool visitDivPowTwoI(LDivPowTwoI *ins); + virtual bool visitModI(LModI *ins); + virtual bool visitModPowTwoI(LModPowTwoI *ins); + virtual bool visitModMaskI(LModMaskI *ins); + virtual bool visitPowHalfD(LPowHalfD *ins); + virtual bool visitShiftI(LShiftI *ins); + virtual bool visitUrshD(LUrshD *ins); + + virtual bool visitTestIAndBranch(LTestIAndBranch *test); + virtual bool visitCompare(LCompare *comp); + virtual bool visitCompareAndBranch(LCompareAndBranch *comp); + virtual bool visitTestDAndBranch(LTestDAndBranch *test); + virtual bool visitTestFAndBranch(LTestFAndBranch *test); + virtual bool visitCompareD(LCompareD *comp); + virtual bool visitCompareF(LCompareF *comp); + virtual bool visitCompareDAndBranch(LCompareDAndBranch *comp); + virtual bool visitCompareFAndBranch(LCompareFAndBranch *comp); + virtual bool visitCompareB(LCompareB *lir); + virtual bool visitCompareBAndBranch(LCompareBAndBranch *lir); + virtual bool visitCompareV(LCompareV *lir); + virtual bool visitCompareVAndBranch(LCompareVAndBranch *lir); + virtual bool visitBitAndAndBranch(LBitAndAndBranch *lir); + virtual bool visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble *lir); + virtual bool visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32 *lir); + virtual bool visitNotI(LNotI *ins); + virtual bool visitNotD(LNotD *ins); + virtual bool visitNotF(LNotF *ins); + + virtual bool visitMathD(LMathD *math); + virtual bool visitMathF(LMathF *math); + virtual bool visitFloor(LFloor *lir); + virtual bool visitFloorF(LFloorF *lir); + virtual bool visitRound(LRound *lir); + virtual bool visitRoundF(LRoundF *lir); + virtual bool visitTruncateDToInt32(LTruncateDToInt32 *ins); + virtual bool visitTruncateFToInt32(LTruncateFToInt32 *ins); + + // Out of line visitors. + bool visitOutOfLineBailout(OutOfLineBailout *ool); + bool visitOutOfLineTableSwitch(OutOfLineTableSwitch *ool); + + protected: + ValueOperand ToValue(LInstruction *ins, size_t pos); + ValueOperand ToOutValue(LInstruction *ins); + ValueOperand ToTempValue(LInstruction *ins, size_t pos); + + // Functions for LTestVAndBranch. + Register splitTagForTest(const ValueOperand &value); + + void storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType, + const Register &elements, const LAllocation *index); + + public: + CodeGeneratorMIPS(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm); + + public: + bool visitBox(LBox *box); + bool visitBoxFloatingPoint(LBoxFloatingPoint *box); + bool visitUnbox(LUnbox *unbox); + bool visitValue(LValue *value); + bool visitDouble(LDouble *ins); + bool visitFloat32(LFloat32 *ins); + + bool visitLoadSlotV(LLoadSlotV *load); + bool visitLoadSlotT(LLoadSlotT *load); + bool visitStoreSlotT(LStoreSlotT *load); + + bool visitLoadElementT(LLoadElementT *load); + + bool visitGuardShape(LGuardShape *guard); + bool visitGuardObjectType(LGuardObjectType *guard); + bool visitGuardClass(LGuardClass *guard); + bool visitImplicitThis(LImplicitThis *lir); + + bool visitInterruptCheck(LInterruptCheck *lir); + + bool visitNegI(LNegI *lir); + bool visitNegD(LNegD *lir); + bool visitNegF(LNegF *lir); + bool visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins); + bool visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins); + bool visitAsmJSLoadHeap(LAsmJSLoadHeap *ins); + bool visitAsmJSStoreHeap(LAsmJSStoreHeap *ins); + bool visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins); + bool visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins); + bool visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins); + bool visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins); + + bool visitAsmJSPassStackArg(LAsmJSPassStackArg *ins); + + bool visitForkJoinGetSlice(LForkJoinGetSlice *ins); + + bool generateInvalidateEpilogue(); + protected: + void postAsmJSCall(LAsmJSCall *lir) {} + + bool visitEffectiveAddress(LEffectiveAddress *ins); + bool visitUDiv(LUDiv *ins); + bool visitUMod(LUMod *ins); +}; + +typedef CodeGeneratorMIPS CodeGeneratorSpecific; + +// An out-of-line bailout thunk. +class OutOfLineBailout : public OutOfLineCodeBase<CodeGeneratorMIPS> +{ + LSnapshot *snapshot_; + uint32_t frameSize_; + + public: + OutOfLineBailout(LSnapshot *snapshot, uint32_t frameSize) + : snapshot_(snapshot), + frameSize_(frameSize) + { } + + bool accept(CodeGeneratorMIPS *codegen); + + LSnapshot *snapshot() const { + return snapshot_; + } +}; + +} // namespace jit +} // namespace js + +#endif /* jit_mips_CodeGenerator_mips_h */
diff --git a/src/third_party/mozjs/js/src/jit/mips/LIR-mips.h b/src/third_party/mozjs/js/src/jit/mips/LIR-mips.h new file mode 100644 index 0000000..7a7d51c --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/LIR-mips.h
@@ -0,0 +1,404 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_mips_LIR_mips_h +#define jit_mips_LIR_mips_h + +namespace js { +namespace jit { + +class LBox : public LInstructionHelper<2, 1, 0> +{ + MIRType type_; + + public: + LIR_HEADER(Box); + + LBox(const LAllocation &in_payload, MIRType type) + : type_(type) + { + setOperand(0, in_payload); + } + + MIRType type() const { + return type_; + } + const char *extraName() const { + return StringFromMIRType(type_); + } +}; + +class LBoxFloatingPoint : public LInstructionHelper<2, 1, 1> +{ + MIRType type_; + + public: + LIR_HEADER(BoxFloatingPoint); + + LBoxFloatingPoint(const LAllocation &in, const LDefinition &temp, MIRType type) + : type_(type) + { + setOperand(0, in); + setTemp(0, temp); + } + + MIRType type() const { + return type_; + } + const char *extraName() const { + return StringFromMIRType(type_); + } +}; + +class LUnbox : public LInstructionHelper<1, 2, 0> +{ + public: + LIR_HEADER(Unbox); + + MUnbox *mir() const { + return mir_->toUnbox(); + } + const LAllocation *payload() { + return getOperand(0); + } + const LAllocation *type() { + return getOperand(1); + } + const char *extraName() const { + return StringFromMIRType(mir()->type()); + } +}; + +class LUnboxFloatingPoint : public LInstructionHelper<1, 2, 0> +{ + MIRType type_; + + public: + LIR_HEADER(UnboxFloatingPoint); + + static const size_t Input = 0; + + LUnboxFloatingPoint(MIRType type) + : type_(type) + { } + + MUnbox *mir() const { + return mir_->toUnbox(); + } + + MIRType type() const { + return type_; + } + const char *extraName() const { + return StringFromMIRType(type_); + } +}; + +// Convert a 32-bit unsigned integer to a double. +class LAsmJSUInt32ToDouble : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(AsmJSUInt32ToDouble) + + LAsmJSUInt32ToDouble(const LAllocation &input) { + setOperand(0, input); + } +}; + +// Convert a 32-bit unsigned integer to a float32. +class LAsmJSUInt32ToFloat32 : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(AsmJSUInt32ToFloat32) + + LAsmJSUInt32ToFloat32(const LAllocation &input) { + setOperand(0, input); + } +}; + + +class LDivI : public LBinaryMath<1> +{ + public: + LIR_HEADER(DivI); + + LDivI(const LAllocation &lhs, const LAllocation &rhs, + const LDefinition &temp) { + setOperand(0, lhs); + setOperand(1, rhs); + setTemp(0, temp); + } + + MDiv *mir() const { + return mir_->toDiv(); + } +}; + +class LDivPowTwoI : public LInstructionHelper<1, 1, 1> +{ + const int32_t shift_; + + public: + LIR_HEADER(DivPowTwoI) + + LDivPowTwoI(const LAllocation &lhs, int32_t shift, const LDefinition &temp) + : shift_(shift) + { + setOperand(0, lhs); + setTemp(0, temp); + } + + const LAllocation *numerator() { + return getOperand(0); + } + + int32_t shift() { + return shift_; + } + + MDiv *mir() const { + return mir_->toDiv(); + } +}; + +class LModI : public LBinaryMath<1> +{ + public: + LIR_HEADER(ModI); + + LModI(const LAllocation &lhs, const LAllocation &rhs, + const LDefinition &callTemp) + { + setOperand(0, lhs); + setOperand(1, rhs); + setTemp(0, callTemp); + } + + const LDefinition *callTemp() { + return getTemp(0); + } + + MMod *mir() const { + return mir_->toMod(); + } +}; + +class LModPowTwoI : public LInstructionHelper<1, 1, 0> +{ + const int32_t shift_; + + public: + LIR_HEADER(ModPowTwoI); + int32_t shift() + { + return shift_; + } + + LModPowTwoI(const LAllocation &lhs, int32_t shift) + : shift_(shift) + { + setOperand(0, lhs); + } + + MMod *mir() const { + return mir_->toMod(); + } +}; + +class LModMaskI : public LInstructionHelper<1, 1, 1> +{ + const int32_t shift_; + + public: + LIR_HEADER(ModMaskI); + + LModMaskI(const LAllocation &lhs, const LDefinition &temp1, int32_t shift) + : shift_(shift) + { + setOperand(0, lhs); + setTemp(0, temp1); + } + + int32_t shift() const { + return shift_; + } + + MMod *mir() const { + return mir_->toMod(); + } +}; + +class LPowHalfD : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(PowHalfD); + LPowHalfD(const LAllocation &input) { + setOperand(0, input); + } + + const LAllocation *input() { + return getOperand(0); + } + const LDefinition *output() { + return getDef(0); + } +}; + +// Takes a tableswitch with an integer to decide +class LTableSwitch : public LInstructionHelper<0, 1, 2> +{ + public: + LIR_HEADER(TableSwitch); + + LTableSwitch(const LAllocation &in, const LDefinition &inputCopy, + const LDefinition &jumpTablePointer, MTableSwitch *ins) { + setOperand(0, in); + setTemp(0, inputCopy); + setTemp(1, jumpTablePointer); + setMir(ins); + } + + MTableSwitch *mir() const { + return mir_->toTableSwitch(); + } + + const LAllocation *index() { + return getOperand(0); + } + const LDefinition *tempInt() { + return getTemp(0); + } + // This is added to share the same CodeGenerator prefixes. + const LDefinition *tempPointer() { + return getTemp(1); + } +}; + +// Takes a tableswitch with an integer to decide +class LTableSwitchV : public LInstructionHelper<0, BOX_PIECES, 3> +{ + public: + LIR_HEADER(TableSwitchV); + + LTableSwitchV(const LDefinition &inputCopy, const LDefinition &floatCopy, + const LDefinition &jumpTablePointer, MTableSwitch *ins) + { + setTemp(0, inputCopy); + setTemp(1, floatCopy); + setTemp(2, jumpTablePointer); + setMir(ins); + } + + MTableSwitch *mir() const { + return mir_->toTableSwitch(); + } + + static const size_t InputValue = 0; + + const LDefinition *tempInt() { + return getTemp(0); + } + const LDefinition *tempFloat() { + return getTemp(1); + } + const LDefinition *tempPointer() { + return getTemp(2); + } +}; + +class LGuardShape : public LInstructionHelper<0, 1, 1> +{ + public: + LIR_HEADER(GuardShape); + + LGuardShape(const LAllocation &in, const LDefinition &temp) { + setOperand(0, in); + setTemp(0, temp); + } + const MGuardShape *mir() const { + return mir_->toGuardShape(); + } + const LDefinition *tempInt() { + return getTemp(0); + } +}; + +class LGuardObjectType : public LInstructionHelper<0, 1, 1> +{ + public: + LIR_HEADER(GuardObjectType); + + LGuardObjectType(const LAllocation &in, const LDefinition &temp) { + setOperand(0, in); + setTemp(0, temp); + } + const MGuardObjectType *mir() const { + return mir_->toGuardObjectType(); + } + const LDefinition *tempInt() { + return getTemp(0); + } +}; + +class LInterruptCheck : public LInstructionHelper<0, 0, 0> +{ + public: + LIR_HEADER(InterruptCheck); +}; + +class LMulI : public LBinaryMath<0> +{ + public: + LIR_HEADER(MulI); + + MMul *mir() { + return mir_->toMul(); + } +}; + +class LUDiv : public LBinaryMath<0> +{ + public: + LIR_HEADER(UDiv); + + MDiv *mir() { + return mir_->toDiv(); + } +}; + +class LUMod : public LBinaryMath<0> +{ + public: + LIR_HEADER(UMod); + + MMod *mir() { + return mir_->toMod(); + } +}; + +class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 1> +{ + public: + LIR_HEADER(AsmJSLoadFuncPtr); + LAsmJSLoadFuncPtr(const LAllocation &index, const LDefinition &temp) { + setOperand(0, index); + setTemp(0, temp); + } + const MAsmJSLoadFuncPtr *mir() const { + return mir_->toAsmJSLoadFuncPtr(); + } + const LAllocation *index() { + return getOperand(0); + } + const LDefinition *temp() { + return getTemp(0); + } +}; + +} // namespace jit +} // namespace js + +#endif /* jit_mips_LIR_mips_h */
diff --git a/src/third_party/mozjs/js/src/jit/mips/LOpcodes-mips.h b/src/third_party/mozjs/js/src/jit/mips/LOpcodes-mips.h new file mode 100644 index 0000000..cb2aa37 --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/LOpcodes-mips.h
@@ -0,0 +1,27 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_mips_LOpcodes_mips_h__ +#define jit_mips_LOpcodes_mips_h__ + +#define LIR_CPU_OPCODE_LIST(_) \ + _(Unbox) \ + _(UnboxFloatingPoint) \ + _(Box) \ + _(BoxFloatingPoint) \ + _(DivI) \ + _(DivPowTwoI) \ + _(ModI) \ + _(ModPowTwoI) \ + _(ModMaskI) \ + _(PowHalfD) \ + _(AsmJSUInt32ToDouble) \ + _(AsmJSUInt32ToFloat32) \ + _(UDiv) \ + _(UMod) \ + _(AsmJSLoadFuncPtr) + +#endif // jit_mips_LOpcodes_mips_h__
diff --git a/src/third_party/mozjs/js/src/jit/mips/Lowering-mips.cpp b/src/third_party/mozjs/js/src/jit/mips/Lowering-mips.cpp new file mode 100644 index 0000000..6f84a5f --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/Lowering-mips.cpp
@@ -0,0 +1,531 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/MathAlgorithms.h" + + +#include "jit/Lowering.h" +#include "jit/mips/Assembler-mips.h" +#include "jit/MIR.h" + +#include "jit/shared/Lowering-shared-inl.h" + +using namespace js; +using namespace js::jit; + +using mozilla::FloorLog2; + +bool +LIRGeneratorMIPS::useBox(LInstruction *lir, size_t n, MDefinition *mir, + LUse::Policy policy, bool useAtStart) +{ + MOZ_ASSERT(mir->type() == MIRType_Value); + + if (!ensureDefined(mir)) + return false; + lir->setOperand(n, LUse(mir->virtualRegister(), policy, useAtStart)); + lir->setOperand(n + 1, LUse(VirtualRegisterOfPayload(mir), policy, useAtStart)); + return true; +} + +bool +LIRGeneratorMIPS::useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, + Register reg2) +{ + MOZ_ASSERT(mir->type() == MIRType_Value); + MOZ_ASSERT(reg1 != reg2); + + if (!ensureDefined(mir)) + return false; + lir->setOperand(n, LUse(reg1, mir->virtualRegister())); + lir->setOperand(n + 1, LUse(reg2, VirtualRegisterOfPayload(mir))); + return true; +} + +LAllocation +LIRGeneratorMIPS::useByteOpRegister(MDefinition *mir) +{ + return useRegister(mir); +} + +LAllocation +LIRGeneratorMIPS::useByteOpRegisterOrNonDoubleConstant(MDefinition *mir) +{ + return useRegisterOrNonDoubleConstant(mir); +} + +bool +LIRGeneratorMIPS::lowerConstantDouble(double d, MInstruction *mir) +{ + return define(new(alloc()) LDouble(d), mir); +} + +bool +LIRGeneratorMIPS::lowerConstantFloat32(float d, MInstruction *mir) +{ + return define(new(alloc()) LFloat32(d), mir); +} + +bool +LIRGeneratorMIPS::visitConstant(MConstant *ins) +{ + if (ins->type() == MIRType_Double) + return lowerConstantDouble(ins->value().toDouble(), ins); + + if (ins->type() == MIRType_Float32) + return lowerConstantFloat32(ins->value().toDouble(), ins); + + // Emit non-double constants at their uses. + if (ins->canEmitAtUses()) + return emitAtUses(ins); + + return LIRGeneratorShared::visitConstant(ins); +} + +bool +LIRGeneratorMIPS::visitBox(MBox *box) +{ + MDefinition *inner = box->getOperand(0); + + // If the box wrapped a double, it needs a new register. + if (IsFloatingPointType(inner->type())) + return defineBox(new(alloc()) LBoxFloatingPoint(useRegisterAtStart(inner), + tempCopy(inner, 0), inner->type()), box); + + if (box->canEmitAtUses()) + return emitAtUses(box); + + if (inner->isConstant()) + return defineBox(new(alloc()) LValue(inner->toConstant()->value()), box); + + LBox *lir = new(alloc()) LBox(use(inner), inner->type()); + + // Otherwise, we should not define a new register for the payload portion + // of the output, so bypass defineBox(). + uint32_t vreg = getVirtualRegister(); + if (vreg >= MAX_VIRTUAL_REGISTERS) + return false; + + // Note that because we're using PASSTHROUGH, we do not change the type of + // the definition. We also do not define the first output as "TYPE", + // because it has no corresponding payload at (vreg + 1). Also note that + // although we copy the input's original type for the payload half of the + // definition, this is only for clarity. PASSTHROUGH definitions are + // ignored. + lir->setDef(0, LDefinition(vreg, LDefinition::GENERAL)); + lir->setDef(1, LDefinition(inner->virtualRegister(), LDefinition::TypeFrom(inner->type()), + LDefinition::PASSTHROUGH)); + box->setVirtualRegister(vreg); + return add(lir); +} + +bool +LIRGeneratorMIPS::visitUnbox(MUnbox *unbox) +{ + // An unbox on mips reads in a type tag (either in memory or a register) and + // a payload. Unlike most instructions consuming a box, we ask for the type + // second, so that the result can re-use the first input. + MDefinition *inner = unbox->getOperand(0); + + if (!ensureDefined(inner)) + return false; + + if (IsFloatingPointType(unbox->type())) { + LUnboxFloatingPoint *lir = new(alloc()) LUnboxFloatingPoint(unbox->type()); + if (unbox->fallible() && !assignSnapshot(lir, unbox->bailoutKind())) + return false; + if (!useBox(lir, LUnboxFloatingPoint::Input, inner)) + return false; + return define(lir, unbox); + } + + // Swap the order we use the box pieces so we can re-use the payload + // register. + LUnbox *lir = new(alloc()) LUnbox; + lir->setOperand(0, usePayloadInRegisterAtStart(inner)); + lir->setOperand(1, useType(inner, LUse::REGISTER)); + + if (unbox->fallible() && !assignSnapshot(lir, unbox->bailoutKind())) + return false; + + // Note that PASSTHROUGH here is illegal, since types and payloads form two + // separate intervals. If the type becomes dead before the payload, it + // could be used as a Value without the type being recoverable. Unbox's + // purpose is to eagerly kill the definition of a type tag, so keeping both + // alive (for the purpose of gcmaps) is unappealing. Instead, we create a + // new virtual register. + return defineReuseInput(lir, unbox, 0); +} + +bool +LIRGeneratorMIPS::visitReturn(MReturn *ret) +{ + MDefinition *opd = ret->getOperand(0); + MOZ_ASSERT(opd->type() == MIRType_Value); + + LReturn *ins = new(alloc()) LReturn; + ins->setOperand(0, LUse(JSReturnReg_Type)); + ins->setOperand(1, LUse(JSReturnReg_Data)); + return fillBoxUses(ins, 0, opd) && add(ins); +} + +// x = !y +bool +LIRGeneratorMIPS::lowerForALU(LInstructionHelper<1, 1, 0> *ins, + MDefinition *mir, MDefinition *input) +{ + ins->setOperand(0, useRegister(input)); + return define(ins, mir, + LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::DEFAULT)); +} + +// z = x+y +bool +LIRGeneratorMIPS::lowerForALU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, + MDefinition *lhs, MDefinition *rhs) +{ + ins->setOperand(0, useRegister(lhs)); + ins->setOperand(1, useRegisterOrConstant(rhs)); + return define(ins, mir, + LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::DEFAULT)); +} + +bool +LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir, + MDefinition *input) +{ + ins->setOperand(0, useRegister(input)); + return define(ins, mir, + LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::DEFAULT)); +} + +bool +LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, + MDefinition *lhs, MDefinition *rhs) +{ + ins->setOperand(0, useRegister(lhs)); + ins->setOperand(1, useRegister(rhs)); + return define(ins, mir, + LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::DEFAULT)); +} + +bool +LIRGeneratorMIPS::lowerForBitAndAndBranch(LBitAndAndBranch *baab, MInstruction *mir, + MDefinition *lhs, MDefinition *rhs) +{ + baab->setOperand(0, useRegisterAtStart(lhs)); + baab->setOperand(1, useRegisterOrConstantAtStart(rhs)); + return add(baab, mir); +} + +bool +LIRGeneratorMIPS::defineUntypedPhi(MPhi *phi, size_t lirIndex) +{ + LPhi *type = current->getPhi(lirIndex + VREG_TYPE_OFFSET); + LPhi *payload = current->getPhi(lirIndex + VREG_DATA_OFFSET); + + uint32_t typeVreg = getVirtualRegister(); + if (typeVreg >= MAX_VIRTUAL_REGISTERS) + return false; + + phi->setVirtualRegister(typeVreg); + + uint32_t payloadVreg = getVirtualRegister(); + if (payloadVreg >= MAX_VIRTUAL_REGISTERS) + return false; + MOZ_ASSERT(typeVreg + 1 == payloadVreg); + + type->setDef(0, LDefinition(typeVreg, LDefinition::TYPE)); + payload->setDef(0, LDefinition(payloadVreg, LDefinition::PAYLOAD)); + annotate(type); + annotate(payload); + return true; +} + +void +LIRGeneratorMIPS::lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, + LBlock *block, size_t lirIndex) +{ + MDefinition *operand = phi->getOperand(inputPosition); + LPhi *type = block->getPhi(lirIndex + VREG_TYPE_OFFSET); + LPhi *payload = block->getPhi(lirIndex + VREG_DATA_OFFSET); + type->setOperand(inputPosition, LUse(operand->virtualRegister() + VREG_TYPE_OFFSET, + LUse::ANY)); + payload->setOperand(inputPosition, LUse(VirtualRegisterOfPayload(operand), LUse::ANY)); +} + +bool +LIRGeneratorMIPS::lowerForShift(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, + MDefinition *lhs, MDefinition *rhs) +{ + ins->setOperand(0, useRegister(lhs)); + ins->setOperand(1, useRegisterOrConstant(rhs)); + return define(ins, mir); +} + +bool +LIRGeneratorMIPS::lowerDivI(MDiv *div) +{ + if (div->isUnsigned()) + return lowerUDiv(div); + + // Division instructions are slow. Division by constant denominators can be + // rewritten to use other instructions. + if (div->rhs()->isConstant()) { + int32_t rhs = div->rhs()->toConstant()->value().toInt32(); + // Check for division by a positive power of two, which is an easy and + // important case to optimize. Note that other optimizations are also + // possible; division by negative powers of two can be optimized in a + // similar manner as positive powers of two, and division by other + // constants can be optimized by a reciprocal multiplication technique. + int32_t shift = FloorLog2(rhs); + if (rhs > 0 && 1 << shift == rhs) { + LDivPowTwoI *lir = new(alloc()) LDivPowTwoI(useRegister(div->lhs()), shift, temp()); + if (div->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) + return false; + return define(lir, div); + } + } + + LDivI *lir = new(alloc()) LDivI(useRegister(div->lhs()), useRegister(div->rhs()), temp()); + if (div->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) + return false; + return define(lir, div); +} + +bool +LIRGeneratorMIPS::lowerMulI(MMul *mul, MDefinition *lhs, MDefinition *rhs) +{ + LMulI *lir = new(alloc()) LMulI; + if (mul->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) + return false; + + return lowerForALU(lir, mul, lhs, rhs); +} + +bool +LIRGeneratorMIPS::lowerModI(MMod *mod) +{ + if (mod->isUnsigned()) + return lowerUMod(mod); + + if (mod->rhs()->isConstant()) { + int32_t rhs = mod->rhs()->toConstant()->value().toInt32(); + int32_t shift = FloorLog2(rhs); + if (rhs > 0 && 1 << shift == rhs) { + LModPowTwoI *lir = new(alloc()) LModPowTwoI(useRegister(mod->lhs()), shift); + if (mod->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) + return false; + return define(lir, mod); + } else if (shift < 31 && (1 << (shift + 1)) - 1 == rhs) { + LModMaskI *lir = new(alloc()) LModMaskI(useRegister(mod->lhs()), + temp(LDefinition::GENERAL), shift + 1); + if (mod->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) + return false; + return define(lir, mod); + } + } + LModI *lir = new(alloc()) LModI(useRegister(mod->lhs()), useRegister(mod->rhs()), + temp(LDefinition::GENERAL)); + + if (mod->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) + return false; + return define(lir, mod); +} + +bool +LIRGeneratorMIPS::visitPowHalf(MPowHalf *ins) +{ + MDefinition *input = ins->input(); + MOZ_ASSERT(input->type() == MIRType_Double); + LPowHalfD *lir = new(alloc()) LPowHalfD(useRegisterAtStart(input)); + return defineReuseInput(lir, ins, 0); +} + +LTableSwitch * +LIRGeneratorMIPS::newLTableSwitch(const LAllocation &in, const LDefinition &inputCopy, + MTableSwitch *tableswitch) +{ + return new(alloc()) LTableSwitch(in, inputCopy, temp(), tableswitch); +} + +LTableSwitchV * +LIRGeneratorMIPS::newLTableSwitchV(MTableSwitch *tableswitch) +{ + return new(alloc()) LTableSwitchV(temp(), tempFloat32(), temp(), tableswitch); +} + +bool +LIRGeneratorMIPS::visitGuardShape(MGuardShape *ins) +{ + MOZ_ASSERT(ins->obj()->type() == MIRType_Object); + + LDefinition tempObj = temp(LDefinition::OBJECT); + LGuardShape *guard = new(alloc()) LGuardShape(useRegister(ins->obj()), tempObj); + if (!assignSnapshot(guard, ins->bailoutKind())) + return false; + if (!add(guard, ins)) + return false; + return redefine(ins, ins->obj()); +} + +bool +LIRGeneratorMIPS::visitGuardObjectType(MGuardObjectType *ins) +{ + MOZ_ASSERT(ins->obj()->type() == MIRType_Object); + + LDefinition tempObj = temp(LDefinition::OBJECT); + LGuardObjectType *guard = new(alloc()) LGuardObjectType(useRegister(ins->obj()), tempObj); + if (!assignSnapshot(guard)) + return false; + if (!add(guard, ins)) + return false; + return redefine(ins, ins->obj()); +} + +bool +LIRGeneratorMIPS::lowerUrshD(MUrsh *mir) +{ + MDefinition *lhs = mir->lhs(); + MDefinition *rhs = mir->rhs(); + + MOZ_ASSERT(lhs->type() == MIRType_Int32); + MOZ_ASSERT(rhs->type() == MIRType_Int32); + + LUrshD *lir = new(alloc()) LUrshD(useRegister(lhs), useRegisterOrConstant(rhs), temp()); + return define(lir, mir); +} + +bool +LIRGeneratorMIPS::visitAsmJSNeg(MAsmJSNeg *ins) +{ + if (ins->type() == MIRType_Int32) + return define(new(alloc()) LNegI(useRegisterAtStart(ins->input())), ins); + + if (ins->type() == MIRType_Float32) + return define(new(alloc()) LNegF(useRegisterAtStart(ins->input())), ins); + + MOZ_ASSERT(ins->type() == MIRType_Double); + return define(new(alloc()) LNegD(useRegisterAtStart(ins->input())), ins); +} + +bool +LIRGeneratorMIPS::lowerUDiv(MDiv *div) +{ + MDefinition *lhs = div->getOperand(0); + MDefinition *rhs = div->getOperand(1); + + LUDiv *lir = new(alloc()) LUDiv; + lir->setOperand(0, useRegister(lhs)); + lir->setOperand(1, useRegister(rhs)); + if (div->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) + return false; + + return define(lir, div); +} + +bool +LIRGeneratorMIPS::lowerUMod(MMod *mod) +{ + MDefinition *lhs = mod->getOperand(0); + MDefinition *rhs = mod->getOperand(1); + + LUMod *lir = new(alloc()) LUMod; + lir->setOperand(0, useRegister(lhs)); + lir->setOperand(1, useRegister(rhs)); + if (mod->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) + return false; + + return define(lir, mod); +} + +bool +LIRGeneratorMIPS::visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins) +{ + MOZ_ASSERT(ins->input()->type() == MIRType_Int32); + LAsmJSUInt32ToDouble *lir = new(alloc()) LAsmJSUInt32ToDouble(useRegisterAtStart(ins->input())); + return define(lir, ins); +} + +bool +LIRGeneratorMIPS::visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32 *ins) +{ + MOZ_ASSERT(ins->input()->type() == MIRType_Int32); + LAsmJSUInt32ToFloat32 *lir = new(alloc()) LAsmJSUInt32ToFloat32(useRegisterAtStart(ins->input())); + return define(lir, ins); +} + +bool +LIRGeneratorMIPS::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins) +{ + MDefinition *ptr = ins->ptr(); + MOZ_ASSERT(ptr->type() == MIRType_Int32); + LAllocation ptrAlloc; + + // For MIPS it is best to keep the 'ptr' in a register if a bounds check + // is needed. + if (ptr->isConstant() && ins->skipBoundsCheck()) { + int32_t ptrValue = ptr->toConstant()->value().toInt32(); + // A bounds check is only skipped for a positive index. + MOZ_ASSERT(ptrValue >= 0); + ptrAlloc = LAllocation(ptr->toConstant()->vp()); + } else + ptrAlloc = useRegisterAtStart(ptr); + + return define(new(alloc()) LAsmJSLoadHeap(ptrAlloc), ins); +} + +bool +LIRGeneratorMIPS::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins) +{ + MDefinition *ptr = ins->ptr(); + MOZ_ASSERT(ptr->type() == MIRType_Int32); + LAllocation ptrAlloc; + + if (ptr->isConstant() && ins->skipBoundsCheck()) { + MOZ_ASSERT(ptr->toConstant()->value().toInt32() >= 0); + ptrAlloc = LAllocation(ptr->toConstant()->vp()); + } else + ptrAlloc = useRegisterAtStart(ptr); + + return add(new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value())), ins); +} + +bool +LIRGeneratorMIPS::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins) +{ + return define(new(alloc()) LAsmJSLoadFuncPtr(useRegister(ins->index()), temp()), ins); +} + +bool +LIRGeneratorMIPS::lowerTruncateDToInt32(MTruncateToInt32 *ins) +{ + MDefinition *opd = ins->input(); + MOZ_ASSERT(opd->type() == MIRType_Double); + + return define(new(alloc()) LTruncateDToInt32(useRegister(opd), LDefinition::BogusTemp()), ins); +} + +bool +LIRGeneratorMIPS::lowerTruncateFToInt32(MTruncateToInt32 *ins) +{ + MDefinition *opd = ins->input(); + MOZ_ASSERT(opd->type() == MIRType_Float32); + + return define(new(alloc()) LTruncateFToInt32(useRegister(opd), LDefinition::BogusTemp()), ins); +} + +bool +LIRGeneratorMIPS::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins) +{ + MOZ_ASSUME_UNREACHABLE("NYI"); +} + +bool +LIRGeneratorMIPS::visitForkJoinGetSlice(MForkJoinGetSlice *ins) +{ + MOZ_ASSUME_UNREACHABLE("NYI"); +}
diff --git a/src/third_party/mozjs/js/src/jit/mips/Lowering-mips.h b/src/third_party/mozjs/js/src/jit/mips/Lowering-mips.h new file mode 100644 index 0000000..0deab80 --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/Lowering-mips.h
@@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_mips_Lowering_mips_h +#define jit_mips_Lowering_mips_h + +#include "jit/shared/Lowering-shared.h" + +namespace js { +namespace jit { + +class LIRGeneratorMIPS : public LIRGeneratorShared +{ + protected: + LIRGeneratorMIPS(MIRGenerator *gen, MIRGraph &graph, LIRGraph &lirGraph) + : LIRGeneratorShared(gen, graph, lirGraph) + { } + + protected: + // Adds a box input to an instruction, setting operand |n| to the type and + // |n+1| to the payload. + bool useBox(LInstruction *lir, size_t n, MDefinition *mir, + LUse::Policy policy = LUse::REGISTER, bool useAtStart = false); + bool useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register reg2); + + // x86 has constraints on what registers can be formatted for 1-byte + // stores and loads; on MIPS all registers are okay. + LAllocation useByteOpRegister(MDefinition *mir); + LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir); + + inline LDefinition tempToUnbox() { + return LDefinition::BogusTemp(); + } + + // MIPS has a scratch register, so no need for another temp for dispatch + // ICs. + LDefinition tempForDispatchCache(MIRType outputType = MIRType_None) { + return LDefinition::BogusTemp(); + } + + void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex); + bool defineUntypedPhi(MPhi *phi, size_t lirIndex); + bool lowerForShift(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs, + MDefinition *rhs); + bool lowerUrshD(MUrsh *mir); + + bool lowerForALU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir, + MDefinition *input); + bool lowerForALU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, + MDefinition *lhs, MDefinition *rhs); + + bool lowerForFPU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir, + MDefinition *src); + bool lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, + MDefinition *lhs, MDefinition *rhs); + bool lowerForBitAndAndBranch(LBitAndAndBranch *baab, MInstruction *mir, + MDefinition *lhs, MDefinition *rhs); + bool lowerConstantDouble(double d, MInstruction *ins); + bool lowerConstantFloat32(float d, MInstruction *ins); + bool lowerTruncateDToInt32(MTruncateToInt32 *ins); + bool lowerTruncateFToInt32(MTruncateToInt32 *ins); + bool lowerDivI(MDiv *div); + bool lowerModI(MMod *mod); + bool lowerMulI(MMul *mul, MDefinition *lhs, MDefinition *rhs); + bool lowerUDiv(MDiv *div); + bool lowerUMod(MMod *mod); + bool visitPowHalf(MPowHalf *ins); + bool visitAsmJSNeg(MAsmJSNeg *ins); + + LTableSwitch *newLTableSwitch(const LAllocation &in, const LDefinition &inputCopy, + MTableSwitch *ins); + LTableSwitchV *newLTableSwitchV(MTableSwitch *ins); + + public: + bool visitConstant(MConstant *ins); + bool visitBox(MBox *box); + bool visitUnbox(MUnbox *unbox); + bool visitReturn(MReturn *ret); + bool lowerPhi(MPhi *phi); + bool visitGuardShape(MGuardShape *ins); + bool visitGuardObjectType(MGuardObjectType *ins); + bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins); + bool visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32 *ins); + bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins); + bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins); + bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins); + bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins); + bool visitForkJoinGetSlice(MForkJoinGetSlice *ins); + + static bool allowFloat32Optimizations() { + return true; + } +}; + +typedef LIRGeneratorMIPS LIRGeneratorSpecific; + +} // namespace jit +} // namespace js + +#endif /* jit_mips_Lowering_mips_h */
diff --git a/src/third_party/mozjs/js/src/jit/mips/MacroAssembler-mips.cpp b/src/third_party/mozjs/js/src/jit/mips/MacroAssembler-mips.cpp new file mode 100644 index 0000000..cee93a5 --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/MacroAssembler-mips.cpp
@@ -0,0 +1,3273 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/mips/MacroAssembler-mips.h" + +#include "mozilla/DebugOnly.h" +#include "mozilla/MathAlgorithms.h" + +#include "jit/Bailouts.h" +#include "jit/BaselineFrame.h" +#include "jit/BaselineRegisters.h" +#include "jit/IonFrames.h" +#include "jit/MoveEmitter.h" + +using namespace js; +using namespace jit; + +using mozilla::Abs; + +static const int32_t PAYLOAD_OFFSET = NUNBOX32_PAYLOAD_OFFSET; +static const int32_t TAG_OFFSET = NUNBOX32_TYPE_OFFSET; + +static_assert(sizeof(intptr_t) == 4, "Not 64-bit clean."); + +void +MacroAssemblerMIPS::convertBoolToInt32(Register src, Register dest) +{ + // Note that C++ bool is only 1 byte, so zero extend it to clear the + // higher-order bits. + ma_and(dest, src, Imm32(0xff)); +} + +void +MacroAssemblerMIPS::convertInt32ToDouble(const Register &src, const FloatRegister &dest) +{ + as_mtc1(src, dest); + as_cvtdw(dest, dest); +} + +void +MacroAssemblerMIPS::convertInt32ToDouble(const Address &src, FloatRegister dest) +{ + ma_lw(ScratchRegister, src); + as_mtc1(ScratchRegister, dest); + as_cvtdw(dest, dest); +} + +void +MacroAssemblerMIPS::convertUInt32ToDouble(const Register &src, const FloatRegister &dest) +{ + // We use SecondScratchFloatReg because MacroAssembler::loadFromTypedArray + // calls with ScratchFloatReg as dest. + MOZ_ASSERT(dest != SecondScratchFloatReg); + + // Subtract INT32_MIN to get a positive number + ma_subu(ScratchRegister, src, Imm32(INT32_MIN)); + + // Convert value + as_mtc1(ScratchRegister, dest); + as_cvtdw(dest, dest); + + // Add unsigned value of INT32_MIN + ma_lid(SecondScratchFloatReg, 2147483648.0); + as_addd(dest, dest, SecondScratchFloatReg); +} + +void +MacroAssemblerMIPS::convertUInt32ToFloat32(const Register &src, const FloatRegister &dest) +{ + MOZ_ASSUME_UNREACHABLE("NYI"); +} + +void +MacroAssemblerMIPS::convertDoubleToFloat32(const FloatRegister &src, const FloatRegister &dest) +{ + as_cvtsd(dest, src); +} + +// Convert the floating point value to an integer, if it did not fit, then it +// was clamped to INT32_MIN/INT32_MAX, and we can test it. +// NOTE: if the value really was supposed to be INT32_MAX / INT32_MIN then it +// will be wrong. +void +MacroAssemblerMIPS::branchTruncateDouble(const FloatRegister &src, const Register &dest, + Label *fail) +{ + Label test, success; + as_truncwd(ScratchFloatReg, src); + as_mfc1(dest, ScratchFloatReg); + + ma_b(dest, Imm32(INT32_MAX), fail, Assembler::Equal); +} + +// Checks whether a double is representable as a 32-bit integer. If so, the +// integer is written to the output register. Otherwise, a bailout is taken to +// the given snapshot. This function overwrites the scratch float register. +void +MacroAssemblerMIPS::convertDoubleToInt32(const FloatRegister &src, const Register &dest, + Label *fail, bool negativeZeroCheck) +{ + // Convert double to int, then convert back and check if we have the + // same number. + as_cvtwd(ScratchFloatReg, src); + as_mfc1(dest, ScratchFloatReg); + as_cvtdw(ScratchFloatReg, ScratchFloatReg); + ma_bc1d(src, ScratchFloatReg, fail, Assembler::DoubleNotEqualOrUnordered); + + if (negativeZeroCheck) { + Label notZero; + ma_b(dest, Imm32(0), ¬Zero, Assembler::NotEqual, ShortJump); + // Test and bail for -0.0, when integer result is 0 + // Move the top word of the double into the output reg, if it is + // non-zero, then the original value was -0.0 + moveFromDoubleHi(src, dest); + ma_b(dest, Imm32(INT32_MIN), fail, Assembler::Equal); + bind(¬Zero); + } +} + +// Checks whether a float32 is representable as a 32-bit integer. If so, the +// integer is written to the output register. Otherwise, a bailout is taken to +// the given snapshot. This function overwrites the scratch float register. +void +MacroAssemblerMIPS::convertFloat32ToInt32(const FloatRegister &src, const Register &dest, + Label *fail, bool negativeZeroCheck) +{ + // convert the floating point value to an integer, if it did not fit, then + // when we convert it *back* to a float, it will have a different value, + // which we can test. + as_cvtws(ScratchFloatReg, src); + as_mfc1(dest, ScratchFloatReg); + as_cvtsw(ScratchFloatReg, ScratchFloatReg); + ma_bc1s(src, ScratchFloatReg, fail, Assembler::DoubleNotEqualOrUnordered); + + if (negativeZeroCheck) { + Label notZero; + ma_b(dest, Imm32(0), ¬Zero, Assembler::NotEqual, ShortJump); + // Test and bail for -0.0, when integer result is 0 + // Move the top word of the double into the output reg, + // if it is non-zero, then the original value was -0.0 + moveFromDoubleHi(src, dest); + ma_b(dest, Imm32(INT32_MIN), fail, Assembler::Equal); + bind(¬Zero); + } +} + +void +MacroAssemblerMIPS::convertFloat32ToDouble(const FloatRegister &src, const FloatRegister &dest) +{ + as_cvtds(dest, src); +} + +void +MacroAssemblerMIPS::branchTruncateFloat32(const FloatRegister &src, const Register &dest, + Label *fail) +{ + Label test, success; + as_truncws(ScratchFloatReg, src); + as_mfc1(dest, ScratchFloatReg); + + ma_b(dest, Imm32(INT32_MAX), fail, Assembler::Equal); +} + +void +MacroAssemblerMIPS::convertInt32ToFloat32(const Register &src, const FloatRegister &dest) +{ + as_mtc1(src, dest); + as_cvtsw(dest, dest); +} + +void +MacroAssemblerMIPS::convertInt32ToFloat32(const Address &src, FloatRegister dest) +{ + ma_lw(ScratchRegister, src); + as_mtc1(ScratchRegister, dest); + as_cvtsw(dest, dest); +} + +void +MacroAssemblerMIPS::addDouble(FloatRegister src, FloatRegister dest) +{ + as_addd(dest, dest, src); +} + +void +MacroAssemblerMIPS::subDouble(FloatRegister src, FloatRegister dest) +{ + as_subd(dest, dest, src); +} + +void +MacroAssemblerMIPS::mulDouble(FloatRegister src, FloatRegister dest) +{ + as_muld(dest, dest, src); +} + +void +MacroAssemblerMIPS::divDouble(FloatRegister src, FloatRegister dest) +{ + as_divd(dest, dest, src); +} + +void +MacroAssemblerMIPS::negateDouble(FloatRegister reg) +{ + as_negd(reg, reg); +} + +void +MacroAssemblerMIPS::inc64(AbsoluteAddress dest) +{ + ma_li(ScratchRegister, Imm32((int32_t)dest.addr)); + as_lw(SecondScratchReg, ScratchRegister, 0); + + as_addiu(SecondScratchReg, SecondScratchReg, 1); + as_sw(SecondScratchReg, ScratchRegister, 0); + + as_sltiu(SecondScratchReg, SecondScratchReg, 1); + as_lw(ScratchRegister, ScratchRegister, 4); + + as_addu(SecondScratchReg, ScratchRegister, SecondScratchReg); + + ma_li(ScratchRegister, Imm32((int32_t)dest.addr)); + as_sw(SecondScratchReg, ScratchRegister, 4); +} + +void +MacroAssemblerMIPS::ma_move(Register rd, Register rs) +{ + as_or(rd, rs, zero); +} + +void +MacroAssemblerMIPS::ma_li(Register dest, const ImmGCPtr &ptr) +{ + writeDataRelocation(ptr); + ma_liPatchable(dest, Imm32(ptr.value)); +} + +void +MacroAssemblerMIPS::ma_li(const Register &dest, AbsoluteLabel *label) +{ + MOZ_ASSERT(!label->bound()); + // Thread the patch list through the unpatched address word in the + // instruction stream. + BufferOffset bo = m_buffer.nextOffset(); + ma_liPatchable(dest, Imm32(label->prev())); + label->setPrev(bo.getOffset()); +} + +void +MacroAssemblerMIPS::ma_li(Register dest, Imm32 imm) +{ + if (Imm16::isInSignedRange(imm.value)) { + as_addiu(dest, zero, imm.value); + } else if (Imm16::isInUnsignedRange(imm.value)) { + as_ori(dest, zero, Imm16::lower(imm).encode()); + } else if (Imm16::lower(imm).encode() == 0) { + as_lui(dest, Imm16::upper(imm).encode()); + } else { + as_lui(dest, Imm16::upper(imm).encode()); + as_ori(dest, dest, Imm16::lower(imm).encode()); + } +} + + +// This method generates lui and ori instruction pair that can be modified by +// updateLuiOriValue, either during compilation (eg. Assembler::bind), or +// during execution (eg. jit::PatchJump). +void +MacroAssemblerMIPS::ma_liPatchable(Register dest, Imm32 imm) +{ + m_buffer.ensureSpace(2 * sizeof(uint32_t)); + as_lui(dest, Imm16::upper(imm).encode()); + as_ori(dest, dest, Imm16::lower(imm).encode()); +} + +void +MacroAssemblerMIPS::ma_liPatchable(Register dest, ImmPtr imm) +{ + return ma_liPatchable(dest, Imm32(int32_t(imm.value))); +} + +// Shifts +void +MacroAssemblerMIPS::ma_sll(Register rd, Register rt, Imm32 shift) +{ + as_sll(rd, rt, shift.value % 32); +} +void +MacroAssemblerMIPS::ma_srl(Register rd, Register rt, Imm32 shift) +{ + as_srl(rd, rt, shift.value % 32); +} + +void +MacroAssemblerMIPS::ma_sra(Register rd, Register rt, Imm32 shift) +{ + as_sra(rd, rt, shift.value % 32); +} + +void +MacroAssemblerMIPS::ma_ror(Register rd, Register rt, Imm32 shift) +{ + as_rotr(rd, rt, shift.value % 32); +} + +void +MacroAssemblerMIPS::ma_rol(Register rd, Register rt, Imm32 shift) +{ + as_rotr(rd, rt, 32 - (shift.value % 32)); +} + +void +MacroAssemblerMIPS::ma_sll(Register rd, Register rt, Register shift) +{ + as_sllv(rd, rt, shift); +} + +void +MacroAssemblerMIPS::ma_srl(Register rd, Register rt, Register shift) +{ + as_srlv(rd, rt, shift); +} + +void +MacroAssemblerMIPS::ma_sra(Register rd, Register rt, Register shift) +{ + as_srav(rd, rt, shift); +} + +void +MacroAssemblerMIPS::ma_ror(Register rd, Register rt, Register shift) +{ + as_rotrv(rd, rt, shift); +} + +void +MacroAssemblerMIPS::ma_rol(Register rd, Register rt, Register shift) +{ + ma_negu(ScratchRegister, shift); + as_rotrv(rd, rt, ScratchRegister); +} + +void +MacroAssemblerMIPS::ma_negu(Register rd, Register rs) +{ + as_subu(rd, zero, rs); +} + +void +MacroAssemblerMIPS::ma_not(Register rd, Register rs) +{ + as_nor(rd, rs, zero); +} + +// And. +void +MacroAssemblerMIPS::ma_and(Register rd, Register rs) +{ + as_and(rd, rd, rs); +} + +void +MacroAssemblerMIPS::ma_and(Register rd, Register rs, Register rt) +{ + as_and(rd, rs, rt); +} + +void +MacroAssemblerMIPS::ma_and(Register rd, Imm32 imm) +{ + ma_and(rd, rd, imm); +} + +void +MacroAssemblerMIPS::ma_and(Register rd, Register rs, Imm32 imm) +{ + if (Imm16::isInUnsignedRange(imm.value)) { + as_andi(rd, rs, imm.value); + } else { + ma_li(ScratchRegister, imm); + as_and(rd, rs, ScratchRegister); + } +} + +// Or. +void +MacroAssemblerMIPS::ma_or(Register rd, Register rs) +{ + as_or(rd, rd, rs); +} + +void +MacroAssemblerMIPS::ma_or(Register rd, Register rs, Register rt) +{ + as_or(rd, rs, rt); +} + +void +MacroAssemblerMIPS::ma_or(Register rd, Imm32 imm) +{ + ma_or(rd, rd, imm); +} + +void +MacroAssemblerMIPS::ma_or(Register rd, Register rs, Imm32 imm) +{ + if (Imm16::isInUnsignedRange(imm.value)) { + as_ori(rd, rs, imm.value); + } else { + ma_li(ScratchRegister, imm); + as_or(rd, rs, ScratchRegister); + } +} + +// xor +void +MacroAssemblerMIPS::ma_xor(Register rd, Register rs) +{ + as_xor(rd, rd, rs); +} + +void +MacroAssemblerMIPS::ma_xor(Register rd, Register rs, Register rt) +{ + as_xor(rd, rs, rt); +} + +void +MacroAssemblerMIPS::ma_xor(Register rd, Imm32 imm) +{ + ma_xor(rd, rd, imm); +} + +void +MacroAssemblerMIPS::ma_xor(Register rd, Register rs, Imm32 imm) +{ + if (Imm16::isInUnsignedRange(imm.value)) { + as_xori(rd, rs, imm.value); + } else { + ma_li(ScratchRegister, imm); + as_xor(rd, rs, ScratchRegister); + } +} + +// Arithmetic-based ops. + +// Add. +void +MacroAssemblerMIPS::ma_addu(Register rd, Register rs, Imm32 imm) +{ + if (Imm16::isInSignedRange(imm.value)) { + as_addiu(rd, rs, imm.value); + } else { + ma_li(ScratchRegister, imm); + as_addu(rd, rs, ScratchRegister); + } +} + +void +MacroAssemblerMIPS::ma_addu(Register rd, Register rs) +{ + as_addu(rd, rd, rs); +} + +void +MacroAssemblerMIPS::ma_addu(Register rd, Imm32 imm) +{ + ma_addu(rd, rd, imm); +} + +void +MacroAssemblerMIPS::ma_addTestOverflow(Register rd, Register rs, Register rt, Label *overflow) +{ + Label goodAddition; + as_addu(SecondScratchReg, rs, rt); + + as_xor(ScratchRegister, rs, rt); // If different sign, no overflow + ma_b(ScratchRegister, Imm32(0), &goodAddition, Assembler::LessThan, ShortJump); + + // If different sign, then overflow + as_xor(ScratchRegister, rs, SecondScratchReg); + ma_b(ScratchRegister, Imm32(0), overflow, Assembler::LessThan); + + bind(&goodAddition); + ma_move(rd, SecondScratchReg); +} + +void +MacroAssemblerMIPS::ma_addTestOverflow(Register rd, Register rs, Imm32 imm, Label *overflow) +{ + // Check for signed range because of as_addiu + // Check for unsigned range because of as_xori + if (Imm16::isInSignedRange(imm.value) && Imm16::isInUnsignedRange(imm.value)) { + Label goodAddition; + as_addiu(SecondScratchReg, rs, imm.value); + + // If different sign, no overflow + as_xori(ScratchRegister, rs, imm.value); + ma_b(ScratchRegister, Imm32(0), &goodAddition, Assembler::LessThan, ShortJump); + + // If different sign, then overflow + as_xor(ScratchRegister, rs, SecondScratchReg); + ma_b(ScratchRegister, Imm32(0), overflow, Assembler::LessThan); + + bind(&goodAddition); + ma_move(rd, SecondScratchReg); + } else { + ma_li(ScratchRegister, imm); + ma_addTestOverflow(rd, rs, ScratchRegister, overflow); + } +} + +// Subtract. +void +MacroAssemblerMIPS::ma_subu(Register rd, Register rs, Register rt) +{ + as_subu(rd, rs, rt); +} + +void +MacroAssemblerMIPS::ma_subu(Register rd, Register rs, Imm32 imm) +{ + if (Imm16::isInSignedRange(-imm.value)) { + as_addiu(rd, rs, -imm.value); + } else { + ma_li(ScratchRegister, imm); + as_subu(rd, rs, ScratchRegister); + } +} + +void +MacroAssemblerMIPS::ma_subu(Register rd, Imm32 imm) +{ + ma_subu(rd, rd, imm); +} + +void +MacroAssemblerMIPS::ma_subTestOverflow(Register rd, Register rs, Register rt, Label *overflow) +{ + Label goodSubtraction; + // Use second scratch. The instructions generated by ma_b don't use the + // second scratch register. + ma_subu(SecondScratchReg, rs, rt); + + as_xor(ScratchRegister, rs, rt); // If same sign, no overflow + ma_b(ScratchRegister, Imm32(0), &goodSubtraction, Assembler::GreaterThanOrEqual, ShortJump); + + // If different sign, then overflow + as_xor(ScratchRegister, rs, SecondScratchReg); + ma_b(ScratchRegister, Imm32(0), overflow, Assembler::LessThan); + + bind(&goodSubtraction); + ma_move(rd, SecondScratchReg); +} + +void +MacroAssemblerMIPS::ma_subTestOverflow(Register rd, Register rs, Imm32 imm, Label *overflow) +{ + if (imm.value != INT32_MIN) { + ma_addTestOverflow(rd, rs, Imm32(-imm.value), overflow); + } else { + ma_li(ScratchRegister, Imm32(imm.value)); + ma_subTestOverflow(rd, rs, ScratchRegister, overflow); + } +} + +void +MacroAssemblerMIPS::ma_mult(Register rs, Imm32 imm) +{ + ma_li(ScratchRegister, imm); + as_mult(rs, ScratchRegister); +} + +void +MacroAssemblerMIPS::ma_mul_branch_overflow(Register rd, Register rs, Register rt, Label *overflow) +{ + as_mult(rs, rt); + as_mflo(rd); + as_sra(ScratchRegister, rd, 31); + as_mfhi(SecondScratchReg); + ma_b(ScratchRegister, SecondScratchReg, overflow, Assembler::NotEqual); +} + +void +MacroAssemblerMIPS::ma_mul_branch_overflow(Register rd, Register rs, Imm32 imm, Label *overflow) +{ + ma_li(ScratchRegister, imm); + ma_mul_branch_overflow(rd, rs, ScratchRegister, overflow); +} + +void +MacroAssemblerMIPS::ma_div_branch_overflow(Register rd, Register rs, Register rt, Label *overflow) +{ + as_div(rs, rt); + as_mflo(rd); + as_mfhi(ScratchRegister); + ma_b(ScratchRegister, ScratchRegister, overflow, Assembler::NonZero); +} + +void +MacroAssemblerMIPS::ma_div_branch_overflow(Register rd, Register rs, Imm32 imm, Label *overflow) +{ + ma_li(ScratchRegister, imm); + ma_div_branch_overflow(rd, rs, ScratchRegister, overflow); +} + +void +MacroAssemblerMIPS::ma_mod_mask(Register src, Register dest, Register hold, int32_t shift, + Label *negZero) +{ + // MATH: + // We wish to compute x % (1<<y) - 1 for a known constant, y. + // First, let b = (1<<y) and C = (1<<y)-1, then think of the 32 bit + // dividend as a number in base b, namely + // c_0*1 + c_1*b + c_2*b^2 ... c_n*b^n + // now, since both addition and multiplication commute with modulus, + // x % C == (c_0 + c_1*b + ... + c_n*b^n) % C == + // (c_0 % C) + (c_1%C) * (b % C) + (c_2 % C) * (b^2 % C)... + // now, since b == C + 1, b % C == 1, and b^n % C == 1 + // this means that the whole thing simplifies to: + // c_0 + c_1 + c_2 ... c_n % C + // each c_n can easily be computed by a shift/bitextract, and the modulus + // can be maintained by simply subtracting by C whenever the number gets + // over C. + int32_t mask = (1 << shift) - 1; + Label head, negative, sumSigned, done; + + // hold holds -1 if the value was negative, 1 otherwise. + // ScratchRegister holds the remaining bits that have not been processed + // lr serves as a temporary location to store extracted bits into as well + // as holding the trial subtraction as a temp value dest is the + // accumulator (and holds the final result) + + // move the whole value into the scratch register, setting the codition + // codes so we can muck with them later. + ma_move(ScratchRegister, src); + // Zero out the dest. + ma_subu(dest, dest, dest); + // Set the hold appropriately. + ma_b(ScratchRegister, ScratchRegister, &negative, Signed, ShortJump); + ma_li(hold, Imm32(1)); + ma_b(&head, ShortJump); + + bind(&negative); + ma_li(hold, Imm32(-1)); + ma_negu(ScratchRegister, ScratchRegister); + + // Begin the main loop. + bind(&head); + + // Extract the bottom bits into lr. + ma_and(SecondScratchReg, ScratchRegister, Imm32(mask)); + // Add those bits to the accumulator. + as_addu(dest, dest, SecondScratchReg); + // Do a trial subtraction, this is the same operation as cmp, but we + // store the dest + ma_subu(SecondScratchReg, dest, Imm32(mask)); + // If (sum - C) > 0, store sum - C back into sum, thus performing a + // modulus. + ma_b(SecondScratchReg, SecondScratchReg, &sumSigned, Signed, ShortJump); + ma_move(dest, SecondScratchReg); + bind(&sumSigned); + // Get rid of the bits that we extracted before. + as_srl(ScratchRegister, ScratchRegister, shift); + // If the shift produced zero, finish, otherwise, continue in the loop. + ma_b(ScratchRegister, ScratchRegister, &head, NonZero, ShortJump); + // Check the hold to see if we need to negate the result. + ma_b(hold, hold, &done, NotSigned, ShortJump); + + // If the hold was non-zero, negate the result to be in line with + // what JS wants + if (negZero != nullptr) { + // Jump out in case of negative zero. + ma_b(hold, hold, negZero, Zero); + ma_negu(dest, dest); + } else { + ma_negu(dest, dest); + } + + bind(&done); +} + +// Memory. + +void +MacroAssemblerMIPS::ma_load(const Register &dest, Address address, + LoadStoreSize size, LoadStoreExtension extension) +{ + int16_t encodedOffset; + Register base; + if (!Imm16::isInSignedRange(address.offset)) { + ma_li(ScratchRegister, Imm32(address.offset)); + as_addu(ScratchRegister, address.base, ScratchRegister); + base = ScratchRegister; + encodedOffset = Imm16(0).encode(); + } else { + encodedOffset = Imm16(address.offset).encode(); + base = address.base; + } + + switch (size) { + case SizeByte: + if (ZeroExtend == extension) + as_lbu(dest, base, encodedOffset); + else + as_lb(dest, base, encodedOffset); + break; + case SizeHalfWord: + if (ZeroExtend == extension) + as_lhu(dest, base, encodedOffset); + else + as_lh(dest, base, encodedOffset); + break; + case SizeWord: + as_lw(dest, base, encodedOffset); + break; + default: + MOZ_ASSUME_UNREACHABLE("Invalid argument for ma_load"); + break; + } +} + +void +MacroAssemblerMIPS::ma_load(const Register &dest, const BaseIndex &src, + LoadStoreSize size, LoadStoreExtension extension) +{ + computeScaledAddress(src, SecondScratchReg); + ma_load(dest, Address(SecondScratchReg, src.offset), size, extension); +} + +void +MacroAssemblerMIPS::ma_store(const Register &data, Address address, LoadStoreSize size, + LoadStoreExtension extension) +{ + int16_t encodedOffset; + Register base; + if (!Imm16::isInSignedRange(address.offset)) { + ma_li(ScratchRegister, Imm32(address.offset)); + as_addu(ScratchRegister, address.base, ScratchRegister); + base = ScratchRegister; + encodedOffset = Imm16(0).encode(); + } else { + encodedOffset = Imm16(address.offset).encode(); + base = address.base; + } + + switch (size) { + case SizeByte: + as_sb(data, base, encodedOffset); + break; + case SizeHalfWord: + as_sh(data, base, encodedOffset); + break; + case SizeWord: + as_sw(data, base, encodedOffset); + break; + default: + MOZ_ASSUME_UNREACHABLE("Invalid argument for ma_store"); + break; + } +} + +void +MacroAssemblerMIPS::ma_store(const Register &data, const BaseIndex &dest, + LoadStoreSize size, LoadStoreExtension extension) +{ + computeScaledAddress(dest, SecondScratchReg); + ma_store(data, Address(SecondScratchReg, dest.offset), size, extension); +} + +void +MacroAssemblerMIPS::ma_store(const Imm32 &imm, const BaseIndex &dest, + LoadStoreSize size, LoadStoreExtension extension) +{ + // Make sure that SecondScratchReg contains absolute address so that + // offset is 0. + computeEffectiveAddress(dest, SecondScratchReg); + + // Scrach register is free now, use it for loading imm value + ma_li(ScratchRegister, imm); + + // with offset=0 ScratchRegister will not be used in ma_store() + // so we can use it as a parameter here + ma_store(ScratchRegister, Address(SecondScratchReg, 0), size, extension); +} + +void +MacroAssemblerMIPS::computeScaledAddress(const BaseIndex &address, Register dest) +{ + int32_t shift = Imm32::ShiftOf(address.scale).value; + if (shift) { + ma_sll(dest, address.index, Imm32(shift)); + as_addu(dest, address.base, dest); + } else { + as_addu(dest, address.base, address.index); + } +} + +// Shortcut for when we know we're transferring 32 bits of data. +void +MacroAssemblerMIPS::ma_lw(Register data, Address address) +{ + ma_load(data, address, SizeWord); +} + +void +MacroAssemblerMIPS::ma_sw(Register data, Address address) +{ + ma_store(data, address, SizeWord); +} + +void +MacroAssemblerMIPS::ma_sw(Imm32 imm, Address address) +{ + MOZ_ASSERT(address.base != ScratchRegister); + ma_li(ScratchRegister, imm); + + if (Imm16::isInSignedRange(address.offset)) { + as_sw(ScratchRegister, address.base, Imm16(address.offset).encode()); + } else { + MOZ_ASSERT(address.base != SecondScratchReg); + + ma_li(SecondScratchReg, Imm32(address.offset)); + as_addu(SecondScratchReg, address.base, SecondScratchReg); + as_sw(ScratchRegister, SecondScratchReg, 0); + } +} + +void +MacroAssemblerMIPS::ma_pop(Register r) +{ + as_lw(r, StackPointer, 0); + as_addiu(StackPointer, StackPointer, sizeof(intptr_t)); +} + +void +MacroAssemblerMIPS::ma_push(Register r) +{ + if (r == sp) { + // Pushing sp requires one more instruction. + ma_move(ScratchRegister, sp); + r = ScratchRegister; + } + + as_addiu(StackPointer, StackPointer, -sizeof(intptr_t)); + as_sw(r, StackPointer, 0); +} + +// Branches when done from within mips-specific code. +void +MacroAssemblerMIPS::ma_b(Register lhs, Register rhs, Label *label, Condition c, JumpKind jumpKind) +{ + switch (c) { + case Equal : + case NotEqual: + branchWithCode(getBranchCode(lhs, rhs, c), label, jumpKind); + break; + case Always: + ma_b(label, jumpKind); + break; + case Zero: + case NonZero: + case Signed: + case NotSigned: + MOZ_ASSERT(lhs == rhs); + branchWithCode(getBranchCode(lhs, c), label, jumpKind); + break; + default: + Condition cond = ma_cmp(ScratchRegister, lhs, rhs, c); + branchWithCode(getBranchCode(ScratchRegister, cond), label, jumpKind); + break; + } +} + +void +MacroAssemblerMIPS::ma_b(Register lhs, Imm32 imm, Label *label, Condition c, JumpKind jumpKind) +{ + MOZ_ASSERT(c != Overflow); + if (imm.value == 0) { + if (c == Always || c == AboveOrEqual) + ma_b(label, jumpKind); + else if (c == Below) + ; // This condition is always false. No branch required. + else + branchWithCode(getBranchCode(lhs, c), label, jumpKind); + } else { + MOZ_ASSERT(lhs != ScratchRegister); + ma_li(ScratchRegister, imm); + ma_b(lhs, ScratchRegister, label, c, jumpKind); + } +} + +void +MacroAssemblerMIPS::ma_b(Register lhs, Address addr, Label *label, Condition c, JumpKind jumpKind) +{ + MOZ_ASSERT(lhs != ScratchRegister); + ma_lw(ScratchRegister, addr); + ma_b(lhs, ScratchRegister, label, c, jumpKind); +} + +void +MacroAssemblerMIPS::ma_b(Address addr, Imm32 imm, Label *label, Condition c, JumpKind jumpKind) +{ + ma_lw(SecondScratchReg, addr); + ma_b(SecondScratchReg, imm, label, c, jumpKind); +} + +void +MacroAssemblerMIPS::ma_b(Label *label, JumpKind jumpKind) +{ + branchWithCode(getBranchCode(BranchIsJump), label, jumpKind); +} + +void +MacroAssemblerMIPS::ma_bal(Label *label, JumpKind jumpKind) +{ + branchWithCode(getBranchCode(BranchIsCall), label, jumpKind); +} + +void +MacroAssemblerMIPS::branchWithCode(InstImm code, Label *label, JumpKind jumpKind) +{ + InstImm inst_bgezal = InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0)); + InstImm inst_beq = InstImm(op_beq, zero, zero, BOffImm16(0)); + + if (label->bound()) { + int32_t offset = label->offset() - m_buffer.nextOffset().getOffset(); + + if (BOffImm16::isInRange(offset)) + jumpKind = ShortJump; + + if (jumpKind == ShortJump) { + MOZ_ASSERT(BOffImm16::isInRange(offset)); + code.setBOffImm16(BOffImm16(offset)); + writeInst(code.encode()); + as_nop(); + return; + } + + // Generate long jump because target is out of range of short jump. + if (code.encode() == inst_bgezal.encode()) { + // Handle long call + addLongJump(nextOffset()); + ma_liPatchable(ScratchRegister, Imm32(label->offset())); + as_jalr(ScratchRegister); + as_nop(); + return; + } + if (code.encode() == inst_beq.encode()) { + // Handle long jump + addLongJump(nextOffset()); + ma_liPatchable(ScratchRegister, Imm32(label->offset())); + as_jr(ScratchRegister); + as_nop(); + return; + } + + // Handle long conditional branch + writeInst(invertBranch(code, BOffImm16(5 * sizeof(uint32_t))).encode()); + // No need for a "nop" here because we can clobber scratch. + addLongJump(nextOffset()); + ma_liPatchable(ScratchRegister, Imm32(label->offset())); + as_jr(ScratchRegister); + as_nop(); + return; + } + + // Generate open jump and link it to a label. + + // Second word holds a pointer to the next branch in label's chain. + uint32_t nextInChain = label->used() ? label->offset() : LabelBase::INVALID_OFFSET; + + if (jumpKind == ShortJump) { + // Make the whole branch continous in the buffer. + m_buffer.ensureSpace(2 * sizeof(uint32_t)); + + // Indicate that this is short jump with offset 4. + code.setBOffImm16(BOffImm16(4)); + BufferOffset bo = writeInst(code.encode()); + writeInst(nextInChain); + label->use(bo.getOffset()); + return; + } + + bool conditional = (code.encode() != inst_bgezal.encode() && + code.encode() != inst_beq.encode()); + + // Make the whole branch continous in the buffer. + m_buffer.ensureSpace((conditional ? 5 : 4) * sizeof(uint32_t)); + + BufferOffset bo = writeInst(code.encode()); + writeInst(nextInChain); + label->use(bo.getOffset()); + // Leave space for potential long jump. + as_nop(); + as_nop(); + if (conditional) + as_nop(); +} + +Assembler::Condition +MacroAssemblerMIPS::ma_cmp(Register scratch, Register lhs, Register rhs, Condition c) +{ + switch (c) { + case Above: + // bgtu s,t,label => + // sltu at,t,s + // bne at,$zero,offs + as_sltu(scratch, rhs, lhs); + return NotEqual; + case AboveOrEqual: + // bgeu s,t,label => + // sltu at,s,t + // beq at,$zero,offs + as_sltu(scratch, lhs, rhs); + return Equal; + case Below: + // bltu s,t,label => + // sltu at,s,t + // bne at,$zero,offs + as_sltu(scratch, lhs, rhs); + return NotEqual; + case BelowOrEqual: + // bleu s,t,label => + // sltu at,t,s + // beq at,$zero,offs + as_sltu(scratch, rhs, lhs); + return Equal; + case GreaterThan: + // bgt s,t,label => + // slt at,t,s + // bne at,$zero,offs + as_slt(scratch, rhs, lhs); + return NotEqual; + case GreaterThanOrEqual: + // bge s,t,label => + // slt at,s,t + // beq at,$zero,offs + as_slt(scratch, lhs, rhs); + return Equal; + case LessThan: + // blt s,t,label => + // slt at,s,t + // bne at,$zero,offs + as_slt(scratch, lhs, rhs); + return NotEqual; + case LessThanOrEqual: + // ble s,t,label => + // slt at,t,s + // beq at,$zero,offs + as_slt(scratch, rhs, lhs); + return Equal; + case Equal : + case NotEqual: + case Zero: + case NonZero: + case Always: + case Signed: + case NotSigned: + MOZ_ASSUME_UNREACHABLE("There is a better way to compare for equality."); + break; + case Overflow: + MOZ_ASSUME_UNREACHABLE("Overflow condition not supported for MIPS."); + break; + default: + MOZ_ASSUME_UNREACHABLE("Invalid condition for branch."); + } + return Always; +} + +void +MacroAssemblerMIPS::ma_cmp_set(Register rd, Register rs, Register rt, Condition c) +{ + switch (c) { + case Equal : + // seq d,s,t => + // xor d,s,t + // sltiu d,d,1 + as_xor(rd, rs, rt); + as_sltiu(rd, rd, 1); + break; + case NotEqual: + // sne d,s,t => + // xor d,s,t + // sltu d,$zero,d + as_xor(rd, rs, rt); + as_sltu(rd, zero, rd); + break; + case Above: + // sgtu d,s,t => + // sltu d,t,s + as_sltu(rd, rt, rs); + break; + case AboveOrEqual: + // sgeu d,s,t => + // sltu d,s,t + // xori d,d,1 + as_sltu(rd, rs, rt); + as_xori(rd, rd, 1); + break; + case Below: + // sltu d,s,t + as_sltu(rd, rs, rt); + break; + case BelowOrEqual: + // sleu d,s,t => + // sltu d,t,s + // xori d,d,1 + as_sltu(rd, rt, rs); + as_xori(rd, rd, 1); + break; + case GreaterThan: + // sgt d,s,t => + // slt d,t,s + as_slt(rd, rt, rs); + break; + case GreaterThanOrEqual: + // sge d,s,t => + // slt d,s,t + // xori d,d,1 + as_slt(rd, rs, rt); + as_xori(rd, rd, 1); + break; + case LessThan: + // slt d,s,t + as_slt(rd, rs, rt); + break; + case LessThanOrEqual: + // sle d,s,t => + // slt d,t,s + // xori d,d,1 + as_slt(rd, rt, rs); + as_xori(rd, rd, 1); + break; + case Zero: + MOZ_ASSERT(rs == rt); + // seq d,s,$zero => + // xor d,s,$zero + // sltiu d,d,1 + as_xor(rd, rs, zero); + as_sltiu(rd, rd, 1); + break; + case NonZero: + // sne d,s,$zero => + // xor d,s,$zero + // sltu d,$zero,d + as_xor(rd, rs, zero); + as_sltu(rd, zero, rd); + break; + case Signed: + as_slt(rd, rs, zero); + break; + case NotSigned: + // sge d,s,$zero => + // slt d,s,$zero + // xori d,d,1 + as_slt(rd, rs, zero); + as_xori(rd, rd, 1); + break; + default: + MOZ_ASSUME_UNREACHABLE("Invalid condition for ma_cmp_set."); + break; + } +} + +void +MacroAssemblerMIPS::compareFloatingPoint(FloatFormat fmt, FloatRegister lhs, FloatRegister rhs, + DoubleCondition c, FloatTestKind *testKind, + FPConditionBit fcc) +{ + switch (c) { + case DoubleOrdered: + as_cun(fmt, lhs, rhs, fcc); + *testKind = TestForFalse; + break; + case DoubleEqual: + as_ceq(fmt, lhs, rhs, fcc); + *testKind = TestForTrue; + break; + case DoubleNotEqual: + as_cueq(fmt, lhs, rhs, fcc); + *testKind = TestForFalse; + break; + case DoubleGreaterThan: + as_colt(fmt, rhs, lhs, fcc); + *testKind = TestForTrue; + break; + case DoubleGreaterThanOrEqual: + as_cole(fmt, rhs, lhs, fcc); + *testKind = TestForTrue; + break; + case DoubleLessThan: + as_colt(fmt, lhs, rhs, fcc); + *testKind = TestForTrue; + break; + case DoubleLessThanOrEqual: + as_cole(fmt, lhs, rhs, fcc); + *testKind = TestForTrue; + break; + case DoubleUnordered: + as_cun(fmt, lhs, rhs, fcc); + *testKind = TestForTrue; + break; + case DoubleEqualOrUnordered: + as_cueq(fmt, lhs, rhs, fcc); + *testKind = TestForTrue; + break; + case DoubleNotEqualOrUnordered: + as_ceq(fmt, lhs, rhs, fcc); + *testKind = TestForFalse; + break; + case DoubleGreaterThanOrUnordered: + as_cult(fmt, rhs, lhs, fcc); + *testKind = TestForTrue; + break; + case DoubleGreaterThanOrEqualOrUnordered: + as_cule(fmt, rhs, lhs, fcc); + *testKind = TestForTrue; + break; + case DoubleLessThanOrUnordered: + as_cult(fmt, lhs, rhs, fcc); + *testKind = TestForTrue; + break; + case DoubleLessThanOrEqualOrUnordered: + as_cule(fmt, lhs, rhs, fcc); + *testKind = TestForTrue; + break; + default: + MOZ_ASSUME_UNREACHABLE("Invalid DoubleCondition."); + break; + } +} + +void +MacroAssemblerMIPS::ma_cmp_set_double(Register dest, FloatRegister lhs, FloatRegister rhs, + DoubleCondition c) +{ + ma_li(dest, Imm32(0)); + ma_li(ScratchRegister, Imm32(1)); + + FloatTestKind moveCondition; + compareFloatingPoint(DoubleFloat, lhs, rhs, c, &moveCondition); + + if (moveCondition == TestForTrue) + as_movt(dest, ScratchRegister); + else + as_movf(dest, ScratchRegister); +} + +void +MacroAssemblerMIPS::ma_cmp_set_float32(Register dest, FloatRegister lhs, FloatRegister rhs, + DoubleCondition c) +{ + ma_li(dest, Imm32(0)); + ma_li(ScratchRegister, Imm32(1)); + + FloatTestKind moveCondition; + compareFloatingPoint(SingleFloat, lhs, rhs, c, &moveCondition); + + if (moveCondition == TestForTrue) + as_movt(dest, ScratchRegister); + else + as_movf(dest, ScratchRegister); +} + +void +MacroAssemblerMIPS::ma_cmp_set(Register rd, Register rs, Imm32 imm, Condition c) +{ + ma_li(ScratchRegister, imm); + ma_cmp_set(rd, rs, ScratchRegister, c); +} + +void +MacroAssemblerMIPS::ma_cmp_set(Register rd, Register rs, Address addr, Condition c) +{ + ma_lw(ScratchRegister, addr); + ma_cmp_set(rd, rs, ScratchRegister, c); +} + +void +MacroAssemblerMIPS::ma_cmp_set(Register dst, Address lhs, Register rhs, Condition c) +{ + ma_lw(ScratchRegister, lhs); + ma_cmp_set(dst, ScratchRegister, rhs, c); +} + +// fp instructions +void +MacroAssemblerMIPS::ma_lis(FloatRegister dest, float value) +{ + Imm32 imm(mozilla::BitwiseCast<uint32_t>(value)); + + ma_li(ScratchRegister, imm); + moveToFloat32(ScratchRegister, dest); +} + +void +MacroAssemblerMIPS::ma_lid(FloatRegister dest, double value) +{ + struct DoubleStruct { + uint32_t lo; + uint32_t hi; + } ; + DoubleStruct intStruct = mozilla::BitwiseCast<DoubleStruct>(value); + + // put hi part of 64 bit value into the odd register + if (intStruct.hi == 0) { + moveToDoubleHi(zero, dest); + } else { + ma_li(ScratchRegister, Imm32(intStruct.hi)); + moveToDoubleHi(ScratchRegister, dest); + } + + // put low part of 64 bit value into the even register + if (intStruct.lo == 0) { + moveToDoubleLo(zero, dest); + } else { + ma_li(ScratchRegister, Imm32(intStruct.lo)); + moveToDoubleLo(ScratchRegister, dest); + } +} + +void +MacroAssemblerMIPS::ma_liNegZero(FloatRegister dest) +{ + moveToDoubleLo(zero, dest); + ma_li(ScratchRegister, Imm32(INT_MIN)); + moveToDoubleHi(ScratchRegister, dest); +} + +void +MacroAssemblerMIPS::ma_mv(FloatRegister src, ValueOperand dest) +{ + moveFromDoubleLo(src, dest.payloadReg()); + moveFromDoubleHi(src, dest.typeReg()); +} + +void +MacroAssemblerMIPS::ma_mv(ValueOperand src, FloatRegister dest) +{ + moveToDoubleLo(src.payloadReg(), dest); + moveToDoubleHi(src.typeReg(), dest); +} + +void +MacroAssemblerMIPS::ma_ls(FloatRegister ft, Address address) +{ + if (Imm16::isInSignedRange(address.offset)) { + as_ls(ft, address.base, Imm16(address.offset).encode()); + } else { + MOZ_ASSERT(address.base != ScratchRegister); + ma_li(ScratchRegister, Imm32(address.offset)); + as_addu(ScratchRegister, address.base, ScratchRegister); + as_ls(ft, ScratchRegister, 0); + } +} + +void +MacroAssemblerMIPS::ma_ld(FloatRegister ft, Address address) +{ + // Use single precision load instructions so we don't have to worry about + // alignment. + + int32_t off2 = address.offset + TAG_OFFSET; + if (Imm16::isInSignedRange(address.offset) && Imm16::isInSignedRange(off2)) { + as_ls(ft, address.base, Imm16(address.offset).encode()); + as_ls(getOddPair(ft), address.base, Imm16(off2).encode()); + } else { + ma_li(ScratchRegister, Imm32(address.offset)); + as_addu(ScratchRegister, address.base, ScratchRegister); + as_ls(ft, ScratchRegister, PAYLOAD_OFFSET); + as_ls(getOddPair(ft), ScratchRegister, TAG_OFFSET); + } +} + +void +MacroAssemblerMIPS::ma_sd(FloatRegister ft, Address address) +{ + int32_t off2 = address.offset + TAG_OFFSET; + if (Imm16::isInSignedRange(address.offset) && Imm16::isInSignedRange(off2)) { + as_ss(ft, address.base, Imm16(address.offset).encode()); + as_ss(getOddPair(ft), address.base, Imm16(off2).encode()); + } else { + ma_li(ScratchRegister, Imm32(address.offset)); + as_addu(ScratchRegister, address.base, ScratchRegister); + as_ss(ft, ScratchRegister, PAYLOAD_OFFSET); + as_ss(getOddPair(ft), ScratchRegister, TAG_OFFSET); + } +} + +void +MacroAssemblerMIPS::ma_sd(FloatRegister ft, BaseIndex address) +{ + computeScaledAddress(address, SecondScratchReg); + ma_sd(ft, Address(SecondScratchReg, address.offset)); +} + +void +MacroAssemblerMIPS::ma_ss(FloatRegister ft, Address address) +{ + if (Imm16::isInSignedRange(address.offset)) { + as_ss(ft, address.base, Imm16(address.offset).encode()); + } else { + ma_li(ScratchRegister, Imm32(address.offset)); + as_addu(ScratchRegister, address.base, ScratchRegister); + as_ss(ft, ScratchRegister, 0); + } +} + +void +MacroAssemblerMIPS::ma_ss(FloatRegister ft, BaseIndex address) +{ + computeScaledAddress(address, SecondScratchReg); + ma_ss(ft, Address(SecondScratchReg, address.offset)); +} + +void +MacroAssemblerMIPS::ma_pop(FloatRegister fs) +{ + ma_ld(fs, Address(StackPointer, 0)); + as_addiu(StackPointer, StackPointer, sizeof(double)); +} + +void +MacroAssemblerMIPS::ma_push(FloatRegister fs) +{ + as_addiu(StackPointer, StackPointer, -sizeof(double)); + ma_sd(fs, Address(StackPointer, 0)); +} + +void +MacroAssemblerMIPS::ma_bc1s(FloatRegister lhs, FloatRegister rhs, Label *label, + DoubleCondition c, JumpKind jumpKind, FPConditionBit fcc) +{ + FloatTestKind testKind; + compareFloatingPoint(SingleFloat, lhs, rhs, c, &testKind, fcc); + branchWithCode(getBranchCode(testKind, fcc), label, jumpKind); +} + +void +MacroAssemblerMIPS::ma_bc1d(FloatRegister lhs, FloatRegister rhs, Label *label, + DoubleCondition c, JumpKind jumpKind, FPConditionBit fcc) +{ + FloatTestKind testKind; + compareFloatingPoint(DoubleFloat, lhs, rhs, c, &testKind, fcc); + branchWithCode(getBranchCode(testKind, fcc), label, jumpKind); +} + +bool +MacroAssemblerMIPSCompat::buildFakeExitFrame(const Register &scratch, uint32_t *offset) +{ + mozilla::DebugOnly<uint32_t> initialDepth = framePushed(); + + CodeLabel cl; + ma_li(scratch, cl.dest()); + + uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS); + Push(Imm32(descriptor)); + Push(scratch); + + bind(cl.src()); + *offset = currentOffset(); + + MOZ_ASSERT(framePushed() == initialDepth + IonExitFrameLayout::Size()); + return addCodeLabel(cl); +} + +bool +MacroAssemblerMIPSCompat::buildOOLFakeExitFrame(void *fakeReturnAddr) +{ + DebugOnly<uint32_t> initialDepth = framePushed(); + uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS); + + Push(Imm32(descriptor)); // descriptor_ + Push(ImmPtr(fakeReturnAddr)); + + return true; +} + +void +MacroAssemblerMIPSCompat::callWithExitFrame(JitCode *target) +{ + uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS); + Push(Imm32(descriptor)); // descriptor + + addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::JITCODE); + ma_liPatchable(ScratchRegister, ImmPtr(target->raw())); + ma_callIonHalfPush(ScratchRegister); +} + +void +MacroAssemblerMIPSCompat::callWithExitFrame(JitCode *target, Register dynStack) +{ + ma_addu(dynStack, dynStack, Imm32(framePushed())); + makeFrameDescriptor(dynStack, JitFrame_IonJS); + Push(dynStack); // descriptor + + addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::JITCODE); + ma_liPatchable(ScratchRegister, ImmPtr(target->raw())); + ma_callIonHalfPush(ScratchRegister); +} + +void +MacroAssemblerMIPSCompat::callIon(const Register &callee) +{ + MOZ_ASSERT((framePushed() & 3) == 0); + if ((framePushed() & 7) == 4) { + ma_callIonHalfPush(callee); + } else { + adjustFrame(sizeof(uint32_t)); + ma_callIon(callee); + } +} + +void +MacroAssemblerMIPSCompat::reserveStack(uint32_t amount) +{ + if (amount) + ma_subu(StackPointer, StackPointer, Imm32(amount)); + adjustFrame(amount); +} + +void +MacroAssemblerMIPSCompat::freeStack(uint32_t amount) +{ + MOZ_ASSERT(amount <= framePushed_); + if (amount) + ma_addu(StackPointer, StackPointer, Imm32(amount)); + adjustFrame(-amount); +} + +void +MacroAssemblerMIPSCompat::freeStack(Register amount) +{ + as_addu(StackPointer, StackPointer, amount); +} + +void +MacroAssembler::PushRegsInMask(RegisterSet set) +{ + int32_t diffF = set.fpus().size() * sizeof(double); + int32_t diffG = set.gprs().size() * sizeof(intptr_t); + + reserveStack(diffG); + for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) { + diffG -= sizeof(intptr_t); + storePtr(*iter, Address(StackPointer, diffG)); + } + MOZ_ASSERT(diffG == 0); + + // Double values have to be aligned. We reserve extra space so that we can + // start writing from the first aligned location. + // We reserve a whole extra double so that the buffer has even size. + ma_and(SecondScratchReg, sp, Imm32(~(StackAlignment - 1))); + reserveStack(diffF + sizeof(double)); + + for (FloatRegisterForwardIterator iter(set.fpus()); iter.more(); iter++) { + // Use assembly s.d because we have alligned the stack. + // :TODO: (Bug 972836) Fix this once odd regs can be used as + // float32 only. For now we skip saving odd regs for O32 ABI. + + // :TODO: (Bug 985881) Make a switch for N32 ABI. + if ((*iter).code() % 2 == 0) + as_sd(*iter, SecondScratchReg, -diffF); + diffF -= sizeof(double); + } + MOZ_ASSERT(diffF == 0); +} + +void +MacroAssembler::PopRegsInMaskIgnore(RegisterSet set, RegisterSet ignore) +{ + int32_t diffG = set.gprs().size() * sizeof(intptr_t); + int32_t diffF = set.fpus().size() * sizeof(double); + const int32_t reservedG = diffG; + const int32_t reservedF = diffF; + + // Read the buffer form the first aligned location. + ma_addu(SecondScratchReg, sp, Imm32(reservedF + sizeof(double))); + ma_and(SecondScratchReg, SecondScratchReg, Imm32(~(StackAlignment - 1))); + + for (FloatRegisterForwardIterator iter(set.fpus()); iter.more(); iter++) { + // :TODO: (Bug 972836) Fix this once odd regs can be used as + // float32 only. For now we skip loading odd regs for O32 ABI. + + // :TODO: (Bug 985881) Make a switch for N32 ABI. + if (!ignore.has(*iter) && ((*iter).code() % 2 == 0)) + // Use assembly l.d because we have alligned the stack. + as_ld(*iter, SecondScratchReg, -diffF); + diffF -= sizeof(double); + } + freeStack(reservedF + sizeof(double)); + MOZ_ASSERT(diffF == 0); + + for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) { + diffG -= sizeof(intptr_t); + if (!ignore.has(*iter)) + loadPtr(Address(StackPointer, diffG), *iter); + } + freeStack(reservedG); + MOZ_ASSERT(diffG == 0); +} + +void +MacroAssemblerMIPSCompat::add32(Register src, Register dest) +{ + as_addu(dest, dest, src); +} + +void +MacroAssemblerMIPSCompat::add32(Imm32 imm, Register dest) +{ + ma_addu(dest, dest, imm); +} + +void + +MacroAssemblerMIPSCompat::add32(Imm32 imm, const Address &dest) +{ + load32(dest, SecondScratchReg); + ma_addu(SecondScratchReg, imm); + store32(SecondScratchReg, dest); +} + +void +MacroAssemblerMIPSCompat::sub32(Imm32 imm, Register dest) +{ + ma_subu(dest, dest, imm); +} + +void +MacroAssemblerMIPSCompat::sub32(Register src, Register dest) +{ + ma_subu(dest, dest, src); +} + +void +MacroAssemblerMIPSCompat::addPtr(Register src, Register dest) +{ + ma_addu(dest, src); +} + +void +MacroAssemblerMIPSCompat::addPtr(const Address &src, Register dest) +{ + loadPtr(src, ScratchRegister); + ma_addu(dest, ScratchRegister); +} + +void +MacroAssemblerMIPSCompat::subPtr(Register src, Register dest) +{ + ma_subu(dest, dest, src); +} + +void +MacroAssemblerMIPSCompat::not32(Register reg) +{ + ma_not(reg, reg); +} + +// Logical operations +void +MacroAssemblerMIPSCompat::and32(Imm32 imm, Register dest) +{ + ma_and(dest, imm); +} + +void +MacroAssemblerMIPSCompat::and32(Imm32 imm, const Address &dest) +{ + load32(dest, SecondScratchReg); + ma_and(SecondScratchReg, imm); + store32(SecondScratchReg, dest); +} + +void +MacroAssemblerMIPSCompat::or32(Imm32 imm, const Address &dest) +{ + load32(dest, SecondScratchReg); + ma_or(SecondScratchReg, imm); + store32(SecondScratchReg, dest); +} + +void +MacroAssemblerMIPSCompat::xor32(Imm32 imm, Register dest) +{ + ma_xor(dest, imm); +} + +void +MacroAssemblerMIPSCompat::xorPtr(Imm32 imm, Register dest) +{ + ma_xor(dest, imm); +} + +void +MacroAssemblerMIPSCompat::xorPtr(Register src, Register dest) +{ + ma_xor(dest, src); +} + +void +MacroAssemblerMIPSCompat::orPtr(Imm32 imm, Register dest) +{ + ma_or(dest, imm); +} + +void +MacroAssemblerMIPSCompat::orPtr(Register src, Register dest) +{ + ma_or(dest, src); +} + +void +MacroAssemblerMIPSCompat::andPtr(Imm32 imm, Register dest) +{ + ma_and(dest, imm); +} + +void +MacroAssemblerMIPSCompat::andPtr(Register src, Register dest) +{ + ma_and(dest, src); +} + +void +MacroAssemblerMIPSCompat::move32(const Imm32 &imm, const Register &dest) +{ + ma_li(dest, imm); +} + +void +MacroAssemblerMIPSCompat::move32(const Register &src, const Register &dest) +{ + ma_move(dest, src); +} + +void +MacroAssemblerMIPSCompat::movePtr(const Register &src, const Register &dest) +{ + ma_move(dest, src); +} +void +MacroAssemblerMIPSCompat::movePtr(const ImmWord &imm, const Register &dest) +{ + ma_li(dest, Imm32(imm.value)); +} + +void +MacroAssemblerMIPSCompat::movePtr(const ImmGCPtr &imm, const Register &dest) +{ + ma_li(dest, imm); +} +void +MacroAssemblerMIPSCompat::movePtr(const ImmPtr &imm, const Register &dest) +{ + movePtr(ImmWord(uintptr_t(imm.value)), dest); +} +void +MacroAssemblerMIPSCompat::movePtr(const AsmJSImmPtr &imm, const Register &dest) +{ + MOZ_ASSUME_UNREACHABLE("NYI"); +} + +void +MacroAssemblerMIPSCompat::load8ZeroExtend(const Address &address, const Register &dest) +{ + ma_load(dest, address, SizeByte, ZeroExtend); +} + +void +MacroAssemblerMIPSCompat::load8ZeroExtend(const BaseIndex &src, const Register &dest) +{ + ma_load(dest, src, SizeByte, ZeroExtend); +} + +void +MacroAssemblerMIPSCompat::load8SignExtend(const Address &address, const Register &dest) +{ + ma_load(dest, address, SizeByte, SignExtend); +} + +void +MacroAssemblerMIPSCompat::load8SignExtend(const BaseIndex &src, const Register &dest) +{ + ma_load(dest, src, SizeByte, SignExtend); +} + +void +MacroAssemblerMIPSCompat::load16ZeroExtend(const Address &address, const Register &dest) +{ + ma_load(dest, address, SizeHalfWord, ZeroExtend); +} + +void +MacroAssemblerMIPSCompat::load16ZeroExtend(const BaseIndex &src, const Register &dest) +{ + ma_load(dest, src, SizeHalfWord, ZeroExtend); +} + +void +MacroAssemblerMIPSCompat::load16SignExtend(const Address &address, const Register &dest) +{ + ma_load(dest, address, SizeHalfWord, SignExtend); +} + +void +MacroAssemblerMIPSCompat::load16SignExtend(const BaseIndex &src, const Register &dest) +{ + ma_load(dest, src, SizeHalfWord, SignExtend); +} + +void +MacroAssemblerMIPSCompat::load32(const Address &address, const Register &dest) +{ + ma_lw(dest, address); +} + +void +MacroAssemblerMIPSCompat::load32(const BaseIndex &address, const Register &dest) +{ + ma_load(dest, address, SizeWord); +} + +void +MacroAssemblerMIPSCompat::load32(const AbsoluteAddress &address, const Register &dest) +{ + ma_li(ScratchRegister, Imm32((uint32_t)address.addr)); + as_lw(dest, ScratchRegister, 0); +} + +void +MacroAssemblerMIPSCompat::loadPtr(const Address &address, const Register &dest) +{ + ma_lw(dest, address); +} + +void +MacroAssemblerMIPSCompat::loadPtr(const BaseIndex &src, const Register &dest) +{ + load32(src, dest); +} + +void +MacroAssemblerMIPSCompat::loadPtr(const AbsoluteAddress &address, const Register &dest) +{ + ma_li(ScratchRegister, Imm32((uint32_t)address.addr)); + as_lw(dest, ScratchRegister, 0); +} +void +MacroAssemblerMIPSCompat::loadPtr(const AsmJSAbsoluteAddress &address, const Register &dest) +{ + movePtr(AsmJSImmPtr(address.kind()), ScratchRegister); + loadPtr(Address(ScratchRegister, 0x0), dest); +} + +void +MacroAssemblerMIPSCompat::loadPrivate(const Address &address, const Register &dest) +{ + ma_lw(dest, Address(address.base, address.offset + PAYLOAD_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::loadDouble(const Address &address, const FloatRegister &dest) +{ + ma_ld(dest, address); +} + +void +MacroAssemblerMIPSCompat::loadDouble(const BaseIndex &src, const FloatRegister &dest) +{ + computeScaledAddress(src, SecondScratchReg); + ma_ld(dest, Address(SecondScratchReg, src.offset)); +} + +void +MacroAssemblerMIPSCompat::loadFloatAsDouble(const Address &address, const FloatRegister &dest) +{ + ma_ls(dest, address); + as_cvtds(dest, dest); +} + +void +MacroAssemblerMIPSCompat::loadFloatAsDouble(const BaseIndex &src, const FloatRegister &dest) +{ + loadFloat32(src, dest); + as_cvtds(dest, dest); +} + +void +MacroAssemblerMIPSCompat::loadFloat32(const Address &address, const FloatRegister &dest) +{ + ma_ls(dest, address); +} + +void +MacroAssemblerMIPSCompat::loadFloat32(const BaseIndex &src, const FloatRegister &dest) +{ + computeScaledAddress(src, SecondScratchReg); + ma_ls(dest, Address(SecondScratchReg, src.offset)); +} + +void +MacroAssemblerMIPSCompat::store8(const Imm32 &imm, const Address &address) +{ + ma_li(SecondScratchReg, imm); + ma_store(SecondScratchReg, address, SizeByte); +} + +void +MacroAssemblerMIPSCompat::store8(const Register &src, const Address &address) +{ + ma_store(src, address, SizeByte); +} + +void +MacroAssemblerMIPSCompat::store8(const Imm32 &imm, const BaseIndex &dest) +{ + ma_store(imm, dest, SizeByte); +} + +void +MacroAssemblerMIPSCompat::store8(const Register &src, const BaseIndex &dest) +{ + ma_store(src, dest, SizeByte); +} + +void +MacroAssemblerMIPSCompat::store16(const Imm32 &imm, const Address &address) +{ + ma_li(SecondScratchReg, imm); + ma_store(SecondScratchReg, address, SizeHalfWord); +} + +void +MacroAssemblerMIPSCompat::store16(const Register &src, const Address &address) +{ + ma_store(src, address, SizeHalfWord); +} + +void +MacroAssemblerMIPSCompat::store16(const Imm32 &imm, const BaseIndex &dest) +{ + ma_store(imm, dest, SizeHalfWord); +} + +void +MacroAssemblerMIPSCompat::store16(const Register &src, const BaseIndex &address) +{ + ma_store(src, address, SizeHalfWord); +} + +void +MacroAssemblerMIPSCompat::store32(const Register &src, const AbsoluteAddress &address) +{ + storePtr(src, address); +} + +void +MacroAssemblerMIPSCompat::store32(const Register &src, const Address &address) +{ + storePtr(src, address); +} + +void +MacroAssemblerMIPSCompat::store32(const Imm32 &src, const Address &address) +{ + move32(src, ScratchRegister); + storePtr(ScratchRegister, address); +} + +void +MacroAssemblerMIPSCompat::store32(const Imm32 &imm, const BaseIndex &dest) +{ + ma_store(imm, dest, SizeWord); +} + +void +MacroAssemblerMIPSCompat::store32(const Register &src, const BaseIndex &dest) +{ + ma_store(src, dest, SizeWord); +} + +void +MacroAssemblerMIPSCompat::storePtr(ImmWord imm, const Address &address) +{ + ma_li(ScratchRegister, Imm32(imm.value)); + ma_sw(ScratchRegister, address); +} + +void +MacroAssemblerMIPSCompat::storePtr(ImmPtr imm, const Address &address) +{ + storePtr(ImmWord(uintptr_t(imm.value)), address); +} + +void +MacroAssemblerMIPSCompat::storePtr(ImmGCPtr imm, const Address &address) +{ + ma_li(ScratchRegister, imm); + ma_sw(ScratchRegister, address); +} + +void +MacroAssemblerMIPSCompat::storePtr(Register src, const Address &address) +{ + ma_sw(src, address); +} + +void +MacroAssemblerMIPSCompat::storePtr(const Register &src, const AbsoluteAddress &dest) +{ + ma_li(ScratchRegister, Imm32((uint32_t)dest.addr)); + as_sw(src, ScratchRegister, 0); +} + +void +MacroAssemblerMIPSCompat::subPtr(Imm32 imm, const Register dest) +{ + ma_subu(dest, dest, imm); +} + +void +MacroAssemblerMIPSCompat::addPtr(Imm32 imm, const Register dest) +{ + ma_addu(dest, imm); +} + +void +MacroAssemblerMIPSCompat::addPtr(Imm32 imm, const Address &dest) +{ + loadPtr(dest, ScratchRegister); + addPtr(imm, ScratchRegister); + storePtr(ScratchRegister, dest); +} + +void +MacroAssemblerMIPSCompat::branchDouble(DoubleCondition cond, const FloatRegister &lhs, + const FloatRegister &rhs, Label *label) +{ + ma_bc1d(lhs, rhs, label, cond); +} + +void +MacroAssemblerMIPSCompat::branchFloat(DoubleCondition cond, const FloatRegister &lhs, + const FloatRegister &rhs, Label *label) +{ + ma_bc1s(lhs, rhs, label, cond); +} + +// higher level tag testing code +Operand +ToPayload(Operand base) +{ + return Operand(Register::FromCode(base.base()), base.disp() + PAYLOAD_OFFSET); +} + +Operand +ToType(Operand base) +{ + return Operand(Register::FromCode(base.base()), base.disp() + TAG_OFFSET); +} + +void +MacroAssemblerMIPSCompat::branchTestGCThing(Condition cond, const Address &address, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + extractTag(address, SecondScratchReg); + ma_b(SecondScratchReg, ImmTag(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET), label, + (cond == Equal) ? AboveOrEqual : Below); +} +void +MacroAssemblerMIPSCompat::branchTestGCThing(Condition cond, const BaseIndex &src, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + extractTag(src, SecondScratchReg); + ma_b(SecondScratchReg, ImmTag(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET), label, + (cond == Equal) ? AboveOrEqual : Below); +} + +void +MacroAssemblerMIPSCompat::branchTestPrimitive(Condition cond, const ValueOperand &value, + Label *label) +{ + branchTestPrimitive(cond, value.typeReg(), label); +} +void +MacroAssemblerMIPSCompat::branchTestPrimitive(Condition cond, const Register &tag, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + ma_b(tag, ImmTag(JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET), label, + (cond == Equal) ? Below : AboveOrEqual); +} + +void +MacroAssemblerMIPSCompat::branchTestInt32(Condition cond, const ValueOperand &value, Label *label) +{ + MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); + ma_b(value.typeReg(), ImmType(JSVAL_TYPE_INT32), label, cond); +} + +void +MacroAssemblerMIPSCompat::branchTestInt32(Condition cond, const Register &tag, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + ma_b(tag, ImmTag(JSVAL_TAG_INT32), label, cond); +} + +void +MacroAssemblerMIPSCompat::branchTestInt32(Condition cond, const Address &address, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + extractTag(address, SecondScratchReg); + ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_INT32), label, cond); +} + +void +MacroAssemblerMIPSCompat::branchTestInt32(Condition cond, const BaseIndex &src, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + extractTag(src, SecondScratchReg); + ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_INT32), label, cond); +} + +void +MacroAssemblerMIPSCompat:: branchTestBoolean(Condition cond, const ValueOperand &value, + Label *label) +{ + MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); + ma_b(value.typeReg(), ImmType(JSVAL_TYPE_BOOLEAN), label, cond); +} + +void +MacroAssemblerMIPSCompat:: branchTestBoolean(Condition cond, const Register &tag, Label *label) +{ + MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); + ma_b(tag, ImmType(JSVAL_TYPE_BOOLEAN), label, cond); +} + +void +MacroAssemblerMIPSCompat::branchTestBoolean(Condition cond, const BaseIndex &src, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + extractTag(src, SecondScratchReg); + ma_b(SecondScratchReg, ImmType(JSVAL_TYPE_BOOLEAN), label, cond); +} + +void +MacroAssemblerMIPSCompat::branchTestDouble(Condition cond, const ValueOperand &value, Label *label) +{ + MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); + Assembler::Condition actual = (cond == Equal) ? Below : AboveOrEqual; + ma_b(value.typeReg(), ImmTag(JSVAL_TAG_CLEAR), label, actual); +} + +void +MacroAssemblerMIPSCompat::branchTestDouble(Condition cond, const Register &tag, Label *label) +{ + MOZ_ASSERT(cond == Assembler::Equal || cond == NotEqual); + Condition actual = (cond == Equal) ? Below : AboveOrEqual; + ma_b(tag, ImmTag(JSVAL_TAG_CLEAR), label, actual); +} + +void +MacroAssemblerMIPSCompat::branchTestDouble(Condition cond, const Address &address, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + extractTag(address, SecondScratchReg); + ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_CLEAR), label, cond); +} + +void +MacroAssemblerMIPSCompat::branchTestDouble(Condition cond, const BaseIndex &src, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + Condition actual = (cond == Equal) ? Below : AboveOrEqual; + extractTag(src, SecondScratchReg); + ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_CLEAR), label, actual); +} + +void +MacroAssemblerMIPSCompat::branchTestNull(Condition cond, const ValueOperand &value, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + ma_b(value.typeReg(), ImmType(JSVAL_TYPE_NULL), label, cond); +} + +void +MacroAssemblerMIPSCompat::branchTestNull(Condition cond, const Register &tag, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + ma_b(tag, ImmTag(JSVAL_TAG_NULL), label, cond); +} + +void +MacroAssemblerMIPSCompat::branchTestNull(Condition cond, const BaseIndex &src, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + extractTag(src, SecondScratchReg); + ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_NULL), label, cond); +} + + +void +MacroAssemblerMIPSCompat::branchTestObject(Condition cond, const ValueOperand &value, Label *label) +{ + branchTestObject(cond, value.typeReg(), label); +} + +void +MacroAssemblerMIPSCompat::branchTestObject(Condition cond, const Register &tag, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + ma_b(tag, ImmTag(JSVAL_TAG_OBJECT), label, cond); +} + +void +MacroAssemblerMIPSCompat::branchTestObject(Condition cond, const BaseIndex &src, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + extractTag(src, SecondScratchReg); + ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_OBJECT), label, cond); +} + + +void +MacroAssemblerMIPSCompat::branchTestString(Condition cond, const ValueOperand &value, Label *label) +{ + branchTestString(cond, value.typeReg(), label); +} + +void +MacroAssemblerMIPSCompat::branchTestString(Condition cond, const Register &tag, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + ma_b(tag, ImmTag(JSVAL_TAG_STRING), label, cond); +} + +void +MacroAssemblerMIPSCompat::branchTestString(Condition cond, const BaseIndex &src, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + extractTag(src, SecondScratchReg); + ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_STRING), label, cond); +} + +void +MacroAssemblerMIPSCompat::branchTestUndefined(Condition cond, const ValueOperand &value, + Label *label) +{ + MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); + ma_b(value.typeReg(), ImmType(JSVAL_TYPE_UNDEFINED), label, cond); +} + +void +MacroAssemblerMIPSCompat::branchTestUndefined(Condition cond, const Register &tag, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + ma_b(tag, ImmTag(JSVAL_TAG_UNDEFINED), label, cond); +} + +void +MacroAssemblerMIPSCompat::branchTestUndefined(Condition cond, const BaseIndex &src, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + extractTag(src, SecondScratchReg); + ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_UNDEFINED), label, cond); +} + +void +MacroAssemblerMIPSCompat::branchTestUndefined(Condition cond, const Address &address, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + extractTag(address, SecondScratchReg); + ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_UNDEFINED), label, cond); +} + + +void +MacroAssemblerMIPSCompat::branchTestNumber(Condition cond, const ValueOperand &value, Label *label) +{ + branchTestNumber(cond, value.typeReg(), label); +} + +void +MacroAssemblerMIPSCompat::branchTestNumber(Condition cond, const Register &tag, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + ma_b(tag, ImmTag(JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET), label, + cond == Equal ? BelowOrEqual : Above); +} + +void +MacroAssemblerMIPSCompat::branchTestMagic(Condition cond, const ValueOperand &value, Label *label) +{ + branchTestMagic(cond, value.typeReg(), label); +} + +void +MacroAssemblerMIPSCompat::branchTestMagic(Condition cond, const Register &tag, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + ma_b(tag, ImmTag(JSVAL_TAG_MAGIC), label, cond); +} + +void +MacroAssemblerMIPSCompat::branchTestMagic(Condition cond, const Address &address, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + extractTag(address, SecondScratchReg); + ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_MAGIC), label, cond); +} + +void +MacroAssemblerMIPSCompat::branchTestMagic(Condition cond, const BaseIndex &src, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + extractTag(src, SecondScratchReg); + ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_MAGIC), label, cond); +} + +void +MacroAssemblerMIPSCompat::branchTestValue(Condition cond, const ValueOperand &value, + const Value &v, Label *label) +{ + moveData(v, ScratchRegister); + + if (cond == Equal) { + Label done; + ma_b(value.payloadReg(), ScratchRegister, &done, NotEqual, ShortJump); + { + ma_b(value.typeReg(), Imm32(getType(v)), label, Equal); + } + bind(&done); + } else { + MOZ_ASSERT(cond == NotEqual); + ma_b(value.payloadReg(), ScratchRegister, label, NotEqual); + + ma_b(value.typeReg(), Imm32(getType(v)), label, NotEqual); + } +} + +void +MacroAssemblerMIPSCompat::branchTestValue(Condition cond, const Address &valaddr, + const ValueOperand &value, Label *label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + + // Load tag. + ma_lw(ScratchRegister, Address(valaddr.base, valaddr.offset + TAG_OFFSET)); + branchPtr(cond, ScratchRegister, value.typeReg(), label); + + // Load payload + ma_lw(ScratchRegister, Address(valaddr.base, valaddr.offset + PAYLOAD_OFFSET)); + branchPtr(cond, ScratchRegister, value.payloadReg(), label); +} + +// unboxing code +void +MacroAssemblerMIPSCompat::unboxInt32(const ValueOperand &operand, const Register &dest) +{ + ma_move(dest, operand.payloadReg()); +} + +void +MacroAssemblerMIPSCompat::unboxInt32(const Address &src, const Register &dest) +{ + ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::unboxBoolean(const ValueOperand &operand, const Register &dest) +{ + ma_move(dest, operand.payloadReg()); +} + +void +MacroAssemblerMIPSCompat::unboxBoolean(const Address &src, const Register &dest) +{ + ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::unboxDouble(const ValueOperand &operand, const FloatRegister &dest) +{ + MOZ_ASSERT(dest != ScratchFloatReg); + moveToDoubleLo(operand.payloadReg(), dest); + moveToDoubleHi(operand.typeReg(), dest); +} + +void +MacroAssemblerMIPSCompat::unboxDouble(const Address &src, const FloatRegister &dest) +{ + ma_lw(ScratchRegister, Address(src.base, src.offset + PAYLOAD_OFFSET)); + moveToDoubleLo(ScratchRegister, dest); + ma_lw(ScratchRegister, Address(src.base, src.offset + TAG_OFFSET)); + moveToDoubleHi(ScratchRegister, dest); +} + +void +MacroAssemblerMIPSCompat::unboxString(const ValueOperand &operand, const Register &dest) +{ + ma_move(dest, operand.payloadReg()); +} + +void +MacroAssemblerMIPSCompat::unboxString(const Address &src, const Register &dest) +{ + ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::unboxObject(const ValueOperand &src, const Register &dest) +{ + ma_move(dest, src.payloadReg()); +} + +void +MacroAssemblerMIPSCompat::unboxValue(const ValueOperand &src, AnyRegister dest) +{ + if (dest.isFloat()) { + Label notInt32, end; + branchTestInt32(Assembler::NotEqual, src, ¬Int32); + convertInt32ToDouble(src.payloadReg(), dest.fpu()); + ma_b(&end, ShortJump); + bind(¬Int32); + unboxDouble(src, dest.fpu()); + bind(&end); + } else if (src.payloadReg() != dest.gpr()) { + ma_move(dest.gpr(), src.payloadReg()); + } +} + +void +MacroAssemblerMIPSCompat::unboxPrivate(const ValueOperand &src, Register dest) +{ + ma_move(dest, src.payloadReg()); +} + +void +MacroAssemblerMIPSCompat::boxDouble(const FloatRegister &src, const ValueOperand &dest) +{ + moveFromDoubleLo(src, dest.payloadReg()); + moveFromDoubleHi(src, dest.typeReg()); +} + +void +MacroAssemblerMIPSCompat::boxNonDouble(JSValueType type, const Register &src, + const ValueOperand &dest) +{ + if (src != dest.payloadReg()) + ma_move(dest.payloadReg(), src); + ma_li(dest.typeReg(), ImmType(type)); +} + +void +MacroAssemblerMIPSCompat::boolValueToDouble(const ValueOperand &operand, const FloatRegister &dest) +{ + convertBoolToInt32(ScratchRegister, operand.payloadReg()); + convertInt32ToDouble(ScratchRegister, dest); +} + +void +MacroAssemblerMIPSCompat::int32ValueToDouble(const ValueOperand &operand, + const FloatRegister &dest) +{ + convertInt32ToDouble(operand.payloadReg(), dest); +} + +void +MacroAssemblerMIPSCompat::boolValueToFloat32(const ValueOperand &operand, + const FloatRegister &dest) +{ + + convertBoolToInt32(ScratchRegister, operand.payloadReg()); + convertInt32ToFloat32(ScratchRegister, dest); +} + +void +MacroAssemblerMIPSCompat::int32ValueToFloat32(const ValueOperand &operand, + const FloatRegister &dest) +{ + convertInt32ToFloat32(operand.payloadReg(), dest); +} + +void +MacroAssemblerMIPSCompat::loadConstantFloat32(float f, const FloatRegister &dest) +{ + ma_lis(dest, f); +} + +void +MacroAssemblerMIPSCompat::loadInt32OrDouble(const Address &src, const FloatRegister &dest) +{ + Label notInt32, end; + // If it's an int, convert it to double. + ma_lw(SecondScratchReg, Address(src.base, src.offset + TAG_OFFSET)); + branchTestInt32(Assembler::NotEqual, SecondScratchReg, ¬Int32); + ma_lw(SecondScratchReg, Address(src.base, src.offset + PAYLOAD_OFFSET)); + convertInt32ToDouble(SecondScratchReg, dest); + ma_b(&end, ShortJump); + + // Not an int, just load as double. + bind(¬Int32); + ma_ld(dest, src); + bind(&end); +} + +void +MacroAssemblerMIPSCompat::loadInt32OrDouble(Register base, Register index, + const FloatRegister &dest, int32_t shift) +{ + Label notInt32, end; + + // If it's an int, convert it to double. + + computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg); + // Since we only have one scratch, we need to stomp over it with the tag. + load32(Address(SecondScratchReg, TAG_OFFSET), SecondScratchReg); + branchTestInt32(Assembler::NotEqual, SecondScratchReg, ¬Int32); + + computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg); + load32(Address(SecondScratchReg, PAYLOAD_OFFSET), SecondScratchReg); + convertInt32ToDouble(SecondScratchReg, dest); + ma_b(&end, ShortJump); + + // Not an int, just load as double. + bind(¬Int32); + // First, recompute the offset that had been stored in the scratch register + // since the scratch register was overwritten loading in the type. + computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg); + loadDouble(Address(SecondScratchReg, 0), dest); + bind(&end); +} + +void +MacroAssemblerMIPSCompat::loadConstantDouble(double dp, const FloatRegister &dest) +{ + ma_lid(dest, dp); +} + +void +MacroAssemblerMIPSCompat::branchTestInt32Truthy(bool b, const ValueOperand &value, Label *label) +{ + ma_and(ScratchRegister, value.payloadReg(), value.payloadReg()); + ma_b(ScratchRegister, ScratchRegister, label, b ? NonZero : Zero); +} + +void +MacroAssemblerMIPSCompat::branchTestStringTruthy(bool b, const ValueOperand &value, Label *label) +{ + Register string = value.payloadReg(); + size_t mask = (0xFFFFFFFF << JSString::LENGTH_SHIFT); + ma_lw(SecondScratchReg, Address(string, JSString::offsetOfLengthAndFlags())); + + // Use SecondScratchReg because ma_and will clobber ScratchRegister + ma_and(ScratchRegister, SecondScratchReg, Imm32(mask)); + ma_b(ScratchRegister, ScratchRegister, label, b ? NonZero : Zero); +} + +void +MacroAssemblerMIPSCompat::branchTestDoubleTruthy(bool b, const FloatRegister &value, Label *label) +{ + ma_lid(ScratchFloatReg, 0.0); + DoubleCondition cond = b ? DoubleNotEqual : DoubleEqualOrUnordered; + ma_bc1d(value, ScratchFloatReg, label, cond); +} + +void +MacroAssemblerMIPSCompat::branchTestBooleanTruthy(bool b, const ValueOperand &operand, + Label *label) +{ + ma_b(operand.payloadReg(), operand.payloadReg(), label, b ? NonZero : Zero); +} + +Register +MacroAssemblerMIPSCompat::extractObject(const Address &address, Register scratch) +{ + ma_lw(scratch, Address(address.base, address.offset + PAYLOAD_OFFSET)); + return scratch; +} + +Register +MacroAssemblerMIPSCompat::extractTag(const Address &address, Register scratch) +{ + ma_lw(scratch, Address(address.base, address.offset + TAG_OFFSET)); + return scratch; +} + +Register +MacroAssemblerMIPSCompat::extractTag(const BaseIndex &address, Register scratch) +{ + computeScaledAddress(address, scratch); + return extractTag(Address(scratch, address.offset), scratch); +} + + +uint32_t +MacroAssemblerMIPSCompat::getType(const Value &val) +{ + jsval_layout jv = JSVAL_TO_IMPL(val); + return jv.s.tag; +} + +void +MacroAssemblerMIPSCompat::moveData(const Value &val, Register data) +{ + jsval_layout jv = JSVAL_TO_IMPL(val); + if (val.isMarkable()) + ma_li(data, ImmGCPtr(reinterpret_cast<gc::Cell *>(val.toGCThing()))); + else + ma_li(data, Imm32(jv.s.payload.i32)); +} + +void +MacroAssemblerMIPSCompat::moveValue(const Value &val, Register type, Register data) +{ + MOZ_ASSERT(type != data); + ma_li(type, Imm32(getType(val))); + moveData(val, data); +} +void +MacroAssemblerMIPSCompat::moveValue(const Value &val, const ValueOperand &dest) +{ + moveValue(val, dest.typeReg(), dest.payloadReg()); +} + +CodeOffsetJump +MacroAssemblerMIPSCompat::jumpWithPatch(RepatchLabel *label) +{ + // Only one branch per label. + MOZ_ASSERT(!label->used()); + uint32_t dest = label->bound() ? label->offset() : LabelBase::INVALID_OFFSET; + + BufferOffset bo = nextOffset(); + label->use(bo.getOffset()); + addLongJump(bo); + ma_liPatchable(ScratchRegister, Imm32(dest)); + as_jr(ScratchRegister); + as_nop(); + return CodeOffsetJump(bo.getOffset()); +} + + +///////////////////////////////////////////////////////////////// +// X86/X64-common/ARM/MIPS interface. +///////////////////////////////////////////////////////////////// +void +MacroAssemblerMIPSCompat::storeValue(ValueOperand val, Operand dst) +{ + storeValue(val, Address(Register::FromCode(dst.base()), dst.disp())); +} + +void +MacroAssemblerMIPSCompat::storeValue(ValueOperand val, const BaseIndex &dest) +{ + computeScaledAddress(dest, SecondScratchReg); + storeValue(val, Address(SecondScratchReg, dest.offset)); +} + +void +MacroAssemblerMIPSCompat::storeValue(JSValueType type, Register reg, BaseIndex dest) +{ + computeScaledAddress(dest, ScratchRegister); + + // Make sure that ma_sw doesn't clobber ScratchRegister + int32_t offset = dest.offset; + if (!Imm16::isInSignedRange(offset)) { + ma_li(SecondScratchReg, Imm32(offset)); + as_addu(ScratchRegister, ScratchRegister, SecondScratchReg); + offset = 0; + } + + storeValue(type, reg, Address(ScratchRegister, offset)); +} + +void +MacroAssemblerMIPSCompat::storeValue(ValueOperand val, const Address &dest) +{ + ma_sw(val.payloadReg(), Address(dest.base, dest.offset + PAYLOAD_OFFSET)); + ma_sw(val.typeReg(), Address(dest.base, dest.offset + TAG_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::storeValue(JSValueType type, Register reg, Address dest) +{ + MOZ_ASSERT(dest.base != SecondScratchReg); + + ma_sw(reg, Address(dest.base, dest.offset + PAYLOAD_OFFSET)); + ma_li(SecondScratchReg, ImmTag(JSVAL_TYPE_TO_TAG(type))); + ma_sw(SecondScratchReg, Address(dest.base, dest.offset + TAG_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::storeValue(const Value &val, Address dest) +{ + MOZ_ASSERT(dest.base != SecondScratchReg); + + ma_li(SecondScratchReg, Imm32(getType(val))); + ma_sw(SecondScratchReg, Address(dest.base, dest.offset + TAG_OFFSET)); + moveData(val, SecondScratchReg); + ma_sw(SecondScratchReg, Address(dest.base, dest.offset + PAYLOAD_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::storeValue(const Value &val, BaseIndex dest) +{ + computeScaledAddress(dest, ScratchRegister); + + // Make sure that ma_sw doesn't clobber ScratchRegister + int32_t offset = dest.offset; + if (!Imm16::isInSignedRange(offset)) { + ma_li(SecondScratchReg, Imm32(offset)); + as_addu(ScratchRegister, ScratchRegister, SecondScratchReg); + offset = 0; + } + storeValue(val, Address(ScratchRegister, offset)); +} + +void +MacroAssemblerMIPSCompat::loadValue(const BaseIndex &addr, ValueOperand val) +{ + computeScaledAddress(addr, SecondScratchReg); + loadValue(Address(SecondScratchReg, addr.offset), val); +} + +void +MacroAssemblerMIPSCompat::loadValue(Address src, ValueOperand val) +{ + // Ensure that loading the payload does not erase the pointer to the + // Value in memory. + if (src.base != val.payloadReg()) { + ma_lw(val.payloadReg(), Address(src.base, src.offset + PAYLOAD_OFFSET)); + ma_lw(val.typeReg(), Address(src.base, src.offset + TAG_OFFSET)); + } else { + ma_lw(val.typeReg(), Address(src.base, src.offset + TAG_OFFSET)); + ma_lw(val.payloadReg(), Address(src.base, src.offset + PAYLOAD_OFFSET)); + } +} + +void +MacroAssemblerMIPSCompat::tagValue(JSValueType type, Register payload, ValueOperand dest) +{ + MOZ_ASSERT(payload != dest.typeReg()); + ma_li(dest.typeReg(), ImmType(type)); + if (payload != dest.payloadReg()) + ma_move(dest.payloadReg(), payload); +} + +void +MacroAssemblerMIPSCompat::pushValue(ValueOperand val) +{ + // Allocate stack slots for type and payload. One for each. + ma_subu(StackPointer, StackPointer, Imm32(sizeof(Value))); + // Store type and payload. + storeValue(val, Address(StackPointer, 0)); +} + +void +MacroAssemblerMIPSCompat::pushValue(const Address &addr) +{ + // Allocate stack slots for type and payload. One for each. + ma_subu(StackPointer, StackPointer, Imm32(sizeof(Value))); + // Store type and payload. + ma_lw(ScratchRegister, Address(addr.base, addr.offset + TAG_OFFSET)); + ma_sw(ScratchRegister, Address(StackPointer, TAG_OFFSET)); + ma_lw(ScratchRegister, Address(addr.base, addr.offset + PAYLOAD_OFFSET)); + ma_sw(ScratchRegister, Address(StackPointer, PAYLOAD_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::popValue(ValueOperand val) +{ + // Load payload and type. + as_lw(val.payloadReg(), StackPointer, PAYLOAD_OFFSET); + as_lw(val.typeReg(), StackPointer, TAG_OFFSET); + // Free stack. + as_addiu(StackPointer, StackPointer, sizeof(Value)); +} + +void +MacroAssemblerMIPSCompat::storePayload(const Value &val, Address dest) +{ + moveData(val, SecondScratchReg); + ma_sw(SecondScratchReg, Address(dest.base, dest.offset + PAYLOAD_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::storePayload(Register src, Address dest) +{ + ma_sw(src, Address(dest.base, dest.offset + PAYLOAD_OFFSET)); + return; +} + +void +MacroAssemblerMIPSCompat::storePayload(const Value &val, Register base, Register index, + int32_t shift) +{ + computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg); + + moveData(val, ScratchRegister); + + as_sw(ScratchRegister, SecondScratchReg, NUNBOX32_PAYLOAD_OFFSET); +} + +void +MacroAssemblerMIPSCompat::storePayload(Register src, Register base, Register index, int32_t shift) +{ + computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg); + as_sw(src, SecondScratchReg, NUNBOX32_PAYLOAD_OFFSET); +} + +void +MacroAssemblerMIPSCompat::storeTypeTag(ImmTag tag, Address dest) +{ + ma_li(SecondScratchReg, tag); + ma_sw(SecondScratchReg, Address(dest.base, dest.offset + TAG_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::storeTypeTag(ImmTag tag, Register base, Register index, int32_t shift) +{ + computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg); + ma_li(ScratchRegister, tag); + as_sw(ScratchRegister, SecondScratchReg, TAG_OFFSET); +} + +void +MacroAssemblerMIPSCompat::linkExitFrame() +{ + uint8_t *dest = (uint8_t*)GetIonContext()->runtime->addressOfIonTop(); + movePtr(ImmPtr(dest), ScratchRegister); + ma_sw(StackPointer, Address(ScratchRegister, 0)); +} + +void +MacroAssemblerMIPSCompat::linkParallelExitFrame(const Register &pt) +{ + ma_sw(StackPointer, Address(pt, offsetof(PerThreadData, ionTop))); +} + +// This macrosintruction calls the ion code and pushes the return address to +// the stack in the case when stack is alligned. +void +MacroAssemblerMIPS::ma_callIon(const Register r) +{ + // This is a MIPS hack to push return address during jalr delay slot. + as_addiu(StackPointer, StackPointer, -2 * sizeof(intptr_t)); + as_jalr(r); + as_sw(ra, StackPointer, 0); +} + +// This macrosintruction calls the ion code and pushes the return address to +// the stack in the case when stack is not alligned. +void +MacroAssemblerMIPS::ma_callIonHalfPush(const Register r) +{ + // This is a MIPS hack to push return address during jalr delay slot. + as_addiu(StackPointer, StackPointer, -sizeof(intptr_t)); + as_jalr(r); + as_sw(ra, StackPointer, 0); +} + +void +MacroAssemblerMIPS::ma_call(ImmPtr dest) +{ + ma_liPatchable(CallReg, dest); + as_jalr(CallReg); + as_nop(); +} + +void +MacroAssemblerMIPS::ma_jump(ImmPtr dest) +{ + ma_liPatchable(ScratchRegister, dest); + as_jr(ScratchRegister); + as_nop(); +} + +void +MacroAssemblerMIPSCompat::breakpoint() +{ + as_break(0); +} + +void +MacroAssemblerMIPSCompat::ensureDouble(const ValueOperand &source, FloatRegister dest, + Label *failure) +{ + Label isDouble, done; + branchTestDouble(Assembler::Equal, source.typeReg(), &isDouble); + branchTestInt32(Assembler::NotEqual, source.typeReg(), failure); + + convertInt32ToDouble(source.payloadReg(), dest); + jump(&done); + + bind(&isDouble); + unboxDouble(source, dest); + + bind(&done); +} + +void +MacroAssemblerMIPSCompat::setupABICall(uint32_t args) +{ + MOZ_ASSERT(!inCall_); + inCall_ = true; + args_ = args; + passedArgs_ = 0; + + usedArgSlots_ = 0; + firstArgType = MoveOp::GENERAL; +} + +void +MacroAssemblerMIPSCompat::setupAlignedABICall(uint32_t args) +{ + setupABICall(args); + + dynamicAlignment_ = false; +} + +void +MacroAssemblerMIPSCompat::setupUnalignedABICall(uint32_t args, const Register &scratch) +{ + setupABICall(args); + dynamicAlignment_ = true; + + ma_move(scratch, StackPointer); + + // Force sp to be aligned + ma_subu(StackPointer, StackPointer, Imm32(sizeof(uint32_t))); + ma_and(StackPointer, StackPointer, Imm32(~(StackAlignment - 1))); + as_sw(scratch, StackPointer, 0); +} + +void +MacroAssemblerMIPSCompat::passABIArg(const MoveOperand &from, MoveOp::Type type) +{ + ++passedArgs_; + if (!enoughMemory_) + return; + switch (type) { + case MoveOp::FLOAT32: + if (!usedArgSlots_) { + if (from.floatReg() != f12) + enoughMemory_ = moveResolver_.addMove(from, MoveOperand(f12), type); + firstArgType = MoveOp::FLOAT32; + } else if ((usedArgSlots_ == 1 && firstArgType == MoveOp::FLOAT32) || + (usedArgSlots_ == 2 && firstArgType == MoveOp::DOUBLE)) { + if (from.floatReg() != f14) + enoughMemory_ = moveResolver_.addMove(from, MoveOperand(f14), type); + } else { + Register destReg; + if (GetIntArgReg(usedArgSlots_, &destReg)) { + if (from.isGeneralReg() && from.reg() == destReg) { + // Nothing to do. Value is in the right register already + } else { + enoughMemory_ = moveResolver_.addMove(from, MoveOperand(destReg), type); + } + } else { + uint32_t disp = GetArgStackDisp(usedArgSlots_); + enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), type); + } + } + usedArgSlots_++; + break; + case MoveOp::DOUBLE: + if (!usedArgSlots_) { + if (from.floatReg() != f12) + enoughMemory_ = moveResolver_.addMove(from, MoveOperand(f12), type); + usedArgSlots_ = 2; + firstArgType = MoveOp::DOUBLE; + } else if (usedArgSlots_ <= 2) { + if ((usedArgSlots_ == 1 && firstArgType == MoveOp::FLOAT32) || + (usedArgSlots_ == 2 && firstArgType == MoveOp::DOUBLE)) { + if (from.floatReg() != f14) + enoughMemory_ = moveResolver_.addMove(from, MoveOperand(f14), type); + } else { + // Create two moves so that cycles are found. Move emitter + // will have special case to handle this. + enoughMemory_ = moveResolver_.addMove(from, MoveOperand(a2), type); + enoughMemory_ = moveResolver_.addMove(from, MoveOperand(a3), type); + } + usedArgSlots_ = 4; + } else { + // Align if necessary + usedArgSlots_ += usedArgSlots_ % 2; + + uint32_t disp = GetArgStackDisp(usedArgSlots_); + enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), type); + usedArgSlots_ += 2; + } + break; + case MoveOp::GENERAL: + Register destReg; + if (GetIntArgReg(usedArgSlots_, &destReg)) { + if (from.isGeneralReg() && from.reg() == destReg) { + // Nothing to do. Value is in the right register already + } else { + enoughMemory_ = moveResolver_.addMove(from, MoveOperand(destReg), type); + } + } else { + uint32_t disp = GetArgStackDisp(usedArgSlots_); + enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), type); + } + usedArgSlots_++; + break; + default: + MOZ_ASSUME_UNREACHABLE("Unexpected argument type"); + } +} + +void +MacroAssemblerMIPSCompat::passABIArg(const Register ®) +{ + passABIArg(MoveOperand(reg), MoveOp::GENERAL); +} + +void +MacroAssemblerMIPSCompat::passABIArg(const FloatRegister &freg, MoveOp::Type type) +{ + passABIArg(MoveOperand(freg), type); +} + +void MacroAssemblerMIPSCompat::checkStackAlignment() +{ +#ifdef DEBUG + Label aligned; + as_andi(ScratchRegister, sp, StackAlignment - 1); + ma_b(ScratchRegister, zero, &aligned, Equal, ShortJump); + as_break(MAX_BREAK_CODE); + bind(&aligned); +#endif +} + +void +MacroAssemblerMIPSCompat::alignPointerUp(Register src, Register dest, uint32_t alignment) +{ + MOZ_ASSERT(alignment > 1); + ma_addu(dest, src, Imm32(alignment - 1)); + ma_and(dest, dest, Imm32(~(alignment - 1))); +} + +void +MacroAssemblerMIPSCompat::callWithABIPre(uint32_t *stackAdjust) +{ + MOZ_ASSERT(inCall_); + + // Reserve place for $ra. + *stackAdjust = sizeof(intptr_t); + + *stackAdjust += usedArgSlots_ > NumIntArgRegs ? + usedArgSlots_ * sizeof(intptr_t) : + NumIntArgRegs * sizeof(intptr_t); + + if (dynamicAlignment_) { + *stackAdjust += ComputeByteAlignment(*stackAdjust, StackAlignment); + } else { + *stackAdjust += ComputeByteAlignment(framePushed_ + *stackAdjust, StackAlignment); + } + + reserveStack(*stackAdjust); + + // Save $ra because call is going to clobber it. Restore it in + // callWithABIPost. NOTE: This is needed for calls from BaselineIC. + // Maybe we can do this differently. + ma_sw(ra, Address(StackPointer, *stackAdjust - sizeof(intptr_t))); + + // Position all arguments. + { + enoughMemory_ = enoughMemory_ && moveResolver_.resolve(); + if (!enoughMemory_) + return; + + MoveEmitter emitter(*this); + emitter.emit(moveResolver_); + emitter.finish(); + } + + checkStackAlignment(); +} + +void +MacroAssemblerMIPSCompat::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result) +{ + // Restore ra value (as stored in callWithABIPre()). + ma_lw(ra, Address(StackPointer, stackAdjust - sizeof(intptr_t))); + + if (dynamicAlignment_) { + // Restore sp value from stack (as stored in setupUnalignedABICall()). + ma_lw(StackPointer, Address(StackPointer, stackAdjust)); + // Use adjustFrame instead of freeStack because we already restored sp. + adjustFrame(-stackAdjust); + } else { + freeStack(stackAdjust); + } + + MOZ_ASSERT(inCall_); + inCall_ = false; +} + +void +MacroAssemblerMIPSCompat::callWithABI(void *fun, MoveOp::Type result) +{ + uint32_t stackAdjust; + callWithABIPre(&stackAdjust); + ma_call(ImmPtr(fun)); + callWithABIPost(stackAdjust, result); +} + +void +MacroAssemblerMIPSCompat::callWithABI(AsmJSImmPtr imm, MoveOp::Type result) +{ + uint32_t stackAdjust; + callWithABIPre(&stackAdjust); + call(imm); + callWithABIPost(stackAdjust, result); +} + +void +MacroAssemblerMIPSCompat::callWithABI(const Address &fun, MoveOp::Type result) +{ + // Load the callee in t9, no instruction between the lw and call + // should clobber it. Note that we can't use fun.base because it may + // be one of the IntArg registers clobbered before the call. + ma_lw(t9, Address(fun.base, fun.offset)); + uint32_t stackAdjust; + callWithABIPre(&stackAdjust); + call(t9); + callWithABIPost(stackAdjust, result); + +} + +void +MacroAssemblerMIPSCompat::handleFailureWithHandler(void *handler) +{ + // Reserve space for exception information. + int size = (sizeof(ResumeFromException) + StackAlignment) & ~(StackAlignment - 1); + ma_subu(StackPointer, StackPointer, Imm32(size)); + ma_move(a0, StackPointer); // Use a0 since it is a first function argument + + // Ask for an exception handler. + setupUnalignedABICall(1, a1); + passABIArg(a0); + callWithABI(handler); + + JitCode *excTail = GetIonContext()->runtime->jitRuntime()->getExceptionTail(); + branch(excTail); +} + +void +MacroAssemblerMIPSCompat::handleFailureWithHandlerTail() +{ + Label entryFrame; + Label catch_; + Label finally; + Label return_; + Label bailout; + + // Already clobbered a0, so use it... + ma_lw(a0, Address(StackPointer, offsetof(ResumeFromException, kind))); + branch32(Assembler::Equal, a0, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame); + branch32(Assembler::Equal, a0, Imm32(ResumeFromException::RESUME_CATCH), &catch_); + branch32(Assembler::Equal, a0, Imm32(ResumeFromException::RESUME_FINALLY), &finally); + branch32(Assembler::Equal, a0, Imm32(ResumeFromException::RESUME_FORCED_RETURN), &return_); + branch32(Assembler::Equal, a0, Imm32(ResumeFromException::RESUME_BAILOUT), &bailout); + + breakpoint(); // Invalid kind. + + // No exception handler. Load the error value, load the new stack pointer + // and return from the entry frame. + bind(&entryFrame); + moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); + ma_lw(StackPointer, Address(StackPointer, offsetof(ResumeFromException, stackPointer))); + + // We're going to be returning by the ion calling convention + ma_pop(ra); + as_jr(ra); + as_nop(); + + // If we found a catch handler, this must be a baseline frame. Restore + // state and jump to the catch block. + bind(&catch_); + ma_lw(a0, Address(StackPointer, offsetof(ResumeFromException, target))); + ma_lw(BaselineFrameReg, Address(StackPointer, offsetof(ResumeFromException, framePointer))); + ma_lw(StackPointer, Address(StackPointer, offsetof(ResumeFromException, stackPointer))); + jump(a0); + + // If we found a finally block, this must be a baseline frame. Push + // two values expected by JSOP_RETSUB: BooleanValue(true) and the + // exception. + bind(&finally); + ValueOperand exception = ValueOperand(a1, a2); + loadValue(Address(sp, offsetof(ResumeFromException, exception)), exception); + + ma_lw(a0, Address(sp, offsetof(ResumeFromException, target))); + ma_lw(BaselineFrameReg, Address(sp, offsetof(ResumeFromException, framePointer))); + ma_lw(sp, Address(sp, offsetof(ResumeFromException, stackPointer))); + + pushValue(BooleanValue(true)); + pushValue(exception); + jump(a0); + + // Only used in debug mode. Return BaselineFrame->returnValue() to the + // caller. + bind(&return_); + ma_lw(BaselineFrameReg, Address(StackPointer, offsetof(ResumeFromException, framePointer))); + ma_lw(StackPointer, Address(StackPointer, offsetof(ResumeFromException, stackPointer))); + loadValue(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfReturnValue()), + JSReturnOperand); + ma_move(StackPointer, BaselineFrameReg); + pop(BaselineFrameReg); + ret(); + + // If we are bailing out to baseline to handle an exception, jump to + // the bailout tail stub. + bind(&bailout); + ma_lw(a2, Address(sp, offsetof(ResumeFromException, bailoutInfo))); + ma_li(ReturnReg, Imm32(BAILOUT_RETURN_OK)); + ma_lw(a1, Address(sp, offsetof(ResumeFromException, target))); + jump(a1); +} + +CodeOffsetLabel +MacroAssemblerMIPSCompat::toggledJump(Label *label) +{ + CodeOffsetLabel ret(nextOffset().getOffset()); + ma_b(label); + return ret; +} + +CodeOffsetLabel +MacroAssemblerMIPSCompat::toggledCall(JitCode *target, bool enabled) +{ + BufferOffset bo = nextOffset(); + CodeOffsetLabel offset(bo.getOffset()); + addPendingJump(bo, ImmPtr(target->raw()), Relocation::JITCODE); + ma_liPatchable(ScratchRegister, ImmPtr(target->raw())); + if (enabled) { + as_jalr(ScratchRegister); + as_nop(); + } else { + as_nop(); + as_nop(); + } + MOZ_ASSERT(nextOffset().getOffset() - offset.offset() == ToggledCallSize()); + return offset; +} + +void +MacroAssemblerMIPSCompat::branchPtrInNurseryRange(Register ptr, Register temp, Label *label) +{ + JS_ASSERT(temp != InvalidReg); + const Nursery &nursery = GetIonContext()->runtime->gcNursery(); + + // ptr and temp may be the same register, in which case we mustn't trash it + // before we use its contents. + if (ptr == temp) { + addPtr(ImmWord(-ptrdiff_t(nursery.start())), ptr); + branchPtr(Assembler::Below, ptr, Imm32(Nursery::NurserySize), label); + } else { + movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp); + addPtr(ptr, temp); + branchPtr(Assembler::Below, temp, Imm32(Nursery::NurserySize), label); + } +}
diff --git a/src/third_party/mozjs/js/src/jit/mips/MacroAssembler-mips.h b/src/third_party/mozjs/js/src/jit/mips/MacroAssembler-mips.h new file mode 100644 index 0000000..ed1f566 --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/MacroAssembler-mips.h
@@ -0,0 +1,1156 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_mips_MacroAssembler_mips_h +#define jit_mips_MacroAssembler_mips_h + +#include "jsopcode.h" + +#include "jit/IonCaches.h" +#include "jit/IonFrames.h" +#include "jit/mips/Assembler-mips.h" +#include "jit/MoveResolver.h" + +namespace js { +namespace jit { + +enum LoadStoreSize +{ + SizeByte = 8, + SizeHalfWord = 16, + SizeWord = 32, + SizeDouble = 64 +}; + +enum LoadStoreExtension +{ + ZeroExtend = 0, + SignExtend = 1 +}; + +enum JumpKind +{ + LongJump = 0, + ShortJump = 1 +}; + +struct ImmTag : public Imm32 +{ + ImmTag(JSValueTag mask) + : Imm32(int32_t(mask)) + { } +}; + +struct ImmType : public ImmTag +{ + ImmType(JSValueType type) + : ImmTag(JSVAL_TYPE_TO_TAG(type)) + { } +}; + +static const ValueOperand JSReturnOperand = ValueOperand(JSReturnReg_Type, JSReturnReg_Data); +static const ValueOperand softfpReturnOperand = ValueOperand(v1, v0); + +static Register CallReg = t9; +static const int defaultShift = 3; +static_assert(1 << defaultShift == sizeof(jsval), "The defaultShift is wrong"); + +class MacroAssemblerMIPS : public Assembler +{ + public: + + void convertBoolToInt32(Register source, Register dest); + void convertInt32ToDouble(const Register &src, const FloatRegister &dest); + void convertInt32ToDouble(const Address &src, FloatRegister dest); + void convertUInt32ToDouble(const Register &src, const FloatRegister &dest); + void convertUInt32ToFloat32(const Register &src, const FloatRegister &dest); + void convertDoubleToFloat32(const FloatRegister &src, const FloatRegister &dest); + void branchTruncateDouble(const FloatRegister &src, const Register &dest, Label *fail); + void convertDoubleToInt32(const FloatRegister &src, const Register &dest, Label *fail, + bool negativeZeroCheck = true); + void convertFloat32ToInt32(const FloatRegister &src, const Register &dest, Label *fail, + bool negativeZeroCheck = true); + + void convertFloat32ToDouble(const FloatRegister &src, const FloatRegister &dest); + void branchTruncateFloat32(const FloatRegister &src, const Register &dest, Label *fail); + void convertInt32ToFloat32(const Register &src, const FloatRegister &dest); + void convertInt32ToFloat32(const Address &src, FloatRegister dest); + + + void addDouble(FloatRegister src, FloatRegister dest); + void subDouble(FloatRegister src, FloatRegister dest); + void mulDouble(FloatRegister src, FloatRegister dest); + void divDouble(FloatRegister src, FloatRegister dest); + + void negateDouble(FloatRegister reg); + void inc64(AbsoluteAddress dest); + + public: + + void ma_move(Register rd, Register rs); + + void ma_li(Register dest, const ImmGCPtr &ptr); + + void ma_li(const Register &dest, AbsoluteLabel *label); + + void ma_li(Register dest, Imm32 imm); + void ma_liPatchable(Register dest, Imm32 imm); + void ma_liPatchable(Register dest, ImmPtr imm); + + // Shift operations + void ma_sll(Register rd, Register rt, Imm32 shift); + void ma_srl(Register rd, Register rt, Imm32 shift); + void ma_sra(Register rd, Register rt, Imm32 shift); + void ma_ror(Register rd, Register rt, Imm32 shift); + void ma_rol(Register rd, Register rt, Imm32 shift); + + void ma_sll(Register rd, Register rt, Register shift); + void ma_srl(Register rd, Register rt, Register shift); + void ma_sra(Register rd, Register rt, Register shift); + void ma_ror(Register rd, Register rt, Register shift); + void ma_rol(Register rd, Register rt, Register shift); + + // Negate + void ma_negu(Register rd, Register rs); + + void ma_not(Register rd, Register rs); + + // and + void ma_and(Register rd, Register rs); + void ma_and(Register rd, Register rs, Register rt); + void ma_and(Register rd, Imm32 imm); + void ma_and(Register rd, Register rs, Imm32 imm); + + // or + void ma_or(Register rd, Register rs); + void ma_or(Register rd, Register rs, Register rt); + void ma_or(Register rd, Imm32 imm); + void ma_or(Register rd, Register rs, Imm32 imm); + + // xor + void ma_xor(Register rd, Register rs); + void ma_xor(Register rd, Register rs, Register rt); + void ma_xor(Register rd, Imm32 imm); + void ma_xor(Register rd, Register rs, Imm32 imm); + + // load + void ma_load(const Register &dest, Address address, LoadStoreSize size = SizeWord, + LoadStoreExtension extension = SignExtend); + void ma_load(const Register &dest, const BaseIndex &src, LoadStoreSize size = SizeWord, + LoadStoreExtension extension = SignExtend); + + // store + void ma_store(const Register &data, Address address, LoadStoreSize size = SizeWord, + LoadStoreExtension extension = SignExtend); + void ma_store(const Register &data, const BaseIndex &dest, LoadStoreSize size = SizeWord, + LoadStoreExtension extension = SignExtend); + void ma_store(const Imm32 &imm, const BaseIndex &dest, LoadStoreSize size = SizeWord, + LoadStoreExtension extension = SignExtend); + + void computeScaledAddress(const BaseIndex &address, Register dest); + + void computeEffectiveAddress(const Address &address, Register dest) { + ma_addu(dest, address.base, Imm32(address.offset)); + } + + void computeEffectiveAddress(const BaseIndex &address, Register dest) { + computeScaledAddress(address, dest); + if (address.offset) { + ma_addu(dest, dest, Imm32(address.offset)); + } + } + + // arithmetic based ops + // add + void ma_addu(Register rd, Register rs, Imm32 imm); + void ma_addu(Register rd, Register rs); + void ma_addu(Register rd, Imm32 imm); + void ma_addTestOverflow(Register rd, Register rs, Register rt, Label *overflow); + void ma_addTestOverflow(Register rd, Register rs, Imm32 imm, Label *overflow); + + // subtract + void ma_subu(Register rd, Register rs, Register rt); + void ma_subu(Register rd, Register rs, Imm32 imm); + void ma_subu(Register rd, Imm32 imm); + void ma_subTestOverflow(Register rd, Register rs, Register rt, Label *overflow); + void ma_subTestOverflow(Register rd, Register rs, Imm32 imm, Label *overflow); + + // multiplies. For now, there are only few that we care about. + void ma_mult(Register rs, Imm32 imm); + void ma_mul_branch_overflow(Register rd, Register rs, Register rt, Label *overflow); + void ma_mul_branch_overflow(Register rd, Register rs, Imm32 imm, Label *overflow); + + // divisions + void ma_div_branch_overflow(Register rd, Register rs, Register rt, Label *overflow); + void ma_div_branch_overflow(Register rd, Register rs, Imm32 imm, Label *overflow); + + // fast mod, uses scratch registers, and thus needs to be in the assembler + // implicitly assumes that we can overwrite dest at the beginning of the sequence + void ma_mod_mask(Register src, Register dest, Register hold, int32_t shift, + Label *negZero = nullptr); + + // memory + // shortcut for when we know we're transferring 32 bits of data + void ma_lw(Register data, Address address); + + void ma_sw(Register data, Address address); + void ma_sw(Imm32 imm, Address address); + + void ma_pop(Register r); + void ma_push(Register r); + + // branches when done from within mips-specific code + void ma_b(Register lhs, Register rhs, Label *l, Condition c, JumpKind jumpKind = LongJump); + void ma_b(Register lhs, Imm32 imm, Label *l, Condition c, JumpKind jumpKind = LongJump); + void ma_b(Register lhs, Address addr, Label *l, Condition c, JumpKind jumpKind = LongJump); + void ma_b(Address addr, Imm32 imm, Label *l, Condition c, JumpKind jumpKind = LongJump); + void ma_b(Label *l, JumpKind jumpKind = LongJump); + void ma_bal(Label *l, JumpKind jumpKind = LongJump); + + // fp instructions + void ma_lis(FloatRegister dest, float value); + void ma_lid(FloatRegister dest, double value); + void ma_liNegZero(FloatRegister dest); + + void ma_mv(FloatRegister src, ValueOperand dest); + void ma_mv(ValueOperand src, FloatRegister dest); + + void ma_ls(FloatRegister fd, Address address); + void ma_ld(FloatRegister fd, Address address); + void ma_sd(FloatRegister fd, Address address); + void ma_sd(FloatRegister fd, BaseIndex address); + void ma_ss(FloatRegister fd, Address address); + void ma_ss(FloatRegister fd, BaseIndex address); + + void ma_pop(FloatRegister fs); + void ma_push(FloatRegister fs); + + //FP branches + void ma_bc1s(FloatRegister lhs, FloatRegister rhs, Label *label, DoubleCondition c, + JumpKind jumpKind = LongJump, FPConditionBit fcc = FCC0); + void ma_bc1d(FloatRegister lhs, FloatRegister rhs, Label *label, DoubleCondition c, + JumpKind jumpKind = LongJump, FPConditionBit fcc = FCC0); + + + // These fuctions abstract the access to high part of the double precision + // float register. It is intended to work on both 32 bit and 64 bit + // floating point coprocessor. + // :TODO: (Bug 985881) Modify this for N32 ABI to use mthc1 and mfhc1 + void moveToDoubleHi(Register src, FloatRegister dest) { + as_mtc1(src, getOddPair(dest)); + } + void moveFromDoubleHi(FloatRegister src, Register dest) { + as_mfc1(dest, getOddPair(src)); + } + + void moveToDoubleLo(Register src, FloatRegister dest) { + as_mtc1(src, dest); + } + void moveFromDoubleLo(FloatRegister src, Register dest) { + as_mfc1(dest, src); + } + + void moveToFloat32(Register src, FloatRegister dest) { + as_mtc1(src, dest); + } + void moveFromFloat32(FloatRegister src, Register dest) { + as_mfc1(dest, src); + } + + protected: + void branchWithCode(InstImm code, Label *label, JumpKind jumpKind); + Condition ma_cmp(Register rd, Register lhs, Register rhs, Condition c); + + void compareFloatingPoint(FloatFormat fmt, FloatRegister lhs, FloatRegister rhs, + DoubleCondition c, FloatTestKind *testKind, + FPConditionBit fcc = FCC0); + + public: + // calls an Ion function, assumes that the stack is untouched (8 byte alinged) + void ma_callIon(const Register reg); + // callso an Ion function, assuming that sp has already been decremented + void ma_callIonNoPush(const Register reg); + // calls an ion function, assuming that the stack is currently not 8 byte aligned + void ma_callIonHalfPush(const Register reg); + + void ma_call(ImmPtr dest); + + void ma_jump(ImmPtr dest); + + void ma_cmp_set(Register dst, Register lhs, Register rhs, Condition c); + void ma_cmp_set(Register dst, Register lhs, Imm32 imm, Condition c); + void ma_cmp_set(Register rd, Register rs, Address addr, Condition c); + void ma_cmp_set(Register dst, Address lhs, Register imm, Condition c); + void ma_cmp_set_double(Register dst, FloatRegister lhs, FloatRegister rhs, DoubleCondition c); + void ma_cmp_set_float32(Register dst, FloatRegister lhs, FloatRegister rhs, DoubleCondition c); +}; + +class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS +{ + // Number of bytes the stack is adjusted inside a call to C. Calls to C may + // not be nested. + bool inCall_; + uint32_t args_; + // The actual number of arguments that were passed, used to assert that + // the initial number of arguments declared was correct. + uint32_t passedArgs_; + + uint32_t usedArgSlots_; + MoveOp::Type firstArgType; + + bool dynamicAlignment_; + + bool enoughMemory_; + // Compute space needed for the function call and set the properties of the + // callee. It returns the space which has to be allocated for calling the + // function. + // + // arg Number of arguments of the function. + void setupABICall(uint32_t arg); + + protected: + MoveResolver moveResolver_; + + // Extra bytes currently pushed onto the frame beyond frameDepth_. This is + // needed to compute offsets to stack slots while temporary space has been + // reserved for unexpected spills or C++ function calls. It is maintained + // by functions which track stack alignment, which for clear distinction + // use StudlyCaps (for example, Push, Pop). + uint32_t framePushed_; + void adjustFrame(int value) { + setFramePushed(framePushed_ + value); + } + public: + MacroAssemblerMIPSCompat() + : inCall_(false), + enoughMemory_(true), + framePushed_(0) + { } + bool oom() const { + return Assembler::oom(); + } + + public: + using MacroAssemblerMIPS::call; + + void j(Label *dest) { + ma_b(dest); + } + + void mov(Register src, Register dest) { + as_or(dest, src, zero); + } + void mov(ImmWord imm, Register dest) { + ma_li(dest, Imm32(imm.value)); + } + void mov(ImmPtr imm, Register dest) { + mov(ImmWord(uintptr_t(imm.value)), dest); + } + void mov(Register src, Address dest) { + MOZ_ASSUME_UNREACHABLE("NYI-IC"); + } + void mov(Address src, Register dest) { + MOZ_ASSUME_UNREACHABLE("NYI-IC"); + } + + void call(const Register reg) { + as_jalr(reg); + as_nop(); + } + + void call(Label *label) { + // for now, assume that it'll be nearby? + ma_bal(label); + } + + void call(ImmWord imm) { + call(ImmPtr((void*)imm.value)); + } + void call(ImmPtr imm) { + BufferOffset bo = m_buffer.nextOffset(); + addPendingJump(bo, imm, Relocation::HARDCODED); + ma_call(imm); + } + void call(AsmJSImmPtr imm) { + movePtr(imm, CallReg); + call(CallReg); + } + void call(JitCode *c) { + BufferOffset bo = m_buffer.nextOffset(); + addPendingJump(bo, ImmPtr(c->raw()), Relocation::JITCODE); + ma_liPatchable(ScratchRegister, Imm32((uint32_t)c->raw())); + ma_callIonHalfPush(ScratchRegister); + } + void branch(JitCode *c) { + BufferOffset bo = m_buffer.nextOffset(); + addPendingJump(bo, ImmPtr(c->raw()), Relocation::JITCODE); + ma_liPatchable(ScratchRegister, Imm32((uint32_t)c->raw())); + as_jr(ScratchRegister); + as_nop(); + } + void branch(const Register reg) { + as_jr(reg); + as_nop(); + } + void nop() { + as_nop(); + } + void ret() { + ma_pop(ra); + as_jr(ra); + as_nop(); + } + void retn(Imm32 n) { + // pc <- [sp]; sp += n + ma_lw(ra, Address(StackPointer, 0)); + ma_addu(StackPointer, StackPointer, n); + as_jr(ra); + as_nop(); + } + void push(Imm32 imm) { + ma_li(ScratchRegister, imm); + ma_push(ScratchRegister); + } + void push(ImmWord imm) { + ma_li(ScratchRegister, Imm32(imm.value)); + ma_push(ScratchRegister); + } + void push(ImmGCPtr imm) { + ma_li(ScratchRegister, imm); + ma_push(ScratchRegister); + } + void push(const Address &address) { + ma_lw(ScratchRegister, address); + ma_push(ScratchRegister); + } + void push(const Register ®) { + ma_push(reg); + } + void push(const FloatRegister ®) { + ma_push(reg); + } + void pop(const Register ®) { + ma_pop(reg); + } + void pop(const FloatRegister ®) { + ma_pop(reg); + } + + // Emit a branch that can be toggled to a non-operation. On MIPS we use + // "andi" instruction to toggle the branch. + // See ToggleToJmp(), ToggleToCmp(). + CodeOffsetLabel toggledJump(Label *label); + + // Emit a "jalr" or "nop" instruction. ToggleCall can be used to patch + // this instruction. + CodeOffsetLabel toggledCall(JitCode *target, bool enabled); + + static size_t ToggledCallSize() { + // Four instructions used in: MacroAssemblerMIPSCompat::toggledCall + return 4 * sizeof(uint32_t); + } + + CodeOffsetLabel pushWithPatch(ImmWord imm) { + CodeOffsetLabel label = movWithPatch(imm, ScratchRegister); + ma_push(ScratchRegister); + return label; + } + + CodeOffsetLabel movWithPatch(ImmWord imm, Register dest) { + CodeOffsetLabel label = currentOffset(); + ma_liPatchable(dest, Imm32(imm.value)); + return label; + } + CodeOffsetLabel movWithPatch(ImmPtr imm, Register dest) { + return movWithPatch(ImmWord(uintptr_t(imm.value)), dest); + } + + void jump(Label *label) { + ma_b(label); + } + void jump(Register reg) { + as_jr(reg); + as_nop(); + } + void jump(const Address &address) { + ma_lw(ScratchRegister, address); + as_jr(ScratchRegister); + as_nop(); + } + + void neg32(Register reg) { + ma_negu(reg, reg); + } + void negl(Register reg) { + ma_negu(reg, reg); + } + + // Returns the register containing the type tag. + Register splitTagForTest(const ValueOperand &value) { + return value.typeReg(); + } + + void branchTestGCThing(Condition cond, const Address &address, Label *label); + void branchTestGCThing(Condition cond, const BaseIndex &src, Label *label); + + void branchTestPrimitive(Condition cond, const ValueOperand &value, Label *label); + void branchTestPrimitive(Condition cond, const Register &tag, Label *label); + + void branchTestValue(Condition cond, const ValueOperand &value, const Value &v, Label *label); + void branchTestValue(Condition cond, const Address &valaddr, const ValueOperand &value, + Label *label); + + // unboxing code + void unboxInt32(const ValueOperand &operand, const Register &dest); + void unboxInt32(const Address &src, const Register &dest); + void unboxBoolean(const ValueOperand &operand, const Register &dest); + void unboxBoolean(const Address &src, const Register &dest); + void unboxDouble(const ValueOperand &operand, const FloatRegister &dest); + void unboxDouble(const Address &src, const FloatRegister &dest); + void unboxString(const ValueOperand &operand, const Register &dest); + void unboxString(const Address &src, const Register &dest); + void unboxObject(const ValueOperand &src, const Register &dest); + void unboxValue(const ValueOperand &src, AnyRegister dest); + void unboxPrivate(const ValueOperand &src, Register dest); + + void notBoolean(const ValueOperand &val) { + as_xori(val.payloadReg(), val.payloadReg(), 1); + } + + // boxing code + void boxDouble(const FloatRegister &src, const ValueOperand &dest); + void boxNonDouble(JSValueType type, const Register &src, const ValueOperand &dest); + + // Extended unboxing API. If the payload is already in a register, returns + // that register. Otherwise, provides a move to the given scratch register, + // and returns that. + Register extractObject(const Address &address, Register scratch); + Register extractObject(const ValueOperand &value, Register scratch) { + return value.payloadReg(); + } + Register extractInt32(const ValueOperand &value, Register scratch) { + return value.payloadReg(); + } + Register extractBoolean(const ValueOperand &value, Register scratch) { + return value.payloadReg(); + } + Register extractTag(const Address &address, Register scratch); + Register extractTag(const BaseIndex &address, Register scratch); + Register extractTag(const ValueOperand &value, Register scratch) { + return value.typeReg(); + } + + void boolValueToDouble(const ValueOperand &operand, const FloatRegister &dest); + void int32ValueToDouble(const ValueOperand &operand, const FloatRegister &dest); + void loadInt32OrDouble(const Address &address, const FloatRegister &dest); + void loadInt32OrDouble(Register base, Register index, + const FloatRegister &dest, int32_t shift = defaultShift); + void loadConstantDouble(double dp, const FloatRegister &dest); + + void boolValueToFloat32(const ValueOperand &operand, const FloatRegister &dest); + void int32ValueToFloat32(const ValueOperand &operand, const FloatRegister &dest); + void loadConstantFloat32(float f, const FloatRegister &dest); + + void branchTestInt32(Condition cond, const ValueOperand &value, Label *label); + void branchTestInt32(Condition cond, const Register &tag, Label *label); + void branchTestInt32(Condition cond, const Address &address, Label *label); + void branchTestInt32(Condition cond, const BaseIndex &src, Label *label); + + void branchTestBoolean(Condition cond, const ValueOperand &value, Label *label); + void branchTestBoolean(Condition cond, const Register &tag, Label *label); + void branchTestBoolean(Condition cond, const BaseIndex &src, Label *label); + + void branch32(Condition cond, Register lhs, Register rhs, Label *label) { + ma_b(lhs, rhs, label, cond); + } + void branch32(Condition cond, Register lhs, Imm32 imm, Label *label) { + ma_b(lhs, imm, label, cond); + } + void branch32(Condition cond, const Operand &lhs, Register rhs, Label *label) { + if (lhs.getTag() == Operand::REG) { + ma_b(lhs.toReg(), rhs, label, cond); + } else { + branch32(cond, lhs.toAddress(), rhs, label); + } + } + void branch32(Condition cond, const Operand &lhs, Imm32 rhs, Label *label) { + if (lhs.getTag() == Operand::REG) { + ma_b(lhs.toReg(), rhs, label, cond); + } else { + branch32(cond, lhs.toAddress(), rhs, label); + } + } + void branch32(Condition cond, const Address &lhs, Register rhs, Label *label) { + ma_lw(ScratchRegister, lhs); + ma_b(ScratchRegister, rhs, label, cond); + } + void branch32(Condition cond, const Address &lhs, Imm32 rhs, Label *label) { + ma_lw(SecondScratchReg, lhs); + ma_b(SecondScratchReg, rhs, label, cond); + } + void branchPtr(Condition cond, const Address &lhs, Register rhs, Label *label) { + branch32(cond, lhs, rhs, label); + } + + void branchPrivatePtr(Condition cond, const Address &lhs, ImmPtr ptr, Label *label) { + branchPtr(cond, lhs, ptr, label); + } + + void branchPrivatePtr(Condition cond, const Address &lhs, Register ptr, Label *label) { + branchPtr(cond, lhs, ptr, label); + } + + void branchPrivatePtr(Condition cond, Register lhs, ImmWord ptr, Label *label) { + branchPtr(cond, lhs, ptr, label); + } + + void branchTestDouble(Condition cond, const ValueOperand &value, Label *label); + void branchTestDouble(Condition cond, const Register &tag, Label *label); + void branchTestDouble(Condition cond, const Address &address, Label *label); + void branchTestDouble(Condition cond, const BaseIndex &src, Label *label); + + void branchTestNull(Condition cond, const ValueOperand &value, Label *label); + void branchTestNull(Condition cond, const Register &tag, Label *label); + void branchTestNull(Condition cond, const BaseIndex &src, Label *label); + + void branchTestObject(Condition cond, const ValueOperand &value, Label *label); + void branchTestObject(Condition cond, const Register &tag, Label *label); + void branchTestObject(Condition cond, const BaseIndex &src, Label *label); + + void branchTestString(Condition cond, const ValueOperand &value, Label *label); + void branchTestString(Condition cond, const Register &tag, Label *label); + void branchTestString(Condition cond, const BaseIndex &src, Label *label); + + void branchTestUndefined(Condition cond, const ValueOperand &value, Label *label); + void branchTestUndefined(Condition cond, const Register &tag, Label *label); + void branchTestUndefined(Condition cond, const BaseIndex &src, Label *label); + void branchTestUndefined(Condition cond, const Address &address, Label *label); + + void branchTestNumber(Condition cond, const ValueOperand &value, Label *label); + void branchTestNumber(Condition cond, const Register &tag, Label *label); + + void branchTestMagic(Condition cond, const ValueOperand &value, Label *label); + void branchTestMagic(Condition cond, const Register &tag, Label *label); + void branchTestMagic(Condition cond, const Address &address, Label *label); + void branchTestMagic(Condition cond, const BaseIndex &src, Label *label); + + void branchTestMagicValue(Condition cond, const ValueOperand &val, JSWhyMagic why, + Label *label) { + MOZ_ASSERT(cond == Equal || cond == NotEqual); + // Test for magic + Label notmagic; + branchTestMagic(cond, val, ¬magic); + // Test magic value + branch32(cond, val.payloadReg(), Imm32(static_cast<int32_t>(why)), label); + bind(¬magic); + } + + void branchTestInt32Truthy(bool b, const ValueOperand &value, Label *label); + + void branchTestStringTruthy(bool b, const ValueOperand &value, Label *label); + + void branchTestDoubleTruthy(bool b, const FloatRegister &value, Label *label); + + void branchTestBooleanTruthy(bool b, const ValueOperand &operand, Label *label); + + void branchTest32(Condition cond, const Register &lhs, const Register &rhs, Label *label) { + MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned); + if (lhs == rhs) { + ma_b(lhs, rhs, label, cond); + } else { + as_and(ScratchRegister, lhs, rhs); + ma_b(ScratchRegister, ScratchRegister, label, cond); + } + } + void branchTest32(Condition cond, const Register &lhs, Imm32 imm, Label *label) { + ma_li(ScratchRegister, imm); + branchTest32(cond, lhs, ScratchRegister, label); + } + void branchTest32(Condition cond, const Address &address, Imm32 imm, Label *label) { + ma_lw(SecondScratchReg, address); + branchTest32(cond, SecondScratchReg, imm, label); + } + void branchTestPtr(Condition cond, const Register &lhs, const Register &rhs, Label *label) { + branchTest32(cond, lhs, rhs, label); + } + void branchTestPtr(Condition cond, const Register &lhs, const Imm32 rhs, Label *label) { + branchTest32(cond, lhs, rhs, label); + } + void branchTestPtr(Condition cond, const Address &lhs, Imm32 imm, Label *label) { + branchTest32(cond, lhs, imm, label); + } + void branchPtr(Condition cond, Register lhs, Register rhs, Label *label) { + ma_b(lhs, rhs, label, cond); + } + void branchPtr(Condition cond, Register lhs, ImmGCPtr ptr, Label *label) { + ma_li(ScratchRegister, ptr); + ma_b(lhs, ScratchRegister, label, cond); + } + void branchPtr(Condition cond, Register lhs, ImmWord imm, Label *label) { + ma_b(lhs, Imm32(imm.value), label, cond); + } + void branchPtr(Condition cond, Register lhs, ImmPtr imm, Label *label) { + branchPtr(cond, lhs, ImmWord(uintptr_t(imm.value)), label); + } + void branchPtr(Condition cond, Register lhs, AsmJSImmPtr imm, Label *label) { + movePtr(imm, ScratchRegister); + branchPtr(cond, lhs, ScratchRegister, label); + } + void branchPtr(Condition cond, Register lhs, Imm32 imm, Label *label) { + ma_b(lhs, imm, label, cond); + } + void decBranchPtr(Condition cond, const Register &lhs, Imm32 imm, Label *label) { + subPtr(imm, lhs); + branch32(cond, lhs, Imm32(0), label); + } + +protected: + uint32_t getType(const Value &val); + void moveData(const Value &val, Register data); +public: + void moveValue(const Value &val, Register type, Register data); + + CodeOffsetJump jumpWithPatch(RepatchLabel *label); + + template <typename T> + CodeOffsetJump branchPtrWithPatch(Condition cond, Register reg, T ptr, RepatchLabel *label) { + movePtr(ptr, ScratchRegister); + Label skipJump; + ma_b(reg, ScratchRegister, &skipJump, InvertCondition(cond), ShortJump); + CodeOffsetJump off = jumpWithPatch(label); + bind(&skipJump); + return off; + } + + template <typename T> + CodeOffsetJump branchPtrWithPatch(Condition cond, Address addr, T ptr, RepatchLabel *label) { + loadPtr(addr, SecondScratchReg); + movePtr(ptr, ScratchRegister); + Label skipJump; + ma_b(SecondScratchReg, ScratchRegister, &skipJump, InvertCondition(cond), ShortJump); + CodeOffsetJump off = jumpWithPatch(label); + bind(&skipJump); + return off; + } + void branchPtr(Condition cond, Address addr, ImmGCPtr ptr, Label *label) { + ma_lw(SecondScratchReg, addr); + ma_li(ScratchRegister, ptr); + ma_b(SecondScratchReg, ScratchRegister, label, cond); + } + void branchPtr(Condition cond, Address addr, ImmWord ptr, Label *label) { + ma_lw(SecondScratchReg, addr); + ma_b(SecondScratchReg, Imm32(ptr.value), label, cond); + } + void branchPtr(Condition cond, Address addr, ImmPtr ptr, Label *label) { + branchPtr(cond, addr, ImmWord(uintptr_t(ptr.value)), label); + } + void branchPtr(Condition cond, const AbsoluteAddress &addr, const Register &ptr, Label *label) { + loadPtr(addr, ScratchRegister); + ma_b(ScratchRegister, ptr, label, cond); + } + void branchPtr(Condition cond, const AsmJSAbsoluteAddress &addr, const Register &ptr, + Label *label) { + loadPtr(addr, ScratchRegister); + ma_b(ScratchRegister, ptr, label, cond); + } + void branch32(Condition cond, const AbsoluteAddress &lhs, Imm32 rhs, Label *label) { + loadPtr(lhs, SecondScratchReg); // ma_b might use scratch + ma_b(SecondScratchReg, rhs, label, cond); + } + void branch32(Condition cond, const AbsoluteAddress &lhs, const Register &rhs, Label *label) { + loadPtr(lhs, ScratchRegister); + ma_b(ScratchRegister, rhs, label, cond); + } + + void loadUnboxedValue(Address address, MIRType type, AnyRegister dest) { + if (dest.isFloat()) + loadInt32OrDouble(address, dest.fpu()); + else + ma_lw(dest.gpr(), address); + } + + void loadUnboxedValue(BaseIndex address, MIRType type, AnyRegister dest) { + if (dest.isFloat()) + loadInt32OrDouble(address.base, address.index, dest.fpu(), address.scale); + else + load32(address, dest.gpr()); + } + + void moveValue(const Value &val, const ValueOperand &dest); + + void moveValue(const ValueOperand &src, const ValueOperand &dest) { + MOZ_ASSERT(src.typeReg() != dest.payloadReg()); + MOZ_ASSERT(src.payloadReg() != dest.typeReg()); + if (src.typeReg() != dest.typeReg()) + ma_move(dest.typeReg(), src.typeReg()); + if (src.payloadReg() != dest.payloadReg()) + ma_move(dest.payloadReg(), src.payloadReg()); + } + + void storeValue(ValueOperand val, Operand dst); + void storeValue(ValueOperand val, const BaseIndex &dest); + void storeValue(JSValueType type, Register reg, BaseIndex dest); + void storeValue(ValueOperand val, const Address &dest); + void storeValue(JSValueType type, Register reg, Address dest); + void storeValue(const Value &val, Address dest); + void storeValue(const Value &val, BaseIndex dest); + + void loadValue(Address src, ValueOperand val); + void loadValue(Operand dest, ValueOperand val) { + loadValue(dest.toAddress(), val); + } + void loadValue(const BaseIndex &addr, ValueOperand val); + void tagValue(JSValueType type, Register payload, ValueOperand dest); + + void pushValue(ValueOperand val); + void popValue(ValueOperand val); + void pushValue(const Value &val) { + jsval_layout jv = JSVAL_TO_IMPL(val); + push(Imm32(jv.s.tag)); + if (val.isMarkable()) + push(ImmGCPtr(reinterpret_cast<gc::Cell *>(val.toGCThing()))); + else + push(Imm32(jv.s.payload.i32)); + } + void pushValue(JSValueType type, Register reg) { + push(ImmTag(JSVAL_TYPE_TO_TAG(type))); + ma_push(reg); + } + void pushValue(const Address &addr); + void Push(const ValueOperand &val) { + pushValue(val); + framePushed_ += sizeof(Value); + } + void Pop(const ValueOperand &val) { + popValue(val); + framePushed_ -= sizeof(Value); + } + void storePayload(const Value &val, Address dest); + void storePayload(Register src, Address dest); + void storePayload(const Value &val, Register base, Register index, int32_t shift = defaultShift); + void storePayload(Register src, Register base, Register index, int32_t shift = defaultShift); + void storeTypeTag(ImmTag tag, Address dest); + void storeTypeTag(ImmTag tag, Register base, Register index, int32_t shift = defaultShift); + + void makeFrameDescriptor(Register frameSizeReg, FrameType type) { + ma_sll(frameSizeReg, frameSizeReg, Imm32(FRAMESIZE_SHIFT)); + ma_or(frameSizeReg, frameSizeReg, Imm32(type)); + } + + void linkExitFrame(); + void linkParallelExitFrame(const Register &pt); + void handleFailureWithHandler(void *handler); + void handleFailureWithHandlerTail(); + + ///////////////////////////////////////////////////////////////// + // Common interface. + ///////////////////////////////////////////////////////////////// + public: + // The following functions are exposed for use in platform-shared code. + void Push(const Register ®) { + ma_push(reg); + adjustFrame(sizeof(intptr_t)); + } + void Push(const Imm32 imm) { + ma_li(ScratchRegister, imm); + ma_push(ScratchRegister); + adjustFrame(sizeof(intptr_t)); + } + void Push(const ImmWord imm) { + ma_li(ScratchRegister, Imm32(imm.value)); + ma_push(ScratchRegister); + adjustFrame(sizeof(intptr_t)); + } + void Push(const ImmPtr imm) { + Push(ImmWord(uintptr_t(imm.value))); + } + void Push(const ImmGCPtr ptr) { + ma_li(ScratchRegister, ptr); + ma_push(ScratchRegister); + adjustFrame(sizeof(intptr_t)); + } + void Push(const FloatRegister &f) { + ma_push(f); + adjustFrame(sizeof(double)); + } + + CodeOffsetLabel PushWithPatch(const ImmWord &word) { + framePushed_ += sizeof(word.value); + return pushWithPatch(word); + } + CodeOffsetLabel PushWithPatch(const ImmPtr &imm) { + return PushWithPatch(ImmWord(uintptr_t(imm.value))); + } + + void Pop(const Register ®) { + ma_pop(reg); + adjustFrame(-sizeof(intptr_t)); + } + void implicitPop(uint32_t args) { + MOZ_ASSERT(args % sizeof(intptr_t) == 0); + adjustFrame(-args); + } + uint32_t framePushed() const { + return framePushed_; + } + void setFramePushed(uint32_t framePushed) { + framePushed_ = framePushed; + } + + // Builds an exit frame on the stack, with a return address to an internal + // non-function. Returns offset to be passed to markSafepointAt(). + bool buildFakeExitFrame(const Register &scratch, uint32_t *offset); + + void callWithExitFrame(JitCode *target); + void callWithExitFrame(JitCode *target, Register dynStack); + + // Makes an Ion call using the only two methods that it is sane for + // indep code to make a call + void callIon(const Register &callee); + + void reserveStack(uint32_t amount); + void freeStack(uint32_t amount); + void freeStack(Register amount); + + void add32(Register src, Register dest); + void add32(Imm32 imm, Register dest); + void add32(Imm32 imm, const Address &dest); + void sub32(Imm32 imm, Register dest); + void sub32(Register src, Register dest); + + void and32(Imm32 imm, Register dest); + void and32(Imm32 imm, const Address &dest); + void or32(Imm32 imm, const Address &dest); + void xor32(Imm32 imm, Register dest); + void xorPtr(Imm32 imm, Register dest); + void xorPtr(Register src, Register dest); + void orPtr(Imm32 imm, Register dest); + void orPtr(Register src, Register dest); + void andPtr(Imm32 imm, Register dest); + void andPtr(Register src, Register dest); + void addPtr(Register src, Register dest); + void subPtr(Register src, Register dest); + void addPtr(const Address &src, Register dest); + void not32(Register reg); + + void move32(const Imm32 &imm, const Register &dest); + void move32(const Register &src, const Register &dest); + + void movePtr(const Register &src, const Register &dest); + void movePtr(const ImmWord &imm, const Register &dest); + void movePtr(const ImmPtr &imm, const Register &dest); + void movePtr(const AsmJSImmPtr &imm, const Register &dest); + void movePtr(const ImmGCPtr &imm, const Register &dest); + + void load8SignExtend(const Address &address, const Register &dest); + void load8SignExtend(const BaseIndex &src, const Register &dest); + + void load8ZeroExtend(const Address &address, const Register &dest); + void load8ZeroExtend(const BaseIndex &src, const Register &dest); + + void load16SignExtend(const Address &address, const Register &dest); + void load16SignExtend(const BaseIndex &src, const Register &dest); + + void load16ZeroExtend(const Address &address, const Register &dest); + void load16ZeroExtend(const BaseIndex &src, const Register &dest); + + void load32(const Address &address, const Register &dest); + void load32(const BaseIndex &address, const Register &dest); + void load32(const AbsoluteAddress &address, const Register &dest); + + void loadPtr(const Address &address, const Register &dest); + void loadPtr(const BaseIndex &src, const Register &dest); + void loadPtr(const AbsoluteAddress &address, const Register &dest); + void loadPtr(const AsmJSAbsoluteAddress &address, const Register &dest); + + void loadPrivate(const Address &address, const Register &dest); + + void loadDouble(const Address &addr, const FloatRegister &dest); + void loadDouble(const BaseIndex &src, const FloatRegister &dest); + + // Load a float value into a register, then expand it to a double. + void loadFloatAsDouble(const Address &addr, const FloatRegister &dest); + void loadFloatAsDouble(const BaseIndex &src, const FloatRegister &dest); + + void loadFloat32(const Address &addr, const FloatRegister &dest); + void loadFloat32(const BaseIndex &src, const FloatRegister &dest); + + void store8(const Register &src, const Address &address); + void store8(const Imm32 &imm, const Address &address); + void store8(const Register &src, const BaseIndex &address); + void store8(const Imm32 &imm, const BaseIndex &address); + + void store16(const Register &src, const Address &address); + void store16(const Imm32 &imm, const Address &address); + void store16(const Register &src, const BaseIndex &address); + void store16(const Imm32 &imm, const BaseIndex &address); + + void store32(const Register &src, const AbsoluteAddress &address); + void store32(const Register &src, const Address &address); + void store32(const Register &src, const BaseIndex &address); + void store32(const Imm32 &src, const Address &address); + void store32(const Imm32 &src, const BaseIndex &address); + + void storePtr(ImmWord imm, const Address &address); + void storePtr(ImmPtr imm, const Address &address); + void storePtr(ImmGCPtr imm, const Address &address); + void storePtr(Register src, const Address &address); + void storePtr(const Register &src, const AbsoluteAddress &dest); + void storeDouble(FloatRegister src, Address addr) { + ma_sd(src, addr); + } + void storeDouble(FloatRegister src, BaseIndex addr) { + MOZ_ASSERT(addr.offset == 0); + ma_sd(src, addr); + } + void moveDouble(FloatRegister src, FloatRegister dest) { + as_movd(dest, src); + } + + void storeFloat32(FloatRegister src, Address addr) { + ma_ss(src, addr); + } + void storeFloat32(FloatRegister src, BaseIndex addr) { + MOZ_ASSERT(addr.offset == 0); + ma_ss(src, addr); + } + + void zeroDouble(FloatRegister reg) { + moveToDoubleLo(zero, reg); + moveToDoubleHi(zero, reg); + } + + void clampIntToUint8(Register reg) { + // look at (reg >> 8) if it is 0, then src shouldn't be clamped + // if it is <0, then we want to clamp to 0, + // otherwise, we wish to clamp to 255 + Label done; + ma_move(ScratchRegister, reg); + as_sra(ScratchRegister, ScratchRegister, 8); + ma_b(ScratchRegister, ScratchRegister, &done, Assembler::Zero, ShortJump); + { + Label negative; + ma_b(ScratchRegister, ScratchRegister, &negative, Assembler::Signed, ShortJump); + { + ma_li(reg, Imm32(255)); + ma_b(&done, ShortJump); + } + bind(&negative); + { + ma_move(reg, zero); + } + } + bind(&done); + } + + void subPtr(Imm32 imm, const Register dest); + void addPtr(Imm32 imm, const Register dest); + void addPtr(Imm32 imm, const Address &dest); + void addPtr(ImmWord imm, const Register dest) { + addPtr(Imm32(imm.value), dest); + } + void addPtr(ImmPtr imm, const Register dest) { + addPtr(ImmWord(uintptr_t(imm.value)), dest); + } + + void breakpoint(); + + void branchDouble(DoubleCondition cond, const FloatRegister &lhs, const FloatRegister &rhs, + Label *label); + + void branchFloat(DoubleCondition cond, const FloatRegister &lhs, const FloatRegister &rhs, + Label *label); + + void checkStackAlignment(); + + void alignPointerUp(Register src, Register dest, uint32_t alignment); + + void rshiftPtr(Imm32 imm, Register dest) { + ma_srl(dest, dest, imm); + } + void lshiftPtr(Imm32 imm, Register dest) { + ma_sll(dest, dest, imm); + } + + // If source is a double, load it into dest. If source is int32, + // convert it to double. Else, branch to failure. + void ensureDouble(const ValueOperand &source, FloatRegister dest, Label *failure); + + // Setup a call to C/C++ code, given the number of general arguments it + // takes. Note that this only supports cdecl. + // + // In order for alignment to work correctly, the MacroAssembler must have a + // consistent view of the stack displacement. It is okay to call "push" + // manually, however, if the stack alignment were to change, the macro + // assembler should be notified before starting a call. + void setupAlignedABICall(uint32_t args); + + // Sets up an ABI call for when the alignment is not known. This may need a + // scratch register. + void setupUnalignedABICall(uint32_t args, const Register &scratch); + + // Arguments must be assigned in a left-to-right order. This process may + // temporarily use more stack, in which case sp-relative addresses will be + // automatically adjusted. It is extremely important that sp-relative + // addresses are computed *after* setupABICall(). Furthermore, no + // operations should be emitted while setting arguments. + void passABIArg(const MoveOperand &from, MoveOp::Type type); + void passABIArg(const Register ®); + void passABIArg(const FloatRegister ®, MoveOp::Type type); + void passABIArg(const ValueOperand ®s); + + protected: + bool buildOOLFakeExitFrame(void *fakeReturnAddr); + + private: + void callWithABIPre(uint32_t *stackAdjust); + void callWithABIPost(uint32_t stackAdjust, MoveOp::Type result); + + public: + // Emits a call to a C/C++ function, resolving all argument moves. + void callWithABI(void *fun, MoveOp::Type result = MoveOp::GENERAL); + void callWithABI(AsmJSImmPtr imm, MoveOp::Type result = MoveOp::GENERAL); + void callWithABI(const Address &fun, MoveOp::Type result = MoveOp::GENERAL); + + CodeOffsetLabel labelForPatch() { + return CodeOffsetLabel(nextOffset().getOffset()); + } + + void memIntToValue(Address Source, Address Dest) { + MOZ_ASSUME_UNREACHABLE("NYI"); + } + + void lea(Operand addr, Register dest) { + MOZ_ASSUME_UNREACHABLE("NYI"); + } + + void abiret() { + MOZ_ASSUME_UNREACHABLE("NYI"); + } + + void ma_storeImm(Imm32 imm, const Address &addr) { + ma_sw(imm, addr); + } + + BufferOffset ma_BoundsCheck(Register bounded) { + BufferOffset bo = m_buffer.nextOffset(); + ma_liPatchable(bounded, Imm32(0)); + return bo; + } + + void moveFloat32(FloatRegister src, FloatRegister dest) { + as_movs(dest, src); + } + + void branchPtrInNurseryRange(Register ptr, Register temp, Label *label); +}; + +typedef MacroAssemblerMIPSCompat MacroAssemblerSpecific; + +} // namespace jit +} // namespace js + +#endif /* jit_mips_MacroAssembler_mips_h */
diff --git a/src/third_party/mozjs/js/src/jit/mips/MoveEmitter-mips.cpp b/src/third_party/mozjs/js/src/jit/mips/MoveEmitter-mips.cpp new file mode 100644 index 0000000..12864b0 --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/MoveEmitter-mips.cpp
@@ -0,0 +1,330 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/mips/MoveEmitter-mips.h" + +using namespace js; +using namespace js::jit; + +MoveEmitterMIPS::MoveEmitterMIPS(MacroAssemblerMIPSCompat &masm) + : inCycle_(false), + masm(masm), + pushedAtCycle_(-1), + pushedAtSpill_(-1), + spilledReg_(InvalidReg), + spilledFloatReg_(InvalidFloatReg) +{ + pushedAtStart_ = masm.framePushed(); +} + +void +MoveEmitterMIPS::emit(const MoveResolver &moves) +{ + if (moves.hasCycles()) { + // Reserve stack for cycle resolution + masm.reserveStack(sizeof(double)); + pushedAtCycle_ = masm.framePushed(); + } + + for (size_t i = 0; i < moves.numMoves(); i++) + emit(moves.getMove(i)); +} + +MoveEmitterMIPS::~MoveEmitterMIPS() +{ + assertDone(); +} + +Address +MoveEmitterMIPS::cycleSlot() const +{ + int offset = masm.framePushed() - pushedAtCycle_; + MOZ_ASSERT(Imm16::isInSignedRange(offset)); + return Address(StackPointer, offset); +} + +int32_t +MoveEmitterMIPS::getAdjustedOffset(const MoveOperand &operand) +{ + MOZ_ASSERT(operand.isMemoryOrEffectiveAddress()); + if (operand.base() != StackPointer) + return operand.disp(); + + // Adjust offset if stack pointer has been moved. + return operand.disp() + masm.framePushed() - pushedAtStart_; +} + +Address +MoveEmitterMIPS::getAdjustedAddress(const MoveOperand &operand) +{ + return Address(operand.base(), getAdjustedOffset(operand)); +} + + +Register +MoveEmitterMIPS::tempReg() +{ + spilledReg_ = SecondScratchReg; + return SecondScratchReg; +} + +void +MoveEmitterMIPS::breakCycle(const MoveOperand &from, const MoveOperand &to, MoveOp::Type type) +{ + // There is some pattern: + // (A -> B) + // (B -> A) + // + // This case handles (A -> B), which we reach first. We save B, then allow + // the original move to continue. + switch (type) { + case MoveOp::FLOAT32: + if (to.isMemory()) { + FloatRegister temp = ScratchFloatReg; + masm.loadFloat32(getAdjustedAddress(to), temp); + masm.storeFloat32(temp, cycleSlot()); + } else { + masm.storeFloat32(to.floatReg(), cycleSlot()); + } + break; + case MoveOp::DOUBLE: + if (to.isMemory()) { + FloatRegister temp = ScratchFloatReg; + masm.loadDouble(getAdjustedAddress(to), temp); + masm.storeDouble(temp, cycleSlot()); + } else { + masm.storeDouble(to.floatReg(), cycleSlot()); + } + break; + case MoveOp::INT32: + MOZ_ASSERT(sizeof(uintptr_t) == sizeof(int32_t)); + case MoveOp::GENERAL: + if (to.isMemory()) { + Register temp = tempReg(); + masm.loadPtr(getAdjustedAddress(to), temp); + masm.storePtr(temp, cycleSlot()); + } else { + // Second scratch register should not be moved by MoveEmitter. + MOZ_ASSERT(to.reg() != spilledReg_); + masm.storePtr(to.reg(), cycleSlot()); + } + break; + default: + MOZ_ASSUME_UNREACHABLE("Unexpected move type"); + } +} + +void +MoveEmitterMIPS::completeCycle(const MoveOperand &from, const MoveOperand &to, MoveOp::Type type) +{ + // There is some pattern: + // (A -> B) + // (B -> A) + // + // This case handles (B -> A), which we reach last. We emit a move from the + // saved value of B, to A. + switch (type) { + case MoveOp::FLOAT32: + if (to.isMemory()) { + FloatRegister temp = ScratchFloatReg; + masm.loadFloat32(cycleSlot(), temp); + masm.storeFloat32(temp, getAdjustedAddress(to)); + } else { + masm.loadFloat32(cycleSlot(), to.floatReg()); + } + break; + case MoveOp::DOUBLE: + if (to.isMemory()) { + FloatRegister temp = ScratchFloatReg; + masm.loadDouble(cycleSlot(), temp); + masm.storeDouble(temp, getAdjustedAddress(to)); + } else { + masm.loadDouble(cycleSlot(), to.floatReg()); + } + break; + case MoveOp::INT32: + MOZ_ASSERT(sizeof(uintptr_t) == sizeof(int32_t)); + case MoveOp::GENERAL: + if (to.isMemory()) { + Register temp = tempReg(); + masm.loadPtr(cycleSlot(), temp); + masm.storePtr(temp, getAdjustedAddress(to)); + } else { + // Second scratch register should not be moved by MoveEmitter. + MOZ_ASSERT(to.reg() != spilledReg_); + masm.loadPtr(cycleSlot(), to.reg()); + } + break; + default: + MOZ_ASSUME_UNREACHABLE("Unexpected move type"); + } +} + +void +MoveEmitterMIPS::emitMove(const MoveOperand &from, const MoveOperand &to) +{ + if (from.isGeneralReg()) { + // Second scratch register should not be moved by MoveEmitter. + MOZ_ASSERT(from.reg() != spilledReg_); + + if (to.isGeneralReg()) + masm.movePtr(from.reg(), to.reg()); + else if (to.isMemory()) + masm.storePtr(from.reg(), getAdjustedAddress(to)); + else + MOZ_ASSUME_UNREACHABLE("Invalid emitMove arguments."); + } else if (from.isMemory()) { + if (to.isGeneralReg()) { + masm.loadPtr(getAdjustedAddress(from), to.reg()); + } else if (to.isMemory()) { + masm.loadPtr(getAdjustedAddress(from), tempReg()); + masm.storePtr(tempReg(), getAdjustedAddress(to)); + } else { + MOZ_ASSUME_UNREACHABLE("Invalid emitMove arguments."); + } + } else if (from.isEffectiveAddress()) { + if (to.isGeneralReg()) { + masm.computeEffectiveAddress(getAdjustedAddress(from), to.reg()); + } else if (to.isMemory()) { + masm.computeEffectiveAddress(getAdjustedAddress(from), tempReg()); + masm.storePtr(tempReg(), getAdjustedAddress(to)); + } else { + MOZ_ASSUME_UNREACHABLE("Invalid emitMove arguments."); + } + } else { + MOZ_ASSUME_UNREACHABLE("Invalid emitMove arguments."); + } +} + +void +MoveEmitterMIPS::emitFloat32Move(const MoveOperand &from, const MoveOperand &to) +{ + // Ensure that we can use ScratchFloatReg in memory move. + MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchFloatReg); + MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchFloatReg); + + if (from.isFloatReg()) { + if (to.isFloatReg()) { + masm.moveFloat32(from.floatReg(), to.floatReg()); + } else if (to.isGeneralReg()) { + // This should only be used when passing float parameter in a1,a2,a3 + MOZ_ASSERT(to.reg() == a1 || to.reg() == a2 || to.reg() == a3); + masm.moveFromFloat32(from.floatReg(), to.reg()); + } else { + MOZ_ASSERT(to.isMemory()); + masm.storeFloat32(from.floatReg(), getAdjustedAddress(to)); + } + } else if (to.isFloatReg()) { + MOZ_ASSERT(from.isMemory()); + masm.loadFloat32(getAdjustedAddress(from), to.floatReg()); + } else if (to.isGeneralReg()) { + MOZ_ASSERT(from.isMemory()); + // This should only be used when passing float parameter in a1,a2,a3 + MOZ_ASSERT(to.reg() == a1 || to.reg() == a2 || to.reg() == a3); + masm.loadPtr(getAdjustedAddress(from), to.reg()); + } else { + MOZ_ASSERT(from.isMemory()); + MOZ_ASSERT(to.isMemory()); + masm.loadFloat32(getAdjustedAddress(from), ScratchFloatReg); + masm.storeFloat32(ScratchFloatReg, getAdjustedAddress(to)); + } +} + +void +MoveEmitterMIPS::emitDoubleMove(const MoveOperand &from, const MoveOperand &to) +{ + // Ensure that we can use ScratchFloatReg in memory move. + MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchFloatReg); + MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchFloatReg); + + if (from.isFloatReg()) { + if (to.isFloatReg()) { + masm.moveDouble(from.floatReg(), to.floatReg()); + } else if (to.isGeneralReg()) { + // Used for passing double parameter in a2,a3 register pair. + // Two moves are added for one double parameter by + // MacroAssemblerMIPSCompat::passABIArg + if(to.reg() == a2) + masm.moveFromDoubleLo(from.floatReg(), a2); + else if(to.reg() == a3) + masm.moveFromDoubleHi(from.floatReg(), a3); + else + MOZ_ASSUME_UNREACHABLE("Invalid emitDoubleMove arguments."); + } else { + MOZ_ASSERT(to.isMemory()); + masm.storeDouble(from.floatReg(), getAdjustedAddress(to)); + } + } else if (to.isFloatReg()) { + MOZ_ASSERT(from.isMemory()); + masm.loadDouble(getAdjustedAddress(from), to.floatReg()); + } else if (to.isGeneralReg()) { + MOZ_ASSERT(from.isMemory()); + // Used for passing double parameter in a2,a3 register pair. + // Two moves are added for one double parameter by + // MacroAssemblerMIPSCompat::passABIArg + if(to.reg() == a2) + masm.loadPtr(getAdjustedAddress(from), a2); + else if(to.reg() == a3) + masm.loadPtr(Address(from.base(), getAdjustedOffset(from) + sizeof(uint32_t)), a3); + else + MOZ_ASSUME_UNREACHABLE("Invalid emitDoubleMove arguments."); + } else { + MOZ_ASSERT(from.isMemory()); + MOZ_ASSERT(to.isMemory()); + masm.loadDouble(getAdjustedAddress(from), ScratchFloatReg); + masm.storeDouble(ScratchFloatReg, getAdjustedAddress(to)); + } +} + +void +MoveEmitterMIPS::emit(const MoveOp &move) +{ + const MoveOperand &from = move.from(); + const MoveOperand &to = move.to(); + + if (move.isCycleEnd()) { + MOZ_ASSERT(inCycle_); + completeCycle(from, to, move.type()); + inCycle_ = false; + return; + } + + if (move.isCycleBegin()) { + MOZ_ASSERT(!inCycle_); + breakCycle(from, to, move.endCycleType()); + inCycle_ = true; + } + + switch (move.type()) { + case MoveOp::FLOAT32: + emitFloat32Move(from, to); + break; + case MoveOp::DOUBLE: + emitDoubleMove(from, to); + break; + case MoveOp::INT32: + MOZ_ASSERT(sizeof(uintptr_t) == sizeof(int32_t)); + case MoveOp::GENERAL: + emitMove(from, to); + break; + default: + MOZ_ASSUME_UNREACHABLE("Unexpected move type"); + } +} + +void +MoveEmitterMIPS::assertDone() +{ + MOZ_ASSERT(!inCycle_); +} + +void +MoveEmitterMIPS::finish() +{ + assertDone(); + + masm.freeStack(masm.framePushed() - pushedAtStart_); +}
diff --git a/src/third_party/mozjs/js/src/jit/mips/MoveEmitter-mips.h b/src/third_party/mozjs/js/src/jit/mips/MoveEmitter-mips.h new file mode 100644 index 0000000..d17820a --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/MoveEmitter-mips.h
@@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_mips_MoveEmitter_mips_h +#define jit_mips_MoveEmitter_mips_h + +#include "jit/IonMacroAssembler.h" +#include "jit/MoveResolver.h" + +namespace js { +namespace jit { + +class CodeGenerator; + +class MoveEmitterMIPS +{ + bool inCycle_; + MacroAssemblerMIPSCompat &masm; + + // Original stack push value. + uint32_t pushedAtStart_; + + // These store stack offsets to spill locations, snapshotting + // codegen->framePushed_ at the time they were allocated. They are -1 if no + // stack space has been allocated for that particular spill. + int32_t pushedAtCycle_; + int32_t pushedAtSpill_; + + // These are registers that are available for temporary use. They may be + // assigned InvalidReg. If no corresponding spill space has been assigned, + // then these registers do not need to be spilled. + Register spilledReg_; + FloatRegister spilledFloatReg_; + + void assertDone(); + Register tempReg(); + FloatRegister tempFloatReg(); + Address cycleSlot() const; + int32_t getAdjustedOffset(const MoveOperand &operand); + Address getAdjustedAddress(const MoveOperand &operand); + + void emitMove(const MoveOperand &from, const MoveOperand &to); + void emitFloat32Move(const MoveOperand &from, const MoveOperand &to); + void emitDoubleMove(const MoveOperand &from, const MoveOperand &to); + void breakCycle(const MoveOperand &from, const MoveOperand &to, MoveOp::Type type); + void completeCycle(const MoveOperand &from, const MoveOperand &to, MoveOp::Type type); + void emit(const MoveOp &move); + + public: + MoveEmitterMIPS(MacroAssemblerMIPSCompat &masm); + ~MoveEmitterMIPS(); + void emit(const MoveResolver &moves); + void finish(); +}; + +typedef MoveEmitterMIPS MoveEmitter; + +} // namespace jit +} // namespace js + +#endif /* jit_mips_MoveEmitter_mips_h */
diff --git a/src/third_party/mozjs/js/src/jit/mips/Trampoline-mips.cpp b/src/third_party/mozjs/js/src/jit/mips/Trampoline-mips.cpp new file mode 100644 index 0000000..b456845 --- /dev/null +++ b/src/third_party/mozjs/js/src/jit/mips/Trampoline-mips.cpp
@@ -0,0 +1,997 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jscompartment.h" + +#include "jit/Bailouts.h" +#include "jit/IonFrames.h" +#include "jit/IonLinker.h" +#include "jit/IonSpewer.h" +#include "jit/JitCompartment.h" +#include "jit/mips/Bailouts-mips.h" +#include "jit/mips/BaselineHelpers-mips.h" +#ifdef JS_ION_PERF +# include "jit/PerfSpewer.h" +#endif +#include "jit/VMFunctions.h" + +#include "jit/ExecutionMode-inl.h" + +using namespace js; +using namespace js::jit; + +static_assert(sizeof(uintptr_t) == sizeof(uint32_t), "Not 64-bit clean."); + +struct EnterJITRegs +{ + double f30; + double f28; + double f26; + double f24; + double f22; + double f20; + + // empty slot for alignment + uintptr_t align; + + // non-volatile registers. + uintptr_t ra; + uintptr_t s7; + uintptr_t s6; + uintptr_t s5; + uintptr_t s4; + uintptr_t s3; + uintptr_t s2; + uintptr_t s1; + uintptr_t s0; +}; + +struct EnterJITArgs +{ + // First 4 argumet placeholders + void *jitcode; // <- sp points here when function is entered. + int maxArgc; + Value *maxArgv; + InterpreterFrame *fp; + + // Arguments on stack + CalleeToken calleeToken; + JSObject *scopeChain; + size_t numStackValues; + Value *vp; +}; + +static void +GenerateReturn(MacroAssembler &masm, int returnCode) +{ + MOZ_ASSERT(masm.framePushed() == sizeof(EnterJITRegs)); + + // Restore non-volatile registers + masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, s0)), s0); + masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, s1)), s1); + masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, s2)), s2); + masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, s3)), s3); + masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, s4)), s4); + masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, s5)), s5); + masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, s6)), s6); + masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, s7)), s7); + masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, ra)), ra); + + // Restore non-volatile floating point registers + masm.loadDouble(Address(StackPointer, offsetof(EnterJITRegs, f20)), f20); + masm.loadDouble(Address(StackPointer, offsetof(EnterJITRegs, f22)), f22); + masm.loadDouble(Address(StackPointer, offsetof(EnterJITRegs, f24)), f24); + masm.loadDouble(Address(StackPointer, offsetof(EnterJITRegs, f26)), f26); + masm.loadDouble(Address(StackPointer, offsetof(EnterJITRegs, f28)), f28); + masm.loadDouble(Address(StackPointer, offsetof(EnterJITRegs, f30)), f30); + + masm.freeStack(sizeof(EnterJITRegs)); + + masm.branch(ra); +} + +static void +GeneratePrologue(MacroAssembler &masm) +{ + // Save non-volatile registers. These must be saved by the trampoline, + // rather than the JIT'd code, because they are scanned by the conservative + // scanner. + masm.reserveStack(sizeof(EnterJITRegs)); + masm.storePtr(s0, Address(StackPointer, offsetof(EnterJITRegs, s0))); + masm.storePtr(s1, Address(StackPointer, offsetof(EnterJITRegs, s1))); + masm.storePtr(s2, Address(StackPointer, offsetof(EnterJITRegs, s2))); + masm.storePtr(s3, Address(StackPointer, offsetof(EnterJITRegs, s3))); + masm.storePtr(s4, Address(StackPointer, offsetof(EnterJITRegs, s4))); + masm.storePtr(s5, Address(StackPointer, offsetof(EnterJITRegs, s5))); + masm.storePtr(s6, Address(StackPointer, offsetof(EnterJITRegs, s6))); + masm.storePtr(s7, Address(StackPointer, offsetof(EnterJITRegs, s7))); + masm.storePtr(ra, Address(StackPointer, offsetof(EnterJITRegs, ra))); + + masm.as_sd(f20, StackPointer, offsetof(EnterJITRegs, f20)); + masm.as_sd(f22, StackPointer, offsetof(EnterJITRegs, f22)); + masm.as_sd(f24, StackPointer, offsetof(EnterJITRegs, f24)); + masm.as_sd(f26, StackPointer, offsetof(EnterJITRegs, f26)); + masm.as_sd(f28, StackPointer, offsetof(EnterJITRegs, f28)); + masm.as_sd(f30, StackPointer, offsetof(EnterJITRegs, f30)); +} + + +/* + * This method generates a trampoline for a c++ function with the following + * signature: + * void enter(void *code, int argc, Value *argv, InterpreterFrame *fp, + * CalleeToken calleeToken, JSObject *scopeChain, Value *vp) + * ...using standard EABI calling convention + */ +JitCode * +JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type) +{ + const Register reg_code = a0; + const Register reg_argc = a1; + const Register reg_argv = a2; + const Register reg_frame = a3; + + MOZ_ASSERT(OsrFrameReg == reg_frame); + + MacroAssembler masm(cx); + GeneratePrologue(masm); + + const Address slotToken(sp, sizeof(EnterJITRegs) + offsetof(EnterJITArgs, calleeToken)); + const Address slotVp(sp, sizeof(EnterJITRegs) + offsetof(EnterJITArgs, vp)); + + // Save stack pointer into s4 + masm.movePtr(StackPointer, s4); + + // Load calleeToken into s2. + masm.loadPtr(slotToken, s2); + + // Save stack pointer as baseline frame. + if (type == EnterJitBaseline) + masm.movePtr(StackPointer, BaselineFrameReg); + + // Load the number of actual arguments into s3. + masm.loadPtr(slotVp, s3); + masm.unboxInt32(Address(s3, 0), s3); + + /*************************************************************** + Loop over argv vector, push arguments onto stack in reverse order + ***************************************************************/ + + masm.as_sll(s0, reg_argc, 3); // s0 = argc * 8 + masm.addPtr(reg_argv, s0); // s0 = argv + argc * 8 + + // Loop over arguments, copying them from an unknown buffer onto the Ion + // stack so they can be accessed from JIT'ed code. + Label header, footer; + // If there aren't any arguments, don't do anything + masm.ma_b(s0, reg_argv, &footer, Assembler::BelowOrEqual, ShortJump); + { + masm.bind(&header); + + masm.subPtr(Imm32(2 * sizeof(uintptr_t)), s0); + masm.subPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer); + + ValueOperand value = ValueOperand(s6, s7); + masm.loadValue(Address(s0, 0), value); + masm.storeValue(value, Address(StackPointer, 0)); + + masm.ma_b(s0, reg_argv, &header, Assembler::Above, ShortJump); + } + masm.bind(&footer); + + masm.subPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer); + masm.storePtr(s3, Address(StackPointer, sizeof(uintptr_t))); // actual arguments + masm.storePtr(s2, Address(StackPointer, 0)); // callee token + + masm.subPtr(StackPointer, s4); + masm.makeFrameDescriptor(s4, JitFrame_Entry); + masm.push(s4); // descriptor + + CodeLabel returnLabel; + if (type == EnterJitBaseline) { + // Handle OSR. + GeneralRegisterSet regs(GeneralRegisterSet::All()); + regs.take(JSReturnOperand); + regs.take(OsrFrameReg); + regs.take(BaselineFrameReg); + regs.take(reg_code); + + const Address slotNumStackValues(BaselineFrameReg, sizeof(EnterJITRegs) + + offsetof(EnterJITArgs, numStackValues)); + const Address slotScopeChain(BaselineFrameReg, sizeof(EnterJITRegs) + + offsetof(EnterJITArgs, scopeChain)); + + Label notOsr; + masm.ma_b(OsrFrameReg, OsrFrameReg, ¬Osr, Assembler::Zero, ShortJump); + + Register scratch = regs.takeAny(); + + Register numStackValues = regs.takeAny(); + masm.load32(slotNumStackValues, numStackValues); + + // Push return address, previous frame pointer. + masm.subPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer); + masm.ma_li(scratch, returnLabel.dest()); + masm.storePtr(scratch, Address(StackPointer, sizeof(uintptr_t))); + masm.storePtr(BaselineFrameReg, Address(StackPointer, 0)); + + // Reserve frame. + Register framePtr = BaselineFrameReg; + masm.subPtr(Imm32(BaselineFrame::Size()), StackPointer); + masm.movePtr(StackPointer, framePtr); + + // Reserve space for locals and stack values. + masm.ma_sll(scratch, numStackValues, Imm32(3)); + masm.subPtr(scratch, StackPointer); + + // Enter exit frame. + masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), scratch); + masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS); + + // Push frame descriptor and fake return address. + masm.reserveStack(2 * sizeof(uintptr_t)); + masm.storePtr(scratch, Address(StackPointer, sizeof(uintptr_t))); // Frame descriptor + masm.storePtr(zero, Address(StackPointer, 0)); // fake return address + + masm.enterFakeExitFrame(); + + masm.reserveStack(2 * sizeof(uintptr_t)); + masm.storePtr(framePtr, Address(StackPointer, sizeof(uintptr_t))); // BaselineFrame + masm.storePtr(reg_code, Address(StackPointer, 0)); // jitcode + + masm.setupUnalignedABICall(3, scratch); + masm.passABIArg(BaselineFrameReg); // BaselineFrame + masm.passABIArg(OsrFrameReg); // InterpreterFrame + masm.passABIArg(numStackValues); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, jit::InitBaselineFrameForOsr)); + + Register jitcode = regs.takeAny(); + masm.loadPtr(Address(StackPointer, 0), jitcode); + masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), framePtr); + masm.freeStack(2 * sizeof(uintptr_t)); + + MOZ_ASSERT(jitcode != ReturnReg); + + Label error; + masm.freeStack(IonExitFrameLayout::SizeWithFooter()); + masm.addPtr(Imm32(BaselineFrame::Size()), framePtr); + masm.branchIfFalseBool(ReturnReg, &error); + + masm.jump(jitcode); + + // OOM: load error value, discard return address and previous frame + // pointer and return. + masm.bind(&error); + masm.movePtr(framePtr, StackPointer); + masm.addPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer); + masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); + masm.ma_li(scratch, returnLabel.dest()); + masm.jump(scratch); + + masm.bind(¬Osr); + // Load the scope chain in R1. + MOZ_ASSERT(R1.scratchReg() != reg_code); + masm.loadPtr(slotScopeChain, R1.scratchReg()); + } + + // Call the function with pushing return address to stack. + masm.ma_callIonHalfPush(reg_code); + + if (type == EnterJitBaseline) { + // Baseline OSR will return here. + masm.bind(returnLabel.src()); + if (!masm.addCodeLabel(returnLabel)) + return nullptr; + } + + // Pop arguments off the stack. + // s0 <- 8*argc (size of all arguments we pushed on the stack) + masm.pop(s0); + masm.rshiftPtr(Imm32(4), s0); + masm.addPtr(s0, StackPointer); + + // Store the returned value into the slotVp + masm.loadPtr(slotVp, s1); + masm.storeValue(JSReturnOperand, Address(s1, 0)); + + // Restore non-volatile registers and return. + GenerateReturn(masm, ShortJump); + + Linker linker(masm); + AutoFlushICache afc("GenerateEnterJIT"); + JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerJitCodeProfile(code, "EnterJIT"); +#endif + + return code; +} + +JitCode * +JitRuntime::generateInvalidator(JSContext *cx) +{ + MacroAssembler masm(cx); + + // NOTE: Members ionScript_ and osiPointReturnAddress_ of + // InvalidationBailoutStack are already on the stack. + static const uint32_t STACK_DATA_SIZE = sizeof(InvalidationBailoutStack) - + 2 * sizeof(uintptr_t); + + // Stack has to be alligned here. If not, we will have to fix it. + masm.checkStackAlignment(); + + // Make room for data on stack. + masm.subPtr(Imm32(STACK_DATA_SIZE), StackPointer); + + // Save general purpose registers + for (uint32_t i = 0; i < Registers::Total; i++) { + Address address = Address(StackPointer, InvalidationBailoutStack::offsetOfRegs() + + i * sizeof(uintptr_t)); + masm.storePtr(Register::FromCode(i), address); + } + + // Save floating point registers + // We can use as_sd because stack is alligned. + for (uint32_t i = 0; i < FloatRegisters::Total; i++) + masm.as_sd(FloatRegister::FromCode(i), StackPointer, + InvalidationBailoutStack::offsetOfFpRegs() + i * sizeof(double)); + + // Pass pointer to InvalidationBailoutStack structure. + masm.movePtr(StackPointer, a0); + + // Reserve place for return value and BailoutInfo pointer + masm.subPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer); + // Pass pointer to return value. + masm.ma_addu(a1, StackPointer, Imm32(sizeof(uintptr_t))); + // Pass pointer to BailoutInfo + masm.movePtr(StackPointer, a2); + + masm.setupAlignedABICall(3); + masm.passABIArg(a0); + masm.passABIArg(a1); + masm.passABIArg(a2); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, InvalidationBailout)); + + masm.loadPtr(Address(StackPointer, 0), a2); + masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), a1); + // Remove the return address, the IonScript, the register state + // (InvaliationBailoutStack) and the space that was allocated for the + // return value. + masm.addPtr(Imm32(sizeof(InvalidationBailoutStack) + 2 * sizeof(uintptr_t)), StackPointer); + // remove the space that this frame was using before the bailout + // (computed by InvalidationBailout) + masm.addPtr(a1, StackPointer); + + // Jump to shared bailout tail. The BailoutInfo pointer has to be in r2. + JitCode *bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail(); + masm.branch(bailoutTail); + + Linker linker(masm); + AutoFlushICache afc("Invalidator"); + JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); + IonSpew(IonSpew_Invalidate, " invalidation thunk created at %p", (void *) code->raw()); + +#ifdef JS_ION_PERF + writePerfSpewerJitCodeProfile(code, "Invalidator"); +#endif + + return code; +} + +JitCode * +JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut) +{ + MacroAssembler masm(cx); + + // ArgumentsRectifierReg contains the |nargs| pushed onto the current + // frame. Including |this|, there are (|nargs| + 1) arguments to copy. + MOZ_ASSERT(ArgumentsRectifierReg == s3); + + Register numActArgsReg = t6; + Register calleeTokenReg = t7; + Register numArgsReg = t5; + + // Copy number of actual arguments into numActArgsReg + masm.loadPtr(Address(StackPointer, IonRectifierFrameLayout::offsetOfNumActualArgs()), + numActArgsReg); + + // Load the number of |undefined|s to push into t1. + masm.loadPtr(Address(StackPointer, IonRectifierFrameLayout::offsetOfCalleeToken()), + calleeTokenReg); + masm.load16ZeroExtend(Address(calleeTokenReg, JSFunction::offsetOfNargs()), numArgsReg); + + masm.ma_subu(t1, numArgsReg, s3); + + masm.moveValue(UndefinedValue(), ValueOperand(t3, t4)); + + masm.movePtr(StackPointer, t2); // Save %sp. + + // Push undefined. + { + Label undefLoopTop; + masm.bind(&undefLoopTop); + + masm.subPtr(Imm32(sizeof(Value)), StackPointer); + masm.storeValue(ValueOperand(t3, t4), Address(StackPointer, 0)); + masm.sub32(Imm32(1), t1); + + masm.ma_b(t1, t1, &undefLoopTop, Assembler::NonZero, ShortJump); + } + + // Get the topmost argument. + masm.ma_sll(t0, s3, Imm32(3)); // t0 <- nargs * 8 + masm.addPtr(t0, t2); // t2 <- t2(saved sp) + nargs * 8 + masm.addPtr(Imm32(sizeof(IonRectifierFrameLayout)), t2); + + // Push arguments, |nargs| + 1 times (to include |this|). + { + Label copyLoopTop, initialSkip; + + masm.ma_b(&initialSkip, ShortJump); + + masm.bind(©LoopTop); + masm.subPtr(Imm32(sizeof(Value)), t2); + masm.sub32(Imm32(1), s3); + + masm.bind(&initialSkip); + + MOZ_ASSERT(sizeof(Value) == 2 * sizeof(uint32_t)); + // Read argument and push to stack. + masm.subPtr(Imm32(sizeof(Value)), StackPointer); + masm.load32(Address(t2, NUNBOX32_TYPE_OFFSET), t0); + masm.store32(t0, Address(StackPointer, NUNBOX32_TYPE_OFFSET)); + masm.load32(Address(t2, NUNBOX32_PAYLOAD_OFFSET), t0); + masm.store32(t0, Address(StackPointer, NUNBOX32_PAYLOAD_OFFSET)); + + masm.ma_b(s3, s3, ©LoopTop, Assembler::NonZero, ShortJump); + } + + // translate the framesize from values into bytes + masm.ma_addu(t0, numArgsReg, Imm32(1)); + masm.lshiftPtr(Imm32(3), t0); + + // Construct sizeDescriptor. + masm.makeFrameDescriptor(t0, JitFrame_Rectifier); + + // Construct IonJSFrameLayout. + masm.subPtr(Imm32(3 * sizeof(uintptr_t)), StackPointer); + // Push actual arguments. + masm.storePtr(numActArgsReg, Address(StackPointer, 2 * sizeof(uintptr_t))); + // Push callee token. + masm.storePtr(calleeTokenReg, Address(StackPointer, sizeof(uintptr_t))); + // Push frame descriptor. + masm.storePtr(t0, Address(StackPointer, 0)); + + // Call the target function. + // Note that this code assumes the function is JITted. + masm.loadPtr(Address(calleeTokenReg, JSFunction::offsetOfNativeOrScript()), t1); + masm.loadBaselineOrIonRaw(t1, t1, mode, nullptr); + masm.ma_callIonHalfPush(t1); + + uint32_t returnOffset = masm.currentOffset(); + + // arg1 + // ... + // argN + // num actual args + // callee token + // sizeDescriptor <- sp now + // return address + + // Remove the rectifier frame. + // t0 <- descriptor with FrameType. + masm.loadPtr(Address(StackPointer, 0), t0); + masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), t0); // t0 <- descriptor. + + // Discard descriptor, calleeToken and number of actual arguments. + masm.addPtr(Imm32(3 * sizeof(uintptr_t)), StackPointer); + + // arg1 + // ... + // argN <- sp now; t0 <- frame descriptor + // num actual args + // callee token + // sizeDescriptor + // return address + + // Discard pushed arguments. + masm.addPtr(t0, StackPointer); + + masm.ret(); + Linker linker(masm); + AutoFlushICache afc("ArgumentsRectifier"); + JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); + + CodeOffsetLabel returnLabel(returnOffset); + returnLabel.fixup(&masm); + if (returnAddrOut) + *returnAddrOut = (void *) (code->raw() + returnLabel.offset()); + +#ifdef JS_ION_PERF + writePerfSpewerJitCodeProfile(code, "ArgumentsRectifier"); +#endif + + return code; +} + +/* There are two different stack layouts when doing bailout. They are + * represented via class BailoutStack. + * + * - First case is when bailout is done trough bailout table. In this case + * table offset is stored in $ra (look at JitRuntime::generateBailoutTable()) + * and thunk code should save it on stack. In this case frameClassId_ cannot + * be NO_FRAME_SIZE_CLASS_ID. Members snapshotOffset_ and padding_ are not on + * the stack. + * + * - Other case is when bailout is done via out of line code (lazy bailout). + * In this case frame size is stored in $ra (look at + * CodeGeneratorMIPS::generateOutOfLineCode()) and thunk code should save it + * on stack. Other difference is that members snapshotOffset_ and padding_ are + * pushed to the stack by CodeGeneratorMIPS::visitOutOfLineBailout(). Field + * frameClassId_ is forced to be NO_FRAME_SIZE_CLASS_ID + * (See: JitRuntime::generateBailoutHandler). + */ +static void +GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass) +{ + // NOTE: Members snapshotOffset_ and padding_ of BailoutStack + // are not stored in this function. + static const uint32_t bailoutDataSize = sizeof(BailoutStack) - 2 * sizeof(uintptr_t); + static const uint32_t bailoutInfoOutParamSize = 2 * sizeof(uintptr_t); + + // Make sure that alignment is proper. + masm.checkStackAlignment(); + + // Make room for data. + masm.subPtr(Imm32(bailoutDataSize), StackPointer); + + // Save general purpose registers. + for (uint32_t i = 0; i < Registers::Total; i++) { + uint32_t off = BailoutStack::offsetOfRegs() + i * sizeof(uintptr_t); + masm.storePtr(Register::FromCode(i), Address(StackPointer, off)); + } + + // Save floating point registers + // We can use as_sd because stack is alligned. + for (uintptr_t i = 0; i < FloatRegisters::Total; i++) + masm.as_sd(FloatRegister::FromCode(i), StackPointer, + BailoutStack::offsetOfFpRegs() + i * sizeof(double)); + + // Store the frameSize_ or tableOffset_ stored in ra + // See: JitRuntime::generateBailoutTable() + // See: CodeGeneratorMIPS::generateOutOfLineCode() + masm.storePtr(ra, Address(StackPointer, BailoutStack::offsetOfFrameSize())); + + // Put frame class to stack + masm.storePtr(ImmWord(frameClass), Address(StackPointer, BailoutStack::offsetOfFrameClass())); + + // Put pointer to BailoutStack as first argument to the Bailout() + masm.movePtr(StackPointer, a0); + // Put pointer to BailoutInfo + masm.subPtr(Imm32(bailoutInfoOutParamSize), StackPointer); + masm.storePtr(ImmPtr(nullptr), Address(StackPointer, 0)); + masm.movePtr(StackPointer, a1); + + masm.setupAlignedABICall(2); + masm.passABIArg(a0); + masm.passABIArg(a1); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, Bailout)); + + // Get BailoutInfo pointer + masm.loadPtr(Address(StackPointer, 0), a2); + + // Remove both the bailout frame and the topmost Ion frame's stack. + if (frameClass == NO_FRAME_SIZE_CLASS_ID) { + // Load frameSize from stack + masm.loadPtr(Address(StackPointer, + bailoutInfoOutParamSize + BailoutStack::offsetOfFrameSize()), a1); + + // Remove complete BailoutStack class and data after it + masm.addPtr(Imm32(sizeof(BailoutStack) + bailoutInfoOutParamSize), StackPointer); + // Remove frame size srom stack + masm.addPtr(a1, StackPointer); + } else { + uint32_t frameSize = FrameSizeClass::FromClass(frameClass).frameSize(); + // Remove the data this fuction added and frame size. + masm.addPtr(Imm32(bailoutDataSize + bailoutInfoOutParamSize + frameSize), StackPointer); + } + + // Jump to shared bailout tail. The BailoutInfo pointer has to be in a2. + JitCode *bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail(); + masm.branch(bailoutTail); +} + +JitCode * +JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass) +{ + MacroAssembler masm(cx); + + Label bailout; + for (size_t i = 0; i < BAILOUT_TABLE_SIZE; i++) { + // Calculate offset to the end of table + int32_t offset = (BAILOUT_TABLE_SIZE - i) * BAILOUT_TABLE_ENTRY_SIZE; + + // We use the 'ra' as table offset later in GenerateBailoutThunk + masm.as_bal(BOffImm16(offset)); + masm.nop(); + } + masm.bind(&bailout); + + GenerateBailoutThunk(cx, masm, frameClass); + + Linker linker(masm); + AutoFlushICache afc("BailoutTable"); + JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerJitCodeProfile(code, "BailoutTable"); +#endif + + return code; +} + +JitCode * +JitRuntime::generateBailoutHandler(JSContext *cx) +{ + MacroAssembler masm(cx); + GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID); + + Linker linker(masm); + AutoFlushICache afc("BailoutHandler"); + JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerJitCodeProfile(code, "BailoutHandler"); +#endif + + return code; +} + +JitCode * +JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f) +{ + MOZ_ASSERT(functionWrappers_); + MOZ_ASSERT(functionWrappers_->initialized()); + VMWrapperMap::AddPtr p = functionWrappers_->lookupForAdd(&f); + if (p) + return p->value(); + + MacroAssembler masm(cx); + + GeneralRegisterSet regs = GeneralRegisterSet(Register::Codes::WrapperMask); + + static_assert((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0, + "Wrapper register set should be a superset of Volatile register set."); + + // The context is the first argument; a0 is the first argument register. + Register cxreg = a0; + regs.take(cxreg); + + // We're aligned to an exit frame, so link it up. + masm.enterExitFrameAndLoadContext(&f, cxreg, regs.getAny(), f.executionMode); + + // Save the base of the argument set stored on the stack. + Register argsBase = InvalidReg; + if (f.explicitArgs) { + argsBase = t1; // Use temporary register. + regs.take(argsBase); + masm.ma_addu(argsBase, StackPointer, Imm32(IonExitFrameLayout::SizeWithFooter())); + } + + // Reserve space for the outparameter. + Register outReg = InvalidReg; + switch (f.outParam) { + case Type_Value: + outReg = t0; // Use temporary register. + regs.take(outReg); + // Value outparam has to be 8 byte aligned because the called + // function can use sdc1 or ldc1 instructions to access it. + masm.reserveStack((StackAlignment - sizeof(uintptr_t)) + sizeof(Value)); + masm.alignPointerUp(StackPointer, outReg, StackAlignment); + break; + + case Type_Handle: + outReg = t0; + regs.take(outReg); + if (f.outParamRootType == VMFunction::RootValue) { + // Value outparam has to be 8 byte aligned because the called + // function can use sdc1 or ldc1 instructions to access it. + masm.reserveStack((StackAlignment - sizeof(uintptr_t)) + sizeof(Value)); + masm.alignPointerUp(StackPointer, outReg, StackAlignment); + masm.storeValue(UndefinedValue(), Address(outReg, 0)); + } + else { + masm.PushEmptyRooted(f.outParamRootType); + masm.movePtr(StackPointer, outReg); + } + break; + + case Type_Bool: + case Type_Int32: + MOZ_ASSERT(sizeof(uintptr_t) == sizeof(uint32_t)); + case Type_Pointer: + outReg = t0; + regs.take(outReg); + masm.reserveStack(sizeof(uintptr_t)); + masm.movePtr(StackPointer, outReg); + break; + + case Type_Double: + outReg = t0; + regs.take(outReg); + // Double outparam has to be 8 byte aligned because the called + // function can use sdc1 or ldc1 instructions to access it. + masm.reserveStack((StackAlignment - sizeof(uintptr_t)) + sizeof(double)); + masm.alignPointerUp(StackPointer, outReg, StackAlignment); + break; + + default: + MOZ_ASSERT(f.outParam == Type_Void); + break; + } + + masm.setupUnalignedABICall(f.argc(), regs.getAny()); + masm.passABIArg(cxreg); + + size_t argDisp = 0; + + // Copy any arguments. + for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) { + MoveOperand from; + switch (f.argProperties(explicitArg)) { + case VMFunction::WordByValue: + masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL); + argDisp += sizeof(uint32_t); + break; + case VMFunction::DoubleByValue: + // Values should be passed by reference, not by value, so we + // assert that the argument is a double-precision float. + MOZ_ASSERT(f.argPassedInFloatReg(explicitArg)); + masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::DOUBLE); + argDisp += sizeof(double); + break; + case VMFunction::WordByRef: + masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS), + MoveOp::GENERAL); + argDisp += sizeof(uint32_t); + break; + case VMFunction::DoubleByRef: + masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS), + MoveOp::GENERAL); + argDisp += sizeof(double); + break; + } + } + + // Copy the implicit outparam, if any. + if (outReg != InvalidReg) + masm.passABIArg(outReg); + + masm.callWithABI(f.wrapped); + + // Test for failure. + switch (f.failType()) { + case Type_Object: + masm.branchTestPtr(Assembler::Zero, v0, v0, masm.failureLabel(f.executionMode)); + break; + case Type_Bool: + // Called functions return bools, which are 0/false and non-zero/true + masm.branchIfFalseBool(v0, masm.failureLabel(f.executionMode)); + break; + default: + MOZ_ASSUME_UNREACHABLE("unknown failure kind"); + } + + // Load the outparam and free any allocated stack. + switch (f.outParam) { + case Type_Handle: + if (f.outParamRootType == VMFunction::RootValue) { + masm.alignPointerUp(StackPointer, SecondScratchReg, StackAlignment); + masm.loadValue(Address(SecondScratchReg, 0), JSReturnOperand); + masm.freeStack((StackAlignment - sizeof(uintptr_t)) + sizeof(Value)); + } + else { + masm.popRooted(f.outParamRootType, ReturnReg, JSReturnOperand); + } + break; + + case Type_Value: + masm.alignPointerUp(StackPointer, SecondScratchReg, StackAlignment); + masm.loadValue(Address(SecondScratchReg, 0), JSReturnOperand); + masm.freeStack((StackAlignment - sizeof(uintptr_t)) + sizeof(Value)); + break; + + case Type_Int32: + MOZ_ASSERT(sizeof(uintptr_t) == sizeof(uint32_t)); + case Type_Pointer: + masm.load32(Address(StackPointer, 0), ReturnReg); + masm.freeStack(sizeof(uintptr_t)); + break; + + case Type_Bool: + masm.load8ZeroExtend(Address(StackPointer, 0), ReturnReg); + masm.freeStack(sizeof(uintptr_t)); + break; + + case Type_Double: + if (cx->runtime()->jitSupportsFloatingPoint) { + masm.alignPointerUp(StackPointer, SecondScratchReg, StackAlignment); + // Address is aligned, so we can use as_ld. + masm.as_ld(ReturnFloatReg, SecondScratchReg, 0); + } else { + masm.assumeUnreachable("Unable to load into float reg, with no FP support."); + } + masm.freeStack((StackAlignment - sizeof(uintptr_t)) + sizeof(double)); + break; + + default: + MOZ_ASSERT(f.outParam == Type_Void); + break; + } + masm.leaveExitFrame(); + masm.retn(Imm32(sizeof(IonExitFrameLayout) + + f.explicitStackSlots() * sizeof(uintptr_t) + + f.extraValuesToPop * sizeof(Value))); + + Linker linker(masm); + AutoFlushICache afc("VMWrapper"); + JitCode *wrapper = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); + if (!wrapper) + return nullptr; + + // linker.newCode may trigger a GC and sweep functionWrappers_ so we have + // to use relookupOrAdd instead of add. + if (!functionWrappers_->relookupOrAdd(p, &f, wrapper)) + return nullptr; + +#ifdef JS_ION_PERF + writePerfSpewerJitCodeProfile(wrapper, "VMWrapper"); +#endif + + return wrapper; +} + +JitCode * +JitRuntime::generatePreBarrier(JSContext *cx, MIRType type) +{ + MacroAssembler masm(cx); + + RegisterSet save; + if (cx->runtime()->jitSupportsFloatingPoint) { + save = RegisterSet(GeneralRegisterSet(Registers::VolatileMask), + FloatRegisterSet(FloatRegisters::VolatileMask)); + } else { + save = RegisterSet(GeneralRegisterSet(Registers::VolatileMask), + FloatRegisterSet()); + } + masm.PushRegsInMask(save); + + MOZ_ASSERT(PreBarrierReg == a1); + masm.movePtr(ImmPtr(cx->runtime()), a0); + + masm.setupUnalignedABICall(2, a2); + masm.passABIArg(a0); + masm.passABIArg(a1); + + if (type == MIRType_Value) { + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkValueFromIon)); + } else { + MOZ_ASSERT(type == MIRType_Shape); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkShapeFromIon)); + } + + masm.PopRegsInMask(save); + masm.ret(); + + Linker linker(masm); + AutoFlushICache afc("PreBarrier"); + JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerJitCodeProfile(code, "PreBarrier"); +#endif + + return code; +} + +typedef bool (*HandleDebugTrapFn)(JSContext *, BaselineFrame *, uint8_t *, bool *); +static const VMFunction HandleDebugTrapInfo = FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap); + +JitCode * +JitRuntime::generateDebugTrapHandler(JSContext *cx) +{ + MacroAssembler masm(cx); + + Register scratch1 = t0; + Register scratch2 = t1; + + // Load BaselineFrame pointer in scratch1. + masm.movePtr(s5, scratch1); + masm.subPtr(Imm32(BaselineFrame::Size()), scratch1); + + // Enter a stub frame and call the HandleDebugTrap VM function. Ensure + // the stub frame has a nullptr ICStub pointer, since this pointer is + // marked during GC. + masm.movePtr(ImmPtr(nullptr), BaselineStubReg); + EmitEnterStubFrame(masm, scratch2); + + JitCode *code = cx->runtime()->jitRuntime()->getVMWrapper(HandleDebugTrapInfo); + if (!code) + return nullptr; + + masm.subPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer); + masm.storePtr(ra, Address(StackPointer, sizeof(uintptr_t))); + masm.storePtr(scratch1, Address(StackPointer, 0)); + + EmitCallVM(code, masm); + + EmitLeaveStubFrame(masm); + + // If the stub returns |true|, we have to perform a forced return + // (return from the JS frame). If the stub returns |false|, just return + // from the trap stub so that execution continues at the current pc. + Label forcedReturn; + masm.branchTest32(Assembler::NonZero, ReturnReg, ReturnReg, &forcedReturn); + + // ra was restored by EmitLeaveStubFrame + masm.branch(ra); + + masm.bind(&forcedReturn); + masm.loadValue(Address(s5, BaselineFrame::reverseOffsetOfReturnValue()), + JSReturnOperand); + masm.movePtr(s5, StackPointer); + masm.pop(s5); + masm.ret(); + + Linker linker(masm); + AutoFlushICache afc("DebugTrapHandler"); + JitCode *codeDbg = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerJitCodeProfile(codeDbg, "DebugTrapHandler"); +#endif + + return codeDbg; +} + + +JitCode * +JitRuntime::generateExceptionTailStub(JSContext *cx) +{ + MacroAssembler masm; + + masm.handleFailureWithHandlerTail(); + + Linker linker(masm); + AutoFlushICache afc("ExceptionTailStub"); + JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerJitCodeProfile(code, "ExceptionTailStub"); +#endif + + return code; +} + +JitCode * +JitRuntime::generateBailoutTailStub(JSContext *cx) +{ + MacroAssembler masm; + + masm.generateBailoutTail(a1, a2); + + Linker linker(masm); + AutoFlushICache afc("BailoutTailStub"); + JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerJitCodeProfile(code, "BailoutTailStub"); +#endif + + return code; +} +