| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * 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 "instruction_set_features.h" |
| |
| #include <algorithm> |
| #include <ostream> |
| |
| #include "android-base/strings.h" |
| #include "arm/instruction_set_features_arm.h" |
| #include "arm64/instruction_set_features_arm64.h" |
| #include "base/casts.h" |
| #include "base/utils.h" |
| #include "riscv64/instruction_set_features_riscv64.h" |
| #include "x86/instruction_set_features_x86.h" |
| #include "x86_64/instruction_set_features_x86_64.h" |
| |
| namespace art { |
| |
| std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromVariant( |
| InstructionSet isa, const std::string& variant, std::string* error_msg) { |
| switch (isa) { |
| case InstructionSet::kArm: |
| case InstructionSet::kThumb2: |
| return ArmInstructionSetFeatures::FromVariant(variant, error_msg); |
| case InstructionSet::kArm64: |
| return Arm64InstructionSetFeatures::FromVariant(variant, error_msg); |
| case InstructionSet::kRiscv64: |
| return Riscv64InstructionSetFeatures::FromVariant(variant, error_msg); |
| case InstructionSet::kX86: |
| return X86InstructionSetFeatures::FromVariant(variant, error_msg); |
| case InstructionSet::kX86_64: |
| return X86_64InstructionSetFeatures::FromVariant(variant, error_msg); |
| |
| default: |
| break; |
| } |
| UNIMPLEMENTED(FATAL) << isa; |
| UNREACHABLE(); |
| } |
| |
| std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromVariantAndHwcap( |
| InstructionSet isa, const std::string& variant, std::string* error_msg) { |
| auto variant_features = FromVariant(isa, variant, error_msg); |
| if (variant_features == nullptr) { |
| return nullptr; |
| } |
| // Pixel3a is wrongly reporting itself as cortex-a75, so validate the features |
| // with hwcaps. |
| // Note that when cross-compiling on device (using dex2oat32 for compiling |
| // arm64), the hwcaps will report that no feature is supported. This is |
| // currently our best approach to be safe/correct. Maybe using the |
| // cpu_features library could fix this issue. |
| if (isa == InstructionSet::kArm64) { |
| auto new_features = down_cast<const Arm64InstructionSetFeatures*>(variant_features.get()) |
| ->IntersectWithHwcap(); |
| if (!variant_features->Equals(new_features.get())) { |
| LOG(WARNING) << "Mismatch between instruction set variant of device (" |
| << *variant_features |
| << ") and features returned by the hardware (" << *new_features << ")"; |
| } |
| return new_features; |
| } else { |
| // TODO: Implement this validation on all architectures. |
| return variant_features; |
| } |
| } |
| |
| std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromBitmap(InstructionSet isa, |
| uint32_t bitmap) { |
| std::unique_ptr<const InstructionSetFeatures> result; |
| switch (isa) { |
| case InstructionSet::kArm: |
| case InstructionSet::kThumb2: |
| result = ArmInstructionSetFeatures::FromBitmap(bitmap); |
| break; |
| case InstructionSet::kArm64: |
| result = Arm64InstructionSetFeatures::FromBitmap(bitmap); |
| break; |
| case InstructionSet::kRiscv64: |
| result = Riscv64InstructionSetFeatures::FromBitmap(bitmap); |
| break; |
| case InstructionSet::kX86: |
| result = X86InstructionSetFeatures::FromBitmap(bitmap); |
| break; |
| case InstructionSet::kX86_64: |
| result = X86_64InstructionSetFeatures::FromBitmap(bitmap); |
| break; |
| |
| default: |
| UNIMPLEMENTED(FATAL) << isa; |
| UNREACHABLE(); |
| } |
| CHECK_EQ(bitmap, result->AsBitmap()); |
| return result; |
| } |
| |
| std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromCppDefines() { |
| switch (kRuntimeISA) { |
| case InstructionSet::kArm: |
| case InstructionSet::kThumb2: |
| return ArmInstructionSetFeatures::FromCppDefines(); |
| case InstructionSet::kArm64: |
| return Arm64InstructionSetFeatures::FromCppDefines(); |
| case InstructionSet::kRiscv64: |
| return Riscv64InstructionSetFeatures::FromCppDefines(); |
| case InstructionSet::kX86: |
| return X86InstructionSetFeatures::FromCppDefines(); |
| case InstructionSet::kX86_64: |
| return X86_64InstructionSetFeatures::FromCppDefines(); |
| |
| default: |
| break; |
| } |
| UNIMPLEMENTED(FATAL) << kRuntimeISA; |
| UNREACHABLE(); |
| } |
| |
| std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromRuntimeDetection() { |
| switch (kRuntimeISA) { |
| #ifdef ART_TARGET_ANDROID |
| case InstructionSet::kArm64: |
| return Arm64InstructionSetFeatures::FromHwcap(); |
| #endif |
| default: |
| return nullptr; |
| } |
| } |
| |
| std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromCpuInfo() { |
| switch (kRuntimeISA) { |
| case InstructionSet::kArm: |
| case InstructionSet::kThumb2: |
| return ArmInstructionSetFeatures::FromCpuInfo(); |
| case InstructionSet::kArm64: |
| return Arm64InstructionSetFeatures::FromCpuInfo(); |
| case InstructionSet::kRiscv64: |
| return Riscv64InstructionSetFeatures::FromCpuInfo(); |
| case InstructionSet::kX86: |
| return X86InstructionSetFeatures::FromCpuInfo(); |
| case InstructionSet::kX86_64: |
| return X86_64InstructionSetFeatures::FromCpuInfo(); |
| |
| default: |
| break; |
| } |
| UNIMPLEMENTED(FATAL) << kRuntimeISA; |
| UNREACHABLE(); |
| } |
| |
| std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromHwcap() { |
| switch (kRuntimeISA) { |
| case InstructionSet::kArm: |
| case InstructionSet::kThumb2: |
| return ArmInstructionSetFeatures::FromHwcap(); |
| case InstructionSet::kArm64: |
| return Arm64InstructionSetFeatures::FromHwcap(); |
| case InstructionSet::kRiscv64: |
| return Riscv64InstructionSetFeatures::FromHwcap(); |
| case InstructionSet::kX86: |
| return X86InstructionSetFeatures::FromHwcap(); |
| case InstructionSet::kX86_64: |
| return X86_64InstructionSetFeatures::FromHwcap(); |
| |
| default: |
| break; |
| } |
| UNIMPLEMENTED(FATAL) << kRuntimeISA; |
| UNREACHABLE(); |
| } |
| |
| std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromAssembly() { |
| switch (kRuntimeISA) { |
| case InstructionSet::kArm: |
| case InstructionSet::kThumb2: |
| return ArmInstructionSetFeatures::FromAssembly(); |
| case InstructionSet::kArm64: |
| return Arm64InstructionSetFeatures::FromAssembly(); |
| case InstructionSet::kRiscv64: |
| return Riscv64InstructionSetFeatures::FromAssembly(); |
| case InstructionSet::kX86: |
| return X86InstructionSetFeatures::FromAssembly(); |
| case InstructionSet::kX86_64: |
| return X86_64InstructionSetFeatures::FromAssembly(); |
| |
| default: |
| break; |
| } |
| UNIMPLEMENTED(FATAL) << kRuntimeISA; |
| UNREACHABLE(); |
| } |
| |
| std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromCpuFeatures() { |
| switch (kRuntimeISA) { |
| case InstructionSet::kArm: |
| case InstructionSet::kThumb2: |
| return ArmInstructionSetFeatures::FromCpuFeatures(); |
| case InstructionSet::kArm64: |
| return Arm64InstructionSetFeatures::FromCpuFeatures(); |
| case InstructionSet::kRiscv64: |
| return Riscv64InstructionSetFeatures::FromCpuFeatures(); |
| case InstructionSet::kX86: |
| return X86InstructionSetFeatures::FromCpuFeatures(); |
| case InstructionSet::kX86_64: |
| return X86_64InstructionSetFeatures::FromCpuFeatures(); |
| |
| default: |
| break; |
| } |
| UNIMPLEMENTED(FATAL) << kRuntimeISA; |
| UNREACHABLE(); |
| } |
| |
| std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::AddFeaturesFromString( |
| const std::string& feature_list, /* out */ std::string* error_msg) const { |
| std::vector<std::string> features; |
| Split(feature_list, ',', &features); |
| std::transform(std::begin(features), std::end(features), std::begin(features), |
| [](const std::string &s) { return android::base::Trim(s); }); |
| auto empty_strings_begin = std::copy_if(std::begin(features), std::end(features), |
| std::begin(features), |
| [](const std::string& s) { return !s.empty(); }); |
| features.erase(empty_strings_begin, std::end(features)); |
| if (features.empty()) { |
| *error_msg = "No instruction set features specified"; |
| return nullptr; |
| } |
| |
| bool use_default = false; |
| bool use_runtime_detection = false; |
| for (const std::string& feature : features) { |
| if (feature == "default") { |
| if (features.size() > 1) { |
| *error_msg = "Specific instruction set feature(s) cannot be used when 'default' is used."; |
| return nullptr; |
| } |
| use_default = true; |
| features.pop_back(); |
| break; |
| } else if (feature == "runtime") { |
| if (features.size() > 1) { |
| *error_msg = "Specific instruction set feature(s) cannot be used when 'runtime' is used."; |
| return nullptr; |
| } |
| use_runtime_detection = true; |
| features.pop_back(); |
| break; |
| } |
| } |
| // Expectation: "default" and "runtime" are standalone, no other feature names. |
| // But an empty features vector after processing can also come along if the |
| // handled feature names are the only ones in the list. So |
| // logically, we check "default or runtime => features.empty." |
| DCHECK((!use_default && !use_runtime_detection) || features.empty()); |
| |
| std::unique_ptr<const InstructionSetFeatures> runtime_detected_features; |
| if (use_runtime_detection) { |
| runtime_detected_features = FromRuntimeDetection(); |
| } |
| |
| if (runtime_detected_features != nullptr) { |
| return AddRuntimeDetectedFeatures(runtime_detected_features.get()); |
| } else { |
| return AddFeaturesFromSplitString(features, error_msg); |
| } |
| } |
| |
| const ArmInstructionSetFeatures* InstructionSetFeatures::AsArmInstructionSetFeatures() const { |
| DCHECK_EQ(InstructionSet::kArm, GetInstructionSet()); |
| return down_cast<const ArmInstructionSetFeatures*>(this); |
| } |
| |
| const Arm64InstructionSetFeatures* InstructionSetFeatures::AsArm64InstructionSetFeatures() const { |
| DCHECK_EQ(InstructionSet::kArm64, GetInstructionSet()); |
| return down_cast<const Arm64InstructionSetFeatures*>(this); |
| } |
| |
| const Riscv64InstructionSetFeatures* InstructionSetFeatures::AsRiscv64InstructionSetFeatures() |
| const { |
| DCHECK_EQ(InstructionSet::kRiscv64, GetInstructionSet()); |
| return down_cast<const Riscv64InstructionSetFeatures*>(this); |
| } |
| |
| const X86InstructionSetFeatures* InstructionSetFeatures::AsX86InstructionSetFeatures() const { |
| DCHECK(InstructionSet::kX86 == GetInstructionSet() || |
| InstructionSet::kX86_64 == GetInstructionSet()); |
| return down_cast<const X86InstructionSetFeatures*>(this); |
| } |
| |
| const X86_64InstructionSetFeatures* InstructionSetFeatures::AsX86_64InstructionSetFeatures() const { |
| DCHECK_EQ(InstructionSet::kX86_64, GetInstructionSet()); |
| return down_cast<const X86_64InstructionSetFeatures*>(this); |
| } |
| |
| bool InstructionSetFeatures::FindVariantInArray(const char* const variants[], size_t num_variants, |
| const std::string& variant) { |
| const char* const * begin = variants; |
| const char* const * end = begin + num_variants; |
| return std::find(begin, end, variant) != end; |
| } |
| |
| std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::AddRuntimeDetectedFeatures( |
| const InstructionSetFeatures *features ATTRIBUTE_UNUSED) const { |
| UNIMPLEMENTED(FATAL) << kRuntimeISA; |
| UNREACHABLE(); |
| } |
| |
| std::ostream& operator<<(std::ostream& os, const InstructionSetFeatures& rhs) { |
| os << "ISA: " << rhs.GetInstructionSet() << " Feature string: " << rhs.GetFeatureString(); |
| return os; |
| } |
| |
| } // namespace art |