Merge "ART: Change InstructionSetFeatures to return unique_ptr"
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 61f682c..0b14859 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -342,6 +342,7 @@
"utils/string_reference_test.cc",
"utils/swap_space_test.cc",
"utils/test_dex_file_builder_test.cc",
+ "verifier_deps_test.cc",
"jni/jni_cfi_test.cc",
"optimizing/codegen_test.cc",
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index dbde41c..56b4ebd 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -72,6 +72,7 @@
#include "verifier/method_verifier.h"
#include "verifier/method_verifier-inl.h"
#include "verifier/verifier_log_mode.h"
+#include "verifier/verifier_deps.h"
namespace art {
@@ -1968,6 +1969,7 @@
hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
Handle<mirror::Class> klass(
hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader)));
+ verifier::MethodVerifier::FailureKind failure_kind;
if (klass.Get() == nullptr) {
CHECK(soa.Self()->IsExceptionPending());
soa.Self()->ClearException();
@@ -1980,7 +1982,8 @@
Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(
soa.Self(), dex_file, false)));
std::string error_msg;
- if (verifier::MethodVerifier::VerifyClass(soa.Self(),
+ failure_kind =
+ verifier::MethodVerifier::VerifyClass(soa.Self(),
&dex_file,
dex_cache,
class_loader,
@@ -1988,15 +1991,15 @@
Runtime::Current()->GetCompilerCallbacks(),
true /* allow soft failures */,
log_level_,
- &error_msg) ==
- verifier::MethodVerifier::kHardFailure) {
+ &error_msg);
+ if (failure_kind == verifier::MethodVerifier::kHardFailure) {
LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(descriptor)
<< " because: " << error_msg;
manager_->GetCompiler()->SetHadHardVerifierFailure();
}
} else if (!SkipClass(jclass_loader, dex_file, klass.Get())) {
CHECK(klass->IsResolved()) << klass->PrettyClass();
- class_linker->VerifyClass(soa.Self(), klass, log_level_);
+ failure_kind = class_linker->VerifyClass(soa.Self(), klass, log_level_);
if (klass->IsErroneous()) {
// ClassLinker::VerifyClass throws, which isn't useful in the compiler.
@@ -2008,13 +2011,18 @@
CHECK(klass->IsCompileTimeVerified() || klass->IsErroneous())
<< klass->PrettyDescriptor() << ": state=" << klass->GetStatus();
- // It is *very* problematic if there are verification errors in the boot classpath. For example,
- // we rely on things working OK without verification when the decryption dialog is brought up.
- // So abort in a debug build if we find this violated.
+ // It is *very* problematic if there are verification errors in the boot classpath.
+ // For example, we rely on things working OK without verification when the
+ // decryption dialog is brought up. So abort in a debug build if we find this violated.
DCHECK(!manager_->GetCompiler()->GetCompilerOptions().IsBootImage() || klass->IsVerified())
<< "Boot classpath class " << klass->PrettyClass()
<< " failed to fully verify.";
+ } else {
+ // Make the skip a soft failure, essentially being considered as verify at runtime.
+ failure_kind = verifier::MethodVerifier::kSoftFailure;
}
+ verifier::VerifierDeps::MaybeRecordVerificationStatus(
+ dex_file, class_def.class_idx_, failure_kind);
soa.Self()->AssertNoPendingException();
}
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 9a4dd85..4a48f9c 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -50,6 +50,7 @@
namespace verifier {
class MethodVerifier;
+class VerifierDepsTest;
} // namespace verifier
class BitVector;
@@ -578,6 +579,7 @@
const BitVector* current_dex_to_dex_methods_;
friend class CompileClassVisitor;
+ friend class verifier::VerifierDepsTest;
DISALLOW_COPY_AND_ASSIGN(CompilerDriver);
};
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 4eb6954..56b632d 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -29,6 +29,10 @@
namespace art {
+namespace verifier {
+ class VerifierDepsTest;
+}
+
class DexFile;
class CompilerOptions FINAL {
@@ -338,6 +342,7 @@
friend class Dex2Oat;
friend class CommonCompilerTest;
+ friend class verifier::VerifierDepsTest;
DISALLOW_COPY_AND_ASSIGN(CompilerOptions);
};
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 32287a0..bfade3c 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -488,6 +488,15 @@
isa_features_(isa_features) {
// Always save the LR register to mimic Quick.
AddAllocatedRegister(Location::RegisterLocation(LR));
+ // Give d14 and d15 as scratch registers to VIXL.
+ // They are removed from the register allocator in `SetupBlockedRegisters()`.
+ // TODO(VIXL): We need two scratch D registers for `EmitSwap` when swapping two double stack
+ // slots. If that is sufficiently rare, and we have pressure on FP registers, we could instead
+ // spill in `EmitSwap`. But if we actually are guaranteed to have 32 D registers, we could give
+ // d30 and d31 to VIXL to avoid removing registers from the allocator. If that is the case, we may
+ // also want to investigate giving those 14 other D registers to the allocator.
+ GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d14);
+ GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d15);
}
#define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()->
@@ -509,6 +518,13 @@
// Reserve temp register.
blocked_core_registers_[IP] = true;
+ // Registers s28-s31 (d14-d15) are left to VIXL for scratch registers.
+ // (They are given to the `MacroAssembler` in `CodeGeneratorARMVIXL::CodeGeneratorARMVIXL`.)
+ blocked_fpu_registers_[28] = true;
+ blocked_fpu_registers_[29] = true;
+ blocked_fpu_registers_[30] = true;
+ blocked_fpu_registers_[31] = true;
+
if (GetGraph()->IsDebuggable()) {
// Stubs do not save callee-save floating point registers. If the graph
// is debuggable, we need to deal with these registers differently. For
@@ -2161,6 +2177,92 @@
}
}
+void LocationsBuilderARMVIXL::VisitCompare(HCompare* compare) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
+ switch (compare->InputAt(0)->GetType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimLong: {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ // Output overlaps because it is written before doing the low comparison.
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+ break;
+ }
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble: {
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, ArithmeticZeroOrFpuRegister(compare->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister());
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType();
+ }
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) {
+ LocationSummary* locations = compare->GetLocations();
+ vixl32::Register out = OutputRegister(compare);
+ Location left = locations->InAt(0);
+ Location right = locations->InAt(1);
+
+ vixl32::Label less, greater, done;
+ Primitive::Type type = compare->InputAt(0)->GetType();
+ vixl32::Condition less_cond = vixl32::Condition(kNone);
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimInt: {
+ // Emit move to `out` before the `Cmp`, as `Mov` might affect the status flags.
+ __ Mov(out, 0);
+ __ Cmp(RegisterFrom(left), RegisterFrom(right)); // Signed compare.
+ less_cond = lt;
+ break;
+ }
+ case Primitive::kPrimLong: {
+ __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right)); // Signed compare.
+ __ B(lt, &less);
+ __ B(gt, &greater);
+ // Emit move to `out` before the last `Cmp`, as `Mov` might affect the status flags.
+ __ Mov(out, 0);
+ __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right)); // Unsigned compare.
+ less_cond = lo;
+ break;
+ }
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble: {
+ __ Mov(out, 0);
+ GenerateVcmp(compare);
+ // To branch on the FP compare result we transfer FPSCR to APSR (encoded as PC in VMRS).
+ __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
+ less_cond = ARMFPCondition(kCondLT, compare->IsGtBias());
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected compare type " << type;
+ UNREACHABLE();
+ }
+
+ __ B(eq, &done);
+ __ B(less_cond, &less);
+
+ __ Bind(&greater);
+ __ Mov(out, 1);
+ __ B(&done);
+
+ __ Bind(&less);
+ __ Mov(out, -1);
+
+ __ Bind(&done);
+}
+
void LocationsBuilderARMVIXL::VisitPhi(HPhi* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
@@ -2648,6 +2750,16 @@
}
}
+Location LocationsBuilderARMVIXL::ArithmeticZeroOrFpuRegister(HInstruction* input) {
+ DCHECK(Primitive::IsFloatingPointType(input->GetType())) << input->GetType();
+ if ((input->IsFloatConstant() && (input->AsFloatConstant()->IsArithmeticZero())) ||
+ (input->IsDoubleConstant() && (input->AsDoubleConstant()->IsArithmeticZero()))) {
+ return Location::ConstantLocation(input->AsConstant());
+ } else {
+ return Location::RequiresFpuRegister();
+ }
+}
+
void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction,
const FieldInfo& field_info) {
DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
@@ -2991,7 +3103,17 @@
} else if (source.IsFpuRegister()) {
TODO_VIXL32(FATAL);
} else if (source.IsDoubleStackSlot()) {
- TODO_VIXL32(FATAL);
+ if (destination.IsDoubleStackSlot()) {
+ vixl32::DRegister temp = temps.AcquireD();
+ GetAssembler()->LoadDFromOffset(temp, sp, source.GetStackIndex());
+ GetAssembler()->StoreDToOffset(temp, sp, destination.GetStackIndex());
+ } else if (destination.IsRegisterPair()) {
+ DCHECK(ExpectedPairLayout(destination));
+ GetAssembler()->LoadFromOffset(
+ kLoadWordPair, LowRegisterFrom(destination), sp, source.GetStackIndex());
+ } else {
+ TODO_VIXL32(FATAL);
+ }
} else if (source.IsRegisterPair()) {
if (destination.IsRegisterPair()) {
__ Mov(LowRegisterFrom(destination), LowRegisterFrom(source));
@@ -3070,18 +3192,77 @@
}
}
-void ParallelMoveResolverARMVIXL::Exchange(Register reg ATTRIBUTE_UNUSED,
- int mem ATTRIBUTE_UNUSED) {
- TODO_VIXL32(FATAL);
+void ParallelMoveResolverARMVIXL::Exchange(vixl32::Register reg, int mem) {
+ UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
+ vixl32::Register temp = temps.Acquire();
+ __ Mov(temp, reg);
+ GetAssembler()->LoadFromOffset(kLoadWord, reg, sp, mem);
+ GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
}
-void ParallelMoveResolverARMVIXL::Exchange(int mem1 ATTRIBUTE_UNUSED,
- int mem2 ATTRIBUTE_UNUSED) {
- TODO_VIXL32(FATAL);
+void ParallelMoveResolverARMVIXL::Exchange(int mem1, int mem2) {
+ // TODO(VIXL32): Double check the performance of this implementation.
+ UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
+ vixl32::Register temp = temps.Acquire();
+ vixl32::SRegister temp_s = temps.AcquireS();
+
+ __ Ldr(temp, MemOperand(sp, mem1));
+ __ Vldr(temp_s, MemOperand(sp, mem2));
+ __ Str(temp, MemOperand(sp, mem2));
+ __ Vstr(temp_s, MemOperand(sp, mem1));
}
-void ParallelMoveResolverARMVIXL::EmitSwap(size_t index ATTRIBUTE_UNUSED) {
- TODO_VIXL32(FATAL);
+void ParallelMoveResolverARMVIXL::EmitSwap(size_t index) {
+ MoveOperands* move = moves_[index];
+ Location source = move->GetSource();
+ Location destination = move->GetDestination();
+ UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
+
+ if (source.IsRegister() && destination.IsRegister()) {
+ vixl32::Register temp = temps.Acquire();
+ DCHECK(!RegisterFrom(source).Is(temp));
+ DCHECK(!RegisterFrom(destination).Is(temp));
+ __ Mov(temp, RegisterFrom(destination));
+ __ Mov(RegisterFrom(destination), RegisterFrom(source));
+ __ Mov(RegisterFrom(source), temp);
+ } else if (source.IsRegister() && destination.IsStackSlot()) {
+ Exchange(RegisterFrom(source), destination.GetStackIndex());
+ } else if (source.IsStackSlot() && destination.IsRegister()) {
+ Exchange(RegisterFrom(destination), source.GetStackIndex());
+ } else if (source.IsStackSlot() && destination.IsStackSlot()) {
+ TODO_VIXL32(FATAL);
+ } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
+ TODO_VIXL32(FATAL);
+ } else if (source.IsRegisterPair() && destination.IsRegisterPair()) {
+ vixl32::DRegister temp = temps.AcquireD();
+ __ Vmov(temp, LowRegisterFrom(source), HighRegisterFrom(source));
+ __ Mov(LowRegisterFrom(source), LowRegisterFrom(destination));
+ __ Mov(HighRegisterFrom(source), HighRegisterFrom(destination));
+ __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), temp);
+ } else if (source.IsRegisterPair() || destination.IsRegisterPair()) {
+ vixl32::Register low_reg = LowRegisterFrom(source.IsRegisterPair() ? source : destination);
+ int mem = source.IsRegisterPair() ? destination.GetStackIndex() : source.GetStackIndex();
+ DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination));
+ vixl32::DRegister temp = temps.AcquireD();
+ __ Vmov(temp, low_reg, vixl32::Register(low_reg.GetCode() + 1));
+ GetAssembler()->LoadFromOffset(kLoadWordPair, low_reg, sp, mem);
+ GetAssembler()->StoreDToOffset(temp, sp, mem);
+ } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
+ TODO_VIXL32(FATAL);
+ } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
+ TODO_VIXL32(FATAL);
+ } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
+ TODO_VIXL32(FATAL);
+ } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
+ vixl32::DRegister temp1 = temps.AcquireD();
+ vixl32::DRegister temp2 = temps.AcquireD();
+ __ Vldr(temp1, MemOperand(sp, source.GetStackIndex()));
+ __ Vldr(temp2, MemOperand(sp, destination.GetStackIndex()));
+ __ Vstr(temp1, MemOperand(sp, destination.GetStackIndex()));
+ __ Vstr(temp2, MemOperand(sp, source.GetStackIndex()));
+ } else {
+ LOG(FATAL) << "Unimplemented" << source << " <-> " << destination;
+ }
}
void ParallelMoveResolverARMVIXL::SpillScratch(int reg ATTRIBUTE_UNUSED) {
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index c749f86..7815db2 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -110,6 +110,7 @@
M(BelowOrEqual) \
M(ClearException) \
M(ClinitCheck) \
+ M(Compare) \
M(CurrentMethod) \
M(Div) \
M(DivZeroCheck) \
@@ -161,7 +162,6 @@
M(BoundType) \
M(CheckCast) \
M(ClassTableGet) \
- M(Compare) \
M(Deoptimize) \
M(DoubleConstant) \
M(FloatConstant) \
@@ -246,7 +246,7 @@
ArmVIXLAssembler* GetAssembler() const;
private:
- void Exchange(Register reg, int mem);
+ void Exchange(vixl32::Register reg, int mem);
void Exchange(int mem1, int mem2);
CodeGeneratorARMVIXL* const codegen_;
@@ -280,6 +280,8 @@
void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
+ Location ArithmeticZeroOrFpuRegister(HInstruction* input);
+
CodeGeneratorARMVIXL* const codegen_;
InvokeDexCallingConventionVisitorARM parameter_visitor_;
diff --git a/runtime/verifier/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
similarity index 94%
rename from runtime/verifier/verifier_deps_test.cc
rename to compiler/verifier_deps_test.cc
index 71203e6..9664e43 100644
--- a/runtime/verifier/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -14,14 +14,17 @@
* limitations under the License.
*/
-#include "verifier_deps.h"
+// Test is in compiler, as it uses compiler related code.
+#include "verifier/verifier_deps.h"
#include "class_linker.h"
-#include "common_runtime_test.h"
+#include "compiler/common_compiler_test.h"
+#include "compiler/driver/compiler_options.h"
+#include "compiler/driver/compiler_driver.h"
#include "compiler_callbacks.h"
#include "dex_file.h"
#include "handle_scope-inl.h"
-#include "method_verifier-inl.h"
+#include "verifier/method_verifier-inl.h"
#include "mirror/class_loader.h"
#include "runtime.h"
#include "thread.h"
@@ -47,10 +50,10 @@
verifier::VerifierDeps* deps_;
};
-class VerifierDepsTest : public CommonRuntimeTest {
+class VerifierDepsTest : public CommonCompilerTest {
public:
void SetUpRuntimeOptions(RuntimeOptions* options) {
- CommonRuntimeTest::SetUpRuntimeOptions(options);
+ CommonCompilerTest::SetUpRuntimeOptions(options);
callbacks_.reset(new VerifierDepsCompilerCallbacks());
}
@@ -147,23 +150,17 @@
void VerifyDexFile() {
std::string error_msg;
- ScopedObjectAccess soa(Thread::Current());
-
- LoadDexFile(&soa);
- SetVerifierDeps({ dex_file_ });
-
- for (size_t i = 0; i < dex_file_->NumClassDefs(); i++) {
- const char* descriptor = dex_file_->GetClassDescriptor(dex_file_->GetClassDef(i));
- mirror::Class* klass = FindClassByName(descriptor, &soa);
- if (klass != nullptr) {
- MethodVerifier::VerifyClass(Thread::Current(),
- klass,
- nullptr,
- true,
- HardFailLogMode::kLogWarning,
- &error_msg);
- }
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ LoadDexFile(&soa);
}
+ SetVerifierDeps({ dex_file_ });
+ TimingLogger timings("Verify", false, false);
+ std::vector<const DexFile*> dex_files;
+ dex_files.push_back(dex_file_);
+ compiler_options_->boot_image_ = false;
+ compiler_driver_->InitializeThreadPools();
+ compiler_driver_->Verify(class_loader_, dex_files, &timings);
}
bool TestAssignabilityRecording(const std::string& dst,
@@ -184,6 +181,21 @@
return true;
}
+ bool HasUnverifiedClass(const std::string& cls) {
+ const DexFile::TypeId* type_id = dex_file_->FindTypeId(cls.c_str());
+ DCHECK(type_id != nullptr);
+ uint16_t index = dex_file_->GetIndexForTypeId(*type_id);
+ MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
+ for (const auto& dex_dep : verifier_deps_->dex_deps_) {
+ for (uint16_t entry : dex_dep.second->unverified_classes_) {
+ if (index == entry) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
// Iterates over all assignability records and tries to find an entry which
// matches the expected destination/source pair.
bool HasAssignable(const std::string& expected_destination,
@@ -361,6 +373,7 @@
bool has_classes = false;
bool has_fields = false;
bool has_methods = false;
+ bool has_unverified_classes = false;
for (auto& entry : verifier_deps_->dex_deps_) {
has_strings |= !entry.second->strings_.empty();
@@ -371,9 +384,15 @@
has_methods |= !entry.second->direct_methods_.empty();
has_methods |= !entry.second->virtual_methods_.empty();
has_methods |= !entry.second->interface_methods_.empty();
+ has_unverified_classes |= !entry.second->unverified_classes_.empty();
}
- return has_strings && has_assignability && has_classes && has_fields && has_methods;
+ return has_strings &&
+ has_assignability &&
+ has_classes &&
+ has_fields &&
+ has_methods &&
+ has_unverified_classes;
}
std::unique_ptr<verifier::VerifierDeps> verifier_deps_;
@@ -1056,5 +1075,18 @@
ASSERT_TRUE(verifier_deps_->Equals(decoded_deps));
}
+TEST_F(VerifierDepsTest, UnverifiedClasses) {
+ VerifyDexFile();
+ ASSERT_FALSE(HasUnverifiedClass("LMyThread;"));
+ // Test that a class with a soft failure is recorded.
+ ASSERT_TRUE(HasUnverifiedClass("LMain;"));
+ // Test that a class with hard failure is recorded.
+ ASSERT_TRUE(HasUnverifiedClass("LMyVerificationFailure;"));
+ // Test that a class with unresolved super is recorded.
+ ASSERT_FALSE(HasUnverifiedClass("LMyClassWithNoSuper;"));
+ // Test that a class with unresolved super and hard failure is recorded.
+ ASSERT_TRUE(HasUnverifiedClass("LMyClassWithNoSuperButFailures;"));
+}
+
} // namespace verifier
} // namespace art
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 6945eb0..b498573 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -565,7 +565,6 @@
"utils_test.cc",
"verifier/method_verifier_test.cc",
"verifier/reg_type_test.cc",
- "verifier/verifier_deps_test.cc",
"zip_archive_test.cc",
],
shared_libs: [
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f29ed1f..c23b1b1 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1239,6 +1239,20 @@
gc::accounting::HeapBitmap* const live_bitmap_;
};
+// Copies data from one array to another array at the same position
+// if pred returns false. If there is a page of continuous data in
+// the src array for which pred consistently returns true then
+// corresponding page in the dst array will not be touched.
+// This should reduce number of allocated physical pages.
+template <class T, class NullPred>
+static void CopyNonNull(const T* src, size_t count, T* dst, const NullPred& pred) {
+ for (size_t i = 0; i < count; ++i) {
+ if (!pred(src[i])) {
+ dst[i] = src[i];
+ }
+ }
+}
+
bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches(
gc::space::ImageSpace* space,
Handle<mirror::ClassLoader> class_loader,
@@ -1330,7 +1344,12 @@
for (size_t j = 0; kIsDebugBuild && j < num_types; ++j) {
DCHECK(types[j].IsNull());
}
- std::copy_n(image_resolved_types, num_types, types);
+ CopyNonNull(image_resolved_types,
+ num_types,
+ types,
+ [](const GcRoot<mirror::Class>& elem) {
+ return elem.IsNull();
+ });
dex_cache->SetResolvedTypes(types);
}
if (num_methods != 0u) {
@@ -1340,7 +1359,12 @@
for (size_t j = 0; kIsDebugBuild && j < num_methods; ++j) {
DCHECK(methods[j] == nullptr);
}
- std::copy_n(image_resolved_methods, num_methods, methods);
+ CopyNonNull(image_resolved_methods,
+ num_methods,
+ methods,
+ [] (const ArtMethod* method) {
+ return method == nullptr;
+ });
dex_cache->SetResolvedMethods(methods);
}
if (num_fields != 0u) {
@@ -1349,7 +1373,12 @@
for (size_t j = 0; kIsDebugBuild && j < num_fields; ++j) {
DCHECK(fields[j] == nullptr);
}
- std::copy_n(dex_cache->GetResolvedFields(), num_fields, fields);
+ CopyNonNull(dex_cache->GetResolvedFields(),
+ num_fields,
+ fields,
+ [] (const ArtField* field) {
+ return field == nullptr;
+ });
dex_cache->SetResolvedFields(fields);
}
if (num_method_types != 0u) {
@@ -3701,9 +3730,8 @@
return false;
}
-void ClassLinker::VerifyClass(Thread* self,
- Handle<mirror::Class> klass,
- verifier::HardFailLogMode log_level) {
+verifier::MethodVerifier::FailureKind ClassLinker::VerifyClass(
+ Thread* self, Handle<mirror::Class> klass, verifier::HardFailLogMode log_level) {
{
// TODO: assert that the monitor on the Class is held
ObjectLock<mirror::Class> lock(self, klass);
@@ -3724,16 +3752,16 @@
// this class as a parent to another.
if (klass->IsErroneous()) {
ThrowEarlierClassFailure(klass.Get());
- return;
+ return verifier::MethodVerifier::kHardFailure;
}
// Don't attempt to re-verify if already sufficiently verified.
if (klass->IsVerified()) {
EnsureSkipAccessChecksMethods(klass);
- return;
+ return verifier::MethodVerifier::kNoFailure;
}
if (klass->IsCompileTimeVerified() && Runtime::Current()->IsAotCompiler()) {
- return;
+ return verifier::MethodVerifier::kNoFailure;
}
if (klass->GetStatus() == mirror::Class::kStatusResolved) {
@@ -3749,7 +3777,7 @@
if (!Runtime::Current()->IsVerificationEnabled()) {
mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, self);
EnsureSkipAccessChecksMethods(klass);
- return;
+ return verifier::MethodVerifier::kNoFailure;
}
}
@@ -3759,7 +3787,7 @@
// If we have a superclass and we get a hard verification failure we can return immediately.
if (supertype.Get() != nullptr && !AttemptSupertypeVerification(self, klass, supertype)) {
CHECK(self->IsExceptionPending()) << "Verification error should be pending.";
- return;
+ return verifier::MethodVerifier::kHardFailure;
}
// Verify all default super-interfaces.
@@ -3786,7 +3814,7 @@
} else if (UNLIKELY(!AttemptSupertypeVerification(self, klass, iface))) {
// We had a hard failure while verifying this interface. Just return immediately.
CHECK(self->IsExceptionPending()) << "Verification error should be pending.";
- return;
+ return verifier::MethodVerifier::kHardFailure;
} else if (UNLIKELY(!iface->IsVerified())) {
// We softly failed to verify the iface. Stop checking and clean up.
// Put the iface into the supertype handle so we know what caused us to fail.
@@ -3812,8 +3840,8 @@
// oat_file_class_status == mirror::Class::kStatusError => !preverified
DCHECK(!(oat_file_class_status == mirror::Class::kStatusError) || !preverified);
- verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure;
std::string error_msg;
+ verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure;
if (!preverified) {
Runtime* runtime = Runtime::Current();
verifier_failure = verifier::MethodVerifier::VerifyClass(self,
@@ -3886,6 +3914,7 @@
EnsureSkipAccessChecksMethods(klass);
}
}
+ return verifier_failure;
}
void ClassLinker::EnsureSkipAccessChecksMethods(Handle<mirror::Class> klass) {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index f2bf581..e99dfe3 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -36,6 +36,7 @@
#include "jni.h"
#include "mirror/class.h"
#include "object_callbacks.h"
+#include "verifier/method_verifier.h"
#include "verifier/verifier_log_mode.h"
namespace art {
@@ -470,9 +471,10 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
- void VerifyClass(Thread* self,
- Handle<mirror::Class> klass,
- verifier::HardFailLogMode log_level = verifier::HardFailLogMode::kLogNone)
+ verifier::MethodVerifier::FailureKind VerifyClass(
+ Thread* self,
+ Handle<mirror::Class> klass,
+ verifier::HardFailLogMode log_level = verifier::HardFailLogMode::kLogNone)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!dex_lock_);
bool VerifyClassUsingOatFile(const DexFile& dex_file,
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 72722dd..a0d712e 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -33,6 +33,7 @@
#include "stack.h"
#include "unstarted_runtime.h"
#include "verifier/method_verifier.h"
+#include "well_known_classes.h"
namespace art {
namespace interpreter {
@@ -491,7 +492,12 @@
Runtime::Current()->AbortTransactionAndThrowAbortError(self, abort_msg);
}
-// Separate declaration is required solely for the attributes.
+// START DECLARATIONS :
+//
+// These additional declarations are required because clang complains
+// about ALWAYS_INLINE (-Werror, -Wgcc-compat) in definitions.
+//
+
template <bool is_range, bool do_assignability_check>
REQUIRES_SHARED(Locks::mutator_lock_)
static inline bool DoCallCommon(ArtMethod* called_method,
@@ -502,7 +508,6 @@
uint32_t (&arg)[Instruction::kMaxVarArgRegs],
uint32_t vregC) ALWAYS_INLINE;
-// Separate declaration is required solely for the attributes.
template <bool is_range> REQUIRES_SHARED(Locks::mutator_lock_)
static inline bool DoCallPolymorphic(ArtMethod* called_method,
Handle<mirror::MethodType> callsite_type,
@@ -513,6 +518,33 @@
uint32_t (&arg)[Instruction::kMaxVarArgRegs],
uint32_t vregC) ALWAYS_INLINE;
+REQUIRES_SHARED(Locks::mutator_lock_)
+static inline bool DoCallTransform(ArtMethod* called_method,
+ Handle<mirror::MethodType> callsite_type,
+ Thread* self,
+ ShadowFrame& shadow_frame,
+ Handle<mirror::MethodHandleImpl> receiver,
+ JValue* result) ALWAYS_INLINE;
+
+REQUIRES_SHARED(Locks::mutator_lock_)
+inline void PerformCall(Thread* self,
+ const DexFile::CodeItem* code_item,
+ ArtMethod* caller_method,
+ const size_t first_dest_reg,
+ ShadowFrame* callee_frame,
+ JValue* result) ALWAYS_INLINE;
+
+template <bool is_range>
+REQUIRES_SHARED(Locks::mutator_lock_)
+inline void CopyRegisters(ShadowFrame& caller_frame,
+ ShadowFrame* callee_frame,
+ const uint32_t (&arg)[Instruction::kMaxVarArgRegs],
+ const size_t first_src_reg,
+ const size_t first_dest_reg,
+ const size_t num_regs) ALWAYS_INLINE;
+
+// END DECLARATIONS.
+
void ArtInterpreterToCompiledCodeBridge(Thread* self,
ArtMethod* caller,
const DexFile::CodeItem* code_item,
@@ -635,12 +667,6 @@
CHECK(called_method != nullptr);
CHECK(handle_type.Get() != nullptr);
- // We now have to massage the number of inputs to the target function.
- // It's always one less than the number of inputs to the signature polymorphic
- // invoke, the first input being a reference to the MethodHandle itself.
- const uint16_t number_of_inputs =
- ((is_range) ? inst->VRegA_4rcc(inst_data) : inst->VRegA_45cc(inst_data)) - 1;
-
uint32_t arg[Instruction::kMaxVarArgRegs] = {};
uint32_t receiver_vregC = 0;
if (is_range) {
@@ -702,18 +728,22 @@
CHECK(called_method != nullptr);
}
- // NOTE: handle_kind == kInvokeStatic needs no special treatment here. We
- // can directly make the call. handle_kind == kInvokeSuper doesn't have any
- // particular use and can probably be dropped.
-
- if (callsite_type->IsExactMatch(handle_type.Get())) {
- return DoCallCommon<is_range, do_access_check>(
- called_method, self, shadow_frame, result, number_of_inputs,
- arg, receiver_vregC);
+ if (handle_kind == kInvokeTransform) {
+ return DoCallTransform(called_method,
+ callsite_type,
+ self,
+ shadow_frame,
+ method_handle /* receiver */,
+ result);
} else {
- return DoCallPolymorphic<is_range>(
- called_method, callsite_type, handle_type, self, shadow_frame,
- result, arg, receiver_vregC);
+ return DoCallPolymorphic<is_range>(called_method,
+ callsite_type,
+ handle_type,
+ self,
+ shadow_frame,
+ result,
+ arg,
+ receiver_vregC);
}
} else {
// TODO(narayan): Implement field getters and setters.
@@ -749,6 +779,64 @@
return num_ins;
}
+
+inline void PerformCall(Thread* self,
+ const DexFile::CodeItem* code_item,
+ ArtMethod* caller_method,
+ const size_t first_dest_reg,
+ ShadowFrame* callee_frame,
+ JValue* result) {
+ if (LIKELY(Runtime::Current()->IsStarted())) {
+ ArtMethod* target = callee_frame->GetMethod();
+ if (ClassLinker::ShouldUseInterpreterEntrypoint(
+ target,
+ target->GetEntryPointFromQuickCompiledCode())) {
+ ArtInterpreterToInterpreterBridge(self, code_item, callee_frame, result);
+ } else {
+ ArtInterpreterToCompiledCodeBridge(
+ self, caller_method, code_item, callee_frame, result);
+ }
+ } else {
+ UnstartedRuntime::Invoke(self, code_item, callee_frame, result, first_dest_reg);
+ }
+}
+
+template <bool is_range>
+inline void CopyRegisters(ShadowFrame& caller_frame,
+ ShadowFrame* callee_frame,
+ const uint32_t (&arg)[Instruction::kMaxVarArgRegs],
+ const size_t first_src_reg,
+ const size_t first_dest_reg,
+ const size_t num_regs) {
+ if (is_range) {
+ const size_t dest_reg_bound = first_dest_reg + num_regs;
+ for (size_t src_reg = first_src_reg, dest_reg = first_dest_reg; dest_reg < dest_reg_bound;
+ ++dest_reg, ++src_reg) {
+ AssignRegister(callee_frame, caller_frame, dest_reg, src_reg);
+ }
+ } else {
+ DCHECK_LE(num_regs, arraysize(arg));
+
+ for (size_t arg_index = 0; arg_index < num_regs; ++arg_index) {
+ AssignRegister(callee_frame, caller_frame, first_dest_reg + arg_index, arg[arg_index]);
+ }
+ }
+}
+
+// Returns true iff. the callsite type for a polymorphic invoke is transformer
+// like, i.e that it has a single input argument whose type is
+// dalvik.system.EmulatedStackFrame.
+static inline bool IsCallerTransformer(Handle<mirror::MethodType> callsite_type)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::ObjectArray<mirror::Class>> param_types(callsite_type->GetPTypes());
+ if (param_types->GetLength() == 1) {
+ ObjPtr<mirror::Class> param(param_types->GetWithoutChecks(0));
+ return param == WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_EmulatedStackFrame);
+ }
+
+ return false;
+}
+
template <bool is_range>
static inline bool DoCallPolymorphic(ArtMethod* called_method,
Handle<mirror::MethodType> callsite_type,
@@ -757,7 +845,7 @@
ShadowFrame& shadow_frame,
JValue* result,
uint32_t (&arg)[Instruction::kMaxVarArgRegs],
- uint32_t vregC) {
+ uint32_t first_src_reg) {
// TODO(narayan): Wire in the String.init hacks.
// Compute method information.
@@ -770,16 +858,18 @@
// some transformations (such as boxing a long -> Long or wideining an
// int -> long will change that number.
uint16_t num_regs;
+ size_t num_input_regs;
size_t first_dest_reg;
if (LIKELY(code_item != nullptr)) {
num_regs = code_item->registers_size_;
first_dest_reg = num_regs - code_item->ins_size_;
+ num_input_regs = code_item->ins_size_;
// Parameter registers go at the end of the shadow frame.
DCHECK_NE(first_dest_reg, (size_t)-1);
} else {
// No local regs for proxy and native methods.
DCHECK(called_method->IsNative() || called_method->IsProxyMethod());
- num_regs = GetInsForProxyOrNativeMethod(called_method);
+ num_regs = num_input_regs = GetInsForProxyOrNativeMethod(called_method);
first_dest_reg = 0;
}
@@ -793,35 +883,109 @@
{
ScopedStackedShadowFramePusher pusher(
self, new_shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction);
- if (!PerformArgumentConversions<is_range>(self, callsite_type, target_type,
- shadow_frame, vregC, first_dest_reg,
- arg, new_shadow_frame)) {
- DCHECK(self->IsExceptionPending());
- result->SetL(0);
- return false;
+ if (callsite_type->IsExactMatch(target_type.Get())) {
+ // This is an exact invoke, we can take the fast path of just copying all
+ // registers without performing any argument conversions.
+ CopyRegisters<is_range>(shadow_frame,
+ new_shadow_frame,
+ arg,
+ first_src_reg,
+ first_dest_reg,
+ num_input_regs);
+ } else {
+ // This includes the case where we're entering this invoke-polymorphic
+ // from a transformer method. In that case, the callsite_type will contain
+ // a single argument of type dalvik.system.EmulatedStackFrame. In that
+ // case, we'll have to unmarshal the EmulatedStackFrame into the
+ // new_shadow_frame and perform argument conversions on it.
+ if (IsCallerTransformer(callsite_type)) {
+ // The emulated stack frame will be the first ahnd only argument
+ // when we're coming through from a transformer.
+ //
+ // TODO(narayan): This should be a mirror::EmulatedStackFrame after that
+ // type is introduced.
+ ObjPtr<mirror::Object> emulated_stack_frame(
+ shadow_frame.GetVRegReference(first_src_reg));
+ if (!ConvertAndCopyArgumentsFromEmulatedStackFrame<is_range>(self,
+ emulated_stack_frame,
+ target_type,
+ first_dest_reg,
+ new_shadow_frame)) {
+ DCHECK(self->IsExceptionPending());
+ result->SetL(0);
+ return false;
+ }
+ } else if (!ConvertAndCopyArgumentsFromCallerFrame<is_range>(self,
+ callsite_type,
+ target_type,
+ shadow_frame,
+ first_src_reg,
+ first_dest_reg,
+ arg,
+ new_shadow_frame)) {
+ DCHECK(self->IsExceptionPending());
+ result->SetL(0);
+ return false;
+ }
}
}
- // Do the call now.
- if (LIKELY(Runtime::Current()->IsStarted())) {
- ArtMethod* target = new_shadow_frame->GetMethod();
- if (ClassLinker::ShouldUseInterpreterEntrypoint(
- target,
- target->GetEntryPointFromQuickCompiledCode())) {
- ArtInterpreterToInterpreterBridge(self, code_item, new_shadow_frame, result);
- } else {
- ArtInterpreterToCompiledCodeBridge(
- self, shadow_frame.GetMethod(), code_item, new_shadow_frame, result);
- }
- } else {
- UnstartedRuntime::Invoke(self, code_item, new_shadow_frame, result, first_dest_reg);
- }
+ PerformCall(self, code_item, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, result);
// TODO(narayan): Perform return value conversions.
return !self->IsExceptionPending();
}
+static inline bool DoCallTransform(ArtMethod* called_method,
+ Handle<mirror::MethodType> callsite_type,
+ Thread* self,
+ ShadowFrame& shadow_frame,
+ Handle<mirror::MethodHandleImpl> receiver,
+ JValue* result) {
+ // This can be fixed, because the method we're calling here
+ // (MethodHandle.transformInternal) doesn't have any locals and the signature
+ // is known :
+ //
+ // private MethodHandle.transformInternal(EmulatedStackFrame sf);
+ //
+ // This means we need only two vregs :
+ // - One for the receiver object.
+ // - One for the only method argument (an EmulatedStackFrame).
+ static constexpr size_t kNumRegsForTransform = 2;
+
+ const DexFile::CodeItem* code_item = called_method->GetCodeItem();
+ DCHECK(code_item != nullptr);
+ DCHECK_EQ(kNumRegsForTransform, code_item->registers_size_);
+ DCHECK_EQ(kNumRegsForTransform, code_item->ins_size_);
+
+ ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
+ CREATE_SHADOW_FRAME(kNumRegsForTransform, &shadow_frame, called_method, /* dex pc */ 0);
+ ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get();
+
+ // TODO(narayan): Perform argument conversions first (if this is an inexact invoke), and
+ // then construct an argument list object that's passed through to the
+ // method. Note that the ArgumentList reference is currently a nullptr.
+ //
+ // NOTE(narayan): If the caller is a transformer method (i.e, there is only
+ // one argument and its type is EmulatedStackFrame), we can directly pass that
+ // through without having to do any additional work.
+ UNUSED(callsite_type);
+
+ new_shadow_frame->SetVRegReference(0, receiver.Get());
+ // TODO(narayan): This is the EmulatedStackFrame, currently nullptr.
+ new_shadow_frame->SetVRegReference(1, nullptr);
+
+ PerformCall(self,
+ code_item,
+ shadow_frame.GetMethod(),
+ 0 /* first dest reg */,
+ new_shadow_frame,
+ result);
+
+ return !self->IsExceptionPending();
+}
+
template <bool is_range,
bool do_assignability_check>
static inline bool DoCallCommon(ArtMethod* called_method,
@@ -982,40 +1146,20 @@
}
}
} else {
- size_t arg_index = 0;
-
- // Fast path: no extra checks.
if (is_range) {
- uint16_t first_src_reg = vregC;
-
- for (size_t src_reg = first_src_reg, dest_reg = first_dest_reg; dest_reg < num_regs;
- ++dest_reg, ++src_reg) {
- AssignRegister(new_shadow_frame, shadow_frame, dest_reg, src_reg);
- }
- } else {
- DCHECK_LE(number_of_inputs, arraysize(arg));
-
- for (; arg_index < number_of_inputs; ++arg_index) {
- AssignRegister(new_shadow_frame, shadow_frame, first_dest_reg + arg_index, arg[arg_index]);
- }
+ DCHECK_EQ(num_regs, first_dest_reg + number_of_inputs);
}
+
+ CopyRegisters<is_range>(shadow_frame,
+ new_shadow_frame,
+ arg,
+ vregC,
+ first_dest_reg,
+ number_of_inputs);
self->EndAssertNoThreadSuspension(old_cause);
}
- // Do the call now.
- if (LIKELY(Runtime::Current()->IsStarted())) {
- ArtMethod* target = new_shadow_frame->GetMethod();
- if (ClassLinker::ShouldUseInterpreterEntrypoint(
- target,
- target->GetEntryPointFromQuickCompiledCode())) {
- ArtInterpreterToInterpreterBridge(self, code_item, new_shadow_frame, result);
- } else {
- ArtInterpreterToCompiledCodeBridge(
- self, shadow_frame.GetMethod(), code_item, new_shadow_frame, result);
- }
- } else {
- UnstartedRuntime::Invoke(self, code_item, new_shadow_frame, result, first_dest_reg);
- }
+ PerformCall(self, code_item, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, result);
if (string_init && !self->IsExceptionPending()) {
SetStringInitValueToAllAliases(&shadow_frame, string_init_vreg_this, *result);
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index e4c67e0..1ec59b3 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -318,11 +318,18 @@
debug_friendly_name += name;
fd.Reset(ashmem_create_region(debug_friendly_name.c_str(), page_aligned_byte_count),
/* check_usage */ false);
+
if (fd.Fd() == -1) {
- *error_msg = StringPrintf("ashmem_create_region failed for '%s': %s", name, strerror(errno));
- return nullptr;
+ // We failed to create the ashmem region. Print a warning, but continue
+ // anyway by creating a true anonymous mmap with an fd of -1. It is
+ // better to use an unlabelled anonymous map than to fail to create a
+ // map at all.
+ PLOG(WARNING) << "ashmem_create_region failed for '" << name << "'";
+ } else {
+ // We succeeded in creating the ashmem region. Use the created ashmem
+ // region as backing for the mmap.
+ flags &= ~MAP_ANONYMOUS;
}
- flags &= ~MAP_ANONYMOUS;
}
// We need to store and potentially set an error number for pretty printing of errors
@@ -354,7 +361,6 @@
}
return nullptr;
}
- std::ostringstream check_map_request_error_msg;
if (!CheckMapRequest(expected_ptr, actual, page_aligned_byte_count, error_msg)) {
return nullptr;
}
@@ -441,7 +447,6 @@
}
return nullptr;
}
- std::ostringstream check_map_request_error_msg;
if (!CheckMapRequest(expected_ptr, actual, page_aligned_byte_count, error_msg)) {
return nullptr;
}
diff --git a/runtime/method_handles-inl.h b/runtime/method_handles-inl.h
index 7a77bda..b488133 100644
--- a/runtime/method_handles-inl.h
+++ b/runtime/method_handles-inl.h
@@ -162,14 +162,14 @@
}
template <bool is_range>
-bool PerformArgumentConversions(Thread* self,
- Handle<mirror::MethodType> callsite_type,
- Handle<mirror::MethodType> callee_type,
- const ShadowFrame& caller_frame,
- uint32_t first_src_reg,
- uint32_t first_dest_reg,
- const uint32_t (&arg)[Instruction::kMaxVarArgRegs],
- ShadowFrame* callee_frame) {
+bool ConvertAndCopyArgumentsFromCallerFrame(Thread* self,
+ Handle<mirror::MethodType> callsite_type,
+ Handle<mirror::MethodType> callee_type,
+ const ShadowFrame& caller_frame,
+ uint32_t first_src_reg,
+ uint32_t first_dest_reg,
+ const uint32_t (&arg)[Instruction::kMaxVarArgRegs],
+ ShadowFrame* callee_frame) {
StackHandleScope<4> hs(self);
Handle<mirror::ObjectArray<mirror::Class>> from_types(hs.NewHandle(callsite_type->GetPTypes()));
Handle<mirror::ObjectArray<mirror::Class>> to_types(hs.NewHandle(callee_type->GetPTypes()));
@@ -244,6 +244,25 @@
return true;
}
+// Similar to |ConvertAndCopyArgumentsFromCallerFrame|, except that the
+// arguments are copied from an |EmulatedStackFrame|.
+template <bool is_range>
+bool ConvertAndCopyArgumentsFromEmulatedStackFrame(Thread* self,
+ ObjPtr<mirror::Object> emulated_stack_frame,
+ Handle<mirror::MethodType> callee_type,
+ const uint32_t first_dest_reg,
+ ShadowFrame* callee_frame) {
+ UNUSED(self);
+ UNUSED(emulated_stack_frame);
+ UNUSED(callee_type);
+ UNUSED(first_dest_reg);
+ UNUSED(callee_frame);
+
+ UNIMPLEMENTED(FATAL) << "ConvertAndCopyArgumentsFromEmulatedStackFrame is unimplemented";
+ return false;
+}
+
+
} // namespace art
#endif // ART_RUNTIME_METHOD_HANDLES_INL_H_
diff --git a/runtime/method_handles.h b/runtime/method_handles.h
index 26a29b3..5175dce 100644
--- a/runtime/method_handles.h
+++ b/runtime/method_handles.h
@@ -42,12 +42,13 @@
kInvokeDirect,
kInvokeStatic,
kInvokeInterface,
+ kInvokeTransform,
kInstanceGet,
kInstancePut,
kStaticGet,
kStaticPut,
kLastValidKind = kStaticPut,
- kLastInvokeKind = kInvokeInterface
+ kLastInvokeKind = kInvokeTransform
};
// Whether the given method handle kind is some variant of an invoke.
@@ -69,14 +70,24 @@
// boxing and unboxing. Returns true on success, on false on failure. A
// pending exception will always be set on failure.
template <bool is_range> REQUIRES_SHARED(Locks::mutator_lock_)
-bool PerformArgumentConversions(Thread* self,
- Handle<mirror::MethodType> callsite_type,
- Handle<mirror::MethodType> callee_type,
- const ShadowFrame& caller_frame,
- uint32_t first_src_reg,
- uint32_t first_dest_reg,
- const uint32_t (&arg)[Instruction::kMaxVarArgRegs],
- ShadowFrame* callee_frame);
+bool ConvertAndCopyArgumentsFromCallerFrame(Thread* self,
+ Handle<mirror::MethodType> callsite_type,
+ Handle<mirror::MethodType> callee_type,
+ const ShadowFrame& caller_frame,
+ uint32_t first_src_reg,
+ uint32_t first_dest_reg,
+ const uint32_t (&arg)[Instruction::kMaxVarArgRegs],
+ ShadowFrame* callee_frame);
+
+// Similar to |ConvertAndCopyArgumentsFromCallerFrame|, except that the
+// arguments are copied from an |EmulatedStackFrame|.
+template <bool is_range> REQUIRES_SHARED(Locks::mutator_lock_)
+bool ConvertAndCopyArgumentsFromEmulatedStackFrame(Thread* self,
+ ObjPtr<mirror::Object> emulated_stack_frame,
+ Handle<mirror::MethodType> callee_type,
+ const uint32_t first_dest_reg,
+ ShadowFrame* callee_frame);
+
} // namespace art
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index 3c7fb7a..4d1e337 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -280,6 +280,22 @@
return callbacks->GetVerifierDeps();
}
+void VerifierDeps::MaybeRecordVerificationStatus(const DexFile& dex_file,
+ uint16_t type_idx,
+ MethodVerifier::FailureKind failure_kind) {
+ if (failure_kind == MethodVerifier::kNoFailure) {
+ // We only record classes that did not fully verify at compile time.
+ return;
+ }
+
+ VerifierDeps* singleton = GetVerifierDepsSingleton();
+ if (singleton != nullptr) {
+ DexFileDeps* dex_deps = singleton->GetDexFileDeps(dex_file);
+ MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
+ dex_deps->unverified_classes_.push_back(type_idx);
+ }
+}
+
void VerifierDeps::MaybeRecordClassResolution(const DexFile& dex_file,
uint16_t type_idx,
mirror::Class* klass) {
@@ -360,6 +376,14 @@
}
}
+static inline void EncodeUint16Vector(std::vector<uint8_t>* out,
+ const std::vector<uint16_t>& vector) {
+ EncodeUnsignedLeb128(out, vector.size());
+ for (uint16_t entry : vector) {
+ EncodeUnsignedLeb128(out, entry);
+ }
+}
+
template<typename T>
static inline void DecodeSet(const uint8_t** in, const uint8_t* end, std::set<T>* set) {
DCHECK(set->empty());
@@ -371,6 +395,17 @@
}
}
+static inline void DecodeUint16Vector(const uint8_t** in,
+ const uint8_t* end,
+ std::vector<uint16_t>* vector) {
+ DCHECK(vector->empty());
+ size_t num_entries = DecodeUint32WithOverflowCheck(in, end);
+ vector->reserve(num_entries);
+ for (size_t i = 0; i < num_entries; ++i) {
+ vector->push_back(dchecked_integral_cast<uint16_t>(DecodeUint32WithOverflowCheck(in, end)));
+ }
+}
+
static inline void EncodeStringVector(std::vector<uint8_t>* out,
const std::vector<std::string>& strings) {
EncodeUnsignedLeb128(out, strings.size());
@@ -407,6 +442,7 @@
EncodeSet(buffer, entry.second->direct_methods_);
EncodeSet(buffer, entry.second->virtual_methods_);
EncodeSet(buffer, entry.second->interface_methods_);
+ EncodeUint16Vector(buffer, entry.second->unverified_classes_);
}
}
@@ -423,6 +459,7 @@
DecodeSet(&data_start, data_end, &entry.second->direct_methods_);
DecodeSet(&data_start, data_end, &entry.second->virtual_methods_);
DecodeSet(&data_start, data_end, &entry.second->interface_methods_);
+ DecodeUint16Vector(&data_start, data_end, &entry.second->unverified_classes_);
}
CHECK_LE(data_start, data_end);
}
@@ -463,7 +500,8 @@
(fields_ == rhs.fields_) &&
(direct_methods_ == rhs.direct_methods_) &&
(virtual_methods_ == rhs.virtual_methods_) &&
- (interface_methods_ == rhs.interface_methods_);
+ (interface_methods_ == rhs.interface_methods_) &&
+ (unverified_classes_ == rhs.unverified_classes_);
}
} // namespace verifier
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index 3223f6f..9d2622d 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -26,6 +26,7 @@
#include "base/array_ref.h"
#include "base/mutex.h"
#include "method_resolution_kind.h"
+#include "method_verifier.h" // For MethodVerifier::FailureKind.
#include "obj_ptr.h"
#include "os.h"
@@ -49,6 +50,12 @@
explicit VerifierDeps(const std::vector<const DexFile*>& dex_files)
REQUIRES(!Locks::verifier_deps_lock_);
+ // Record the verification status of the class at `type_idx`.
+ static void MaybeRecordVerificationStatus(const DexFile& dex_file,
+ uint16_t type_idx,
+ MethodVerifier::FailureKind failure_kind)
+ REQUIRES(!Locks::verifier_deps_lock_);
+
// Record the outcome `klass` of resolving type `type_idx` from `dex_file`.
// If `klass` is null, the class is assumed unresolved.
static void MaybeRecordClassResolution(const DexFile& dex_file,
@@ -136,7 +143,7 @@
};
using TypeAssignabilityBase = std::tuple<uint32_t, uint32_t>;
- struct TypeAssignability : public std::tuple<uint32_t, uint32_t> {
+ struct TypeAssignability : public TypeAssignabilityBase {
TypeAssignability() = default;
TypeAssignability(const TypeAssignability&) = default;
TypeAssignability(uint32_t destination_idx, uint32_t source_idx)
@@ -165,6 +172,9 @@
std::set<MethodResolution> virtual_methods_;
std::set<MethodResolution> interface_methods_;
+ // List of classes that were not fully verified in that dex file.
+ std::vector<uint16_t> unverified_classes_;
+
bool Equals(const DexFileDeps& rhs) const;
};
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 78fc53a..153c7ef 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -37,6 +37,7 @@
jclass WellKnownClasses::dalvik_system_DexFile;
jclass WellKnownClasses::dalvik_system_DexPathList;
jclass WellKnownClasses::dalvik_system_DexPathList__Element;
+jclass WellKnownClasses::dalvik_system_EmulatedStackFrame;
jclass WellKnownClasses::dalvik_system_PathClassLoader;
jclass WellKnownClasses::dalvik_system_VMRuntime;
jclass WellKnownClasses::java_lang_annotation_Annotation__array;
@@ -266,6 +267,7 @@
dalvik_system_DexFile = CacheClass(env, "dalvik/system/DexFile");
dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
dalvik_system_DexPathList__Element = CacheClass(env, "dalvik/system/DexPathList$Element");
+ dalvik_system_EmulatedStackFrame = CacheClass(env, "dalvik/system/EmulatedStackFrame");
dalvik_system_PathClassLoader = CacheClass(env, "dalvik/system/PathClassLoader");
dalvik_system_VMRuntime = CacheClass(env, "dalvik/system/VMRuntime");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 248ba7f..2fb5bb4 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -50,6 +50,7 @@
static jclass dalvik_system_DexFile;
static jclass dalvik_system_DexPathList;
static jclass dalvik_system_DexPathList__Element;
+ static jclass dalvik_system_EmulatedStackFrame;
static jclass dalvik_system_PathClassLoader;
static jclass dalvik_system_VMRuntime;
static jclass java_lang_annotation_Annotation__array;
diff --git a/test/151-OpenFileLimit/expected.txt b/test/151-OpenFileLimit/expected.txt
index 971e472..6bc45ef 100644
--- a/test/151-OpenFileLimit/expected.txt
+++ b/test/151-OpenFileLimit/expected.txt
@@ -1,3 +1,3 @@
Message includes "Too many open files"
-Message includes "Too many open files"
+thread run.
done.
diff --git a/test/151-OpenFileLimit/info.txt b/test/151-OpenFileLimit/info.txt
index 56ed396..9af393d 100644
--- a/test/151-OpenFileLimit/info.txt
+++ b/test/151-OpenFileLimit/info.txt
@@ -1,3 +1,2 @@
-This test verifies the exception message is informative for failure to launch
-a thread due to the number of available file descriptors in the process being
-exceeded.
+This test verifies that running out of file descriptors in the process doesn't
+prevent us from launching a new thread.
diff --git a/test/151-OpenFileLimit/src/Main.java b/test/151-OpenFileLimit/src/Main.java
index 01a9a4e..9fe47c8 100644
--- a/test/151-OpenFileLimit/src/Main.java
+++ b/test/151-OpenFileLimit/src/Main.java
@@ -52,11 +52,7 @@
thread.start();
thread.join();
} catch (Throwable e) {
- if (e.getMessage().contains("Too many open files")) {
- System.out.println("Message includes \"Too many open files\"");
- } else {
- System.out.println(e.getMessage());
- }
+ System.out.println(e.getMessage());
}
for (int i = 0; i < files.size(); i++) {
diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java
index 2802dfa..badea53 100644
--- a/test/956-methodhandles/src/Main.java
+++ b/test/956-methodhandles/src/Main.java
@@ -57,6 +57,8 @@
public static void main(String[] args) throws Throwable {
testfindSpecial_invokeSuperBehaviour();
testfindSpecial_invokeDirectBehaviour();
+
+ testThrowException();
}
public static void testfindSpecial_invokeSuperBehaviour() throws Throwable {
@@ -131,6 +133,21 @@
} catch (IllegalAccessException expected) {
}
}
+
+ public static void testThrowException() throws Throwable {
+ MethodHandle handle = MethodHandles.throwException(String.class,
+ IllegalArgumentException.class);
+ if (handle.type().returnType() != String.class) {
+ System.out.println("Unexpected return type for handle: " + handle
+ + " [ " + handle.type() + "]");
+ }
+
+ try {
+ handle.invoke();
+ System.out.println("Expected an exception of type: java.lang.IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
}
diff --git a/test/Android.arm_vixl.mk b/test/Android.arm_vixl.mk
index 0bbcb64..5e1b953 100644
--- a/test/Android.arm_vixl.mk
+++ b/test/Android.arm_vixl.mk
@@ -52,7 +52,6 @@
037-inherit \
041-narrowing \
042-new-instance \
- 043-privates \
044-proxy \
045-reflect-array \
046-reflect \
@@ -62,7 +61,6 @@
050-sync-test \
051-thread \
052-verifier-fun \
- 053-wait-some \
054-uncaught \
058-enum-order \
059-finalizer-throw \
@@ -137,9 +135,6 @@
201-built-in-except-detail-messages \
302-float-conversion \
304-method-tracing \
- 403-optimizing-long \
- 404-optimizing-allocator \
- 405-optimizing-long-allocator \
406-fields \
407-arrays \
410-floats \
@@ -153,7 +148,6 @@
417-optimizing-arith-div \
419-long-parameter \
421-exceptions \
- 421-large-frame \
422-instanceof \
422-type-conversion \
423-invoke-interface \
@@ -180,7 +174,6 @@
442-checker-constant-folding \
444-checker-nce \
445-checker-licm \
- 446-checker-inliner2 \
447-checker-inliner3 \
448-multiple-returns \
449-checker-bce \
@@ -197,33 +190,24 @@
459-dead-phi \
460-multiple-returns3 \
461-get-reference-vreg \
- 462-checker-inlining-dex-files \
463-checker-boolean-simplifier \
- 464-checker-inline-sharpen-calls \
466-get-live-vreg \
467-regalloc-pair \
468-checker-bool-simplif-regression \
469-condition-materialization \
- 470-huge-method \
471-deopt-environment \
472-type-propagation \
- 473-checker-inliner-constants \
474-checker-boolean-input \
474-fp-sub-neg \
475-regression-inliner-ids \
- 476-checker-ctor-memory-barrier \
477-checker-bound-type \
477-long-2-float-convers-precision \
478-checker-clinit-check-pruning \
- 478-checker-inliner-nested-loop \
- 480-checker-dead-blocks \
- 482-checker-loop-back-edge-use \
483-dce-block \
484-checker-register-hints \
485-checker-dce-loop-update \
485-checker-dce-switch \
486-checker-must-do-null-check \
- 487-checker-inline-calls \
488-checker-inline-recursive-calls \
490-checker-inline \
491-current-method \
@@ -273,7 +257,6 @@
535-regression-const-val \
536-checker-intrinsic-optimization \
536-checker-needs-access-check \
- 537-checker-arraycopy \
537-checker-inline-and-unverified \
537-checker-jump-over-jump \
538-checker-embed-constants \
@@ -285,10 +268,8 @@
543-env-long-ref \
545-tracing-and-jit \
546-regression-simplify-catch \
- 548-checker-inlining-and-dce \
550-checker-multiply-accumulate \
550-checker-regression-wide-store \
- 551-checker-clinit \
551-checker-shifter-operand \
551-implicit-null-checks \
551-invoke-super \
@@ -311,17 +292,13 @@
562-no-intermediate \
563-checker-fakestring \
564-checker-bitcount \
- 564-checker-inline-loop \
564-checker-irreducible-loop \
564-checker-negbitwise \
565-checker-condition-liveness \
565-checker-doublenegbitwise \
565-checker-irreducible-loop \
565-checker-rotate \
- 566-checker-codegen-select \
- 566-checker-signum \
566-polymorphic-inlining \
- 567-checker-compare \
568-checker-onebit \
570-checker-osr \
570-checker-select \
@@ -346,7 +323,6 @@
590-checker-arr-set-null-regression \
591-new-instance-string \
592-checker-regression-bool-input \
- 593-checker-boolean-2-integral-conv \
593-checker-long-2-float-regression \
593-checker-shift-and-simplifier \
594-checker-array-alias \
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 9b456c1..1e3a997 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -232,11 +232,9 @@
# Disable 149-suspend-all-stress, its output is flaky (b/28988206).
-# Disable 151-OpenFileLimit (b/32302133)
# Disable 577-profile-foreign-dex (b/27454772).
TEST_ART_BROKEN_ALL_TARGET_TESTS := \
149-suspend-all-stress \
- 151-OpenFileLimit \
577-profile-foreign-dex \
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
diff --git a/test/VerifierDeps/MyClassWithNoSuper.smali b/test/VerifierDeps/MyClassWithNoSuper.smali
new file mode 100644
index 0000000..d8509bc
--- /dev/null
+++ b/test/VerifierDeps/MyClassWithNoSuper.smali
@@ -0,0 +1,16 @@
+# Copyright (C) 2016 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.
+
+.class public LMyClassWithNoSuper;
+.super LNoSuper;
diff --git a/test/VerifierDeps/MyClassWithNoSuperButFailures.smali b/test/VerifierDeps/MyClassWithNoSuperButFailures.smali
new file mode 100644
index 0000000..1dbe9d1
--- /dev/null
+++ b/test/VerifierDeps/MyClassWithNoSuperButFailures.smali
@@ -0,0 +1,21 @@
+# Copyright (C) 2016 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.
+
+.class public LMyClassWithNoSuperButFailures;
+.super LNoSuper;
+
+.method public final foo()I
+ .registers 1
+ return-void
+.end method
diff --git a/test/VerifierDeps/MyVerificationFailure.smali b/test/VerifierDeps/MyVerificationFailure.smali
new file mode 100644
index 0000000..187b1ad
--- /dev/null
+++ b/test/VerifierDeps/MyVerificationFailure.smali
@@ -0,0 +1,21 @@
+# Copyright (C) 2016 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.
+
+.class public LMyVerificationFailure;
+.super Ljava/lang/Object;
+
+.method public final foo()I
+ .registers 1
+ return-void
+.end method