diff options
26 files changed, 727 insertions, 200 deletions
diff --git a/build/Android.bp b/build/Android.bp index 9156027dee..cd9d74a934 100644 --- a/build/Android.bp +++ b/build/Android.bp @@ -145,6 +145,10 @@ art_global_defaults { "external/vixl/src", "external/zlib", ], + + tidy_checks: [ + "-google-default-arguments", + ], } art_debug_defaults { diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 57823c9684..7cab97d2e5 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -638,6 +638,11 @@ class ReadBarrierMarkSlowPathARM : public SlowPathCodeARM { (instruction_->IsInvokeStaticOrDirect() && instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier marking slow path: " << instruction_->DebugName(); + // The read barrier instrumentation of object ArrayGet + // instructions does not support the HIntermediateAddress + // instruction. + DCHECK(!(instruction_->IsArrayGet() && + instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress())); __ Bind(GetEntryLabel()); // No need to save live registers; it's taken care of by the @@ -894,6 +899,11 @@ class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCodeARM { (instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified()) << "Unexpected instruction in read barrier for heap reference slow path: " << instruction_->DebugName(); + // The read barrier instrumentation of object ArrayGet + // instructions does not support the HIntermediateAddress + // instruction. + DCHECK(!(instruction_->IsArrayGet() && + instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress())); __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); @@ -4841,8 +4851,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { instruction->IsStringCharAt(); HInstruction* array_instr = instruction->GetArray(); bool has_intermediate_address = array_instr->IsIntermediateAddress(); - // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. - DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier)); switch (type) { case Primitive::kPrimBoolean: @@ -4915,6 +4923,11 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimNot: { + // The read barrier instrumentation of object ArrayGet + // instructions does not support the HIntermediateAddress + // instruction. + DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier)); + static_assert( sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); @@ -5055,8 +5068,6 @@ void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { Location value_loc = locations->InAt(2); HInstruction* array_instr = instruction->GetArray(); bool has_intermediate_address = array_instr->IsIntermediateAddress(); - // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. - DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier)); switch (value_type) { case Primitive::kPrimBoolean: @@ -5306,8 +5317,6 @@ void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) { } void LocationsBuilderARM::VisitIntermediateAddress(HIntermediateAddress* instruction) { - // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. - DCHECK(!kEmitCompilerReadBarrier); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); @@ -5322,9 +5331,6 @@ void InstructionCodeGeneratorARM::VisitIntermediateAddress(HIntermediateAddress* Location first = locations->InAt(0); Location second = locations->InAt(1); - // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. - DCHECK(!kEmitCompilerReadBarrier); - if (second.IsRegister()) { __ add(out.AsRegister<Register>(), first.AsRegister<Register>(), diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index b411a431cc..d868984387 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -626,6 +626,11 @@ class ReadBarrierMarkSlowPathARM64 : public SlowPathCodeARM64 { (instruction_->IsInvokeStaticOrDirect() && instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier marking slow path: " << instruction_->DebugName(); + // The read barrier instrumentation of object ArrayGet + // instructions does not support the HIntermediateAddress + // instruction. + DCHECK(!(instruction_->IsArrayGet() && + instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress())); __ Bind(GetEntryLabel()); // No need to save live registers; it's taken care of by the @@ -876,7 +881,9 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { (instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified()) << "Unexpected instruction in read barrier for heap reference slow path: " << instruction_->DebugName(); - // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. + // The read barrier instrumentation of object ArrayGet + // instructions does not support the HIntermediateAddress + // instruction. DCHECK(!(instruction_->IsArrayGet() && instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress())); @@ -2192,8 +2199,6 @@ void InstructionCodeGeneratorARM64::VisitArm64DataProcWithShifterOp( } void LocationsBuilderARM64::VisitIntermediateAddress(HIntermediateAddress* instruction) { - // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. - DCHECK(!kEmitCompilerReadBarrier); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); @@ -2201,10 +2206,7 @@ void LocationsBuilderARM64::VisitIntermediateAddress(HIntermediateAddress* instr locations->SetOut(Location::RequiresRegister()); } -void InstructionCodeGeneratorARM64::VisitIntermediateAddress( - HIntermediateAddress* instruction) { - // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. - DCHECK(!kEmitCompilerReadBarrier); +void InstructionCodeGeneratorARM64::VisitIntermediateAddress(HIntermediateAddress* instruction) { __ Add(OutputRegister(instruction), InputRegisterAt(instruction, 0), Operand(InputOperandAt(instruction, 1))); @@ -2304,11 +2306,15 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { // Block pools between `Load` and `MaybeRecordImplicitNullCheck`. BlockPoolsScope block_pools(masm); + // The read barrier instrumentation of object ArrayGet instructions + // does not support the HIntermediateAddress instruction. + DCHECK(!((type == Primitive::kPrimNot) && + instruction->GetArray()->IsIntermediateAddress() && + kEmitCompilerReadBarrier)); + if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) { // Object ArrayGet with Baker's read barrier case. Register temp = temps.AcquireW(); - // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. - DCHECK(!instruction->GetArray()->IsIntermediateAddress()); // Note that a potential implicit null check is handled in the // CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier call. codegen_->GenerateArrayLoadWithBakerReadBarrier( @@ -2341,9 +2347,6 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { } else { Register temp = temps.AcquireSameSizeAs(obj); if (instruction->GetArray()->IsIntermediateAddress()) { - // The read barrier instrumentation does not support the - // HIntermediateAddress instruction yet. - DCHECK(!kEmitCompilerReadBarrier); // We do not need to compute the intermediate address from the array: the // input instruction has done it already. See the comment in // `TryExtractArrayAccessAddress()`. @@ -2451,9 +2454,6 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { UseScratchRegisterScope temps(masm); Register temp = temps.AcquireSameSizeAs(array); if (instruction->GetArray()->IsIntermediateAddress()) { - // The read barrier instrumentation does not support the - // HIntermediateAddress instruction yet. - DCHECK(!kEmitCompilerReadBarrier); // We do not need to compute the intermediate address from the array: the // input instruction has done it already. See the comment in // `TryExtractArrayAccessAddress()`. diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 49f33d2567..232c3b3cbb 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -5740,7 +5740,19 @@ void InstructionCodeGeneratorX86_64::VisitThrow(HThrow* instruction) { CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>(); } -static bool TypeCheckNeedsATemporary(TypeCheckKind type_check_kind) { +static bool CheckCastTypeCheckNeedsATemporary(TypeCheckKind type_check_kind) { + if (type_check_kind == TypeCheckKind::kInterfaceCheck && !kPoisonHeapReferences) { + // We need a temporary for holding the iftable length. + return true; + } + return kEmitCompilerReadBarrier && + !kUseBakerReadBarrier && + (type_check_kind == TypeCheckKind::kAbstractClassCheck || + type_check_kind == TypeCheckKind::kClassHierarchyCheck || + type_check_kind == TypeCheckKind::kArrayObjectCheck); +} + +static bool InstanceOfTypeCheckNeedsATemporary(TypeCheckKind type_check_kind) { return kEmitCompilerReadBarrier && !kUseBakerReadBarrier && (type_check_kind == TypeCheckKind::kAbstractClassCheck || @@ -5778,7 +5790,7 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) { locations->SetOut(Location::RequiresRegister()); // When read barriers are enabled, we need a temporary register for // some cases. - if (TypeCheckNeedsATemporary(type_check_kind)) { + if (InstanceOfTypeCheckNeedsATemporary(type_check_kind)) { locations->AddTemp(Location::RequiresRegister()); } } @@ -5791,7 +5803,7 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { Location cls = locations->InAt(1); Location out_loc = locations->Out(); CpuRegister out = out_loc.AsRegister<CpuRegister>(); - Location maybe_temp_loc = TypeCheckNeedsATemporary(type_check_kind) ? + Location maybe_temp_loc = InstanceOfTypeCheckNeedsATemporary(type_check_kind) ? locations->GetTemp(0) : Location::NoLocation(); uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); @@ -5809,7 +5821,11 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } // /* HeapReference<Class> */ out = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset); + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + kEmitCompilerReadBarrier); switch (type_check_kind) { case TypeCheckKind::kExactCheck: { @@ -5970,33 +5986,45 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } } -void LocationsBuilderX86_64::VisitCheckCast(HCheckCast* instruction) { - LocationSummary::CallKind call_kind = LocationSummary::kNoCall; - bool throws_into_catch = instruction->CanThrowIntoCatchBlock(); - TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); +bool IsTypeCheckSlowPathFatal(TypeCheckKind type_check_kind, bool throws_into_catch) { switch (type_check_kind) { case TypeCheckKind::kExactCheck: case TypeCheckKind::kAbstractClassCheck: case TypeCheckKind::kClassHierarchyCheck: case TypeCheckKind::kArrayObjectCheck: - call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ? - LocationSummary::kCallOnSlowPath : - LocationSummary::kNoCall; // In fact, call on a fatal (non-returning) slow path. - break; + return !throws_into_catch && !kEmitCompilerReadBarrier; + case TypeCheckKind::kInterfaceCheck: + return !throws_into_catch && !kEmitCompilerReadBarrier && !kPoisonHeapReferences; case TypeCheckKind::kArrayCheck: case TypeCheckKind::kUnresolvedCheck: - case TypeCheckKind::kInterfaceCheck: - call_kind = LocationSummary::kCallOnSlowPath; - break; + return false; } + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); +} + +void LocationsBuilderX86_64::VisitCheckCast(HCheckCast* instruction) { + bool throws_into_catch = instruction->CanThrowIntoCatchBlock(); + TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); + bool is_fatal_slow_path = IsTypeCheckSlowPathFatal(type_check_kind, throws_into_catch); + LocationSummary::CallKind call_kind = is_fatal_slow_path + ? LocationSummary::kNoCall + : LocationSummary::kCallOnSlowPath; LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::Any()); + if (type_check_kind == TypeCheckKind::kInterfaceCheck) { + // Require a register for the interface check since there is a loop that compares the class to + // a memory address. + locations->SetInAt(1, Location::RequiresRegister()); + } else { + locations->SetInAt(1, Location::Any()); + } + // Note that TypeCheckSlowPathX86_64 uses this "temp" register too. locations->AddTemp(Location::RequiresRegister()); // When read barriers are enabled, we need an additional temporary // register for some cases. - if (TypeCheckNeedsATemporary(type_check_kind)) { + if (CheckCastTypeCheckNeedsATemporary(type_check_kind)) { locations->AddTemp(Location::RequiresRegister()); } } @@ -6009,20 +6037,19 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { Location cls = locations->InAt(1); Location temp_loc = locations->GetTemp(0); CpuRegister temp = temp_loc.AsRegister<CpuRegister>(); - Location maybe_temp2_loc = TypeCheckNeedsATemporary(type_check_kind) ? + Location maybe_temp2_loc = CheckCastTypeCheckNeedsATemporary(type_check_kind) ? locations->GetTemp(1) : Location::NoLocation(); - uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); - uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); - uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); - uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); + const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); + const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); + const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); + const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); + const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value(); + const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value(); + const int object_array_data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value(); bool is_type_check_slow_path_fatal = - (type_check_kind == TypeCheckKind::kExactCheck || - type_check_kind == TypeCheckKind::kAbstractClassCheck || - type_check_kind == TypeCheckKind::kClassHierarchyCheck || - type_check_kind == TypeCheckKind::kArrayObjectCheck) && - !instruction->CanThrowIntoCatchBlock(); + IsTypeCheckSlowPathFatal(type_check_kind, instruction->CanThrowIntoCatchBlock()); SlowPathCode* type_check_slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathX86_64(instruction, is_type_check_slow_path_fatal); @@ -6039,8 +6066,11 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { } // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset); - + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kEmitCompilerReadBarrier); if (cls.IsRegister()) { __ cmpl(temp, cls.AsRegister<CpuRegister>()); } else { @@ -6063,8 +6093,11 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { } // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset); - + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kEmitCompilerReadBarrier); // If the class is abstract, we eagerly fetch the super class of the // object to avoid doing a comparison we know will fail. NearLabel loop, compare_classes; @@ -6083,7 +6116,11 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { // going into the slow path, as it has been overwritten in the // meantime. // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset); + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kEmitCompilerReadBarrier); __ jmp(type_check_slow_path->GetEntryLabel()); __ Bind(&compare_classes); @@ -6107,8 +6144,11 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { } // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset); - + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kEmitCompilerReadBarrier); // Walk over the class hierarchy to find a match. NearLabel loop; __ Bind(&loop); @@ -6133,7 +6173,11 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { // going into the slow path, as it has been overwritten in the // meantime. // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset); + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kEmitCompilerReadBarrier); __ jmp(type_check_slow_path->GetEntryLabel()); __ Bind(&done); break; @@ -6152,8 +6196,11 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { } // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset); - + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kEmitCompilerReadBarrier); // Do an exact check. NearLabel check_non_primitive_component_type; if (cls.IsRegister()) { @@ -6180,7 +6227,11 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { // going into the slow path, as it has been overwritten in the // meantime. // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset); + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kEmitCompilerReadBarrier); __ jmp(type_check_slow_path->GetEntryLabel()); __ Bind(&check_non_primitive_component_type); @@ -6188,7 +6239,11 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { __ j(kEqual, &done); // Same comment as above regarding `temp` and the slow path. // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset); + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kEmitCompilerReadBarrier); __ jmp(type_check_slow_path->GetEntryLabel()); __ Bind(&done); break; @@ -6197,17 +6252,15 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: NearLabel done; + // Avoid null check if we know obj is not null. if (instruction->MustDoNullCheck()) { __ testl(obj, obj); __ j(kEqual, &done); } - // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset); - // We always go into the type check slow path for the unresolved - // and interface check cases. + // We always go into the type check slow path for the unresolved case. // // We cannot directly call the CheckCast runtime entry point // without resorting to a type checking slow path here (i.e. by @@ -6223,6 +6276,53 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { // case of the `switch` code as it was previously (with a direct // call to the runtime not using a type checking slow path). // This should also be beneficial for the other cases above. + + // Fast path for the interface check. Since we compare with a memory location in the inner + // loop we would need to have cls poisoned. However unpoisoning cls would reset the + // conditional flags and cause the conditional jump to be incorrect. + if (type_check_kind == TypeCheckKind::kInterfaceCheck && !kPoisonHeapReferences) { + // Try to avoid read barriers to improve the fast path. We can not get false positives by + // doing this. + // /* HeapReference<Class> */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + /*emit_read_barrier*/ false); + + // /* HeapReference<Class> */ temp = temp->iftable_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + temp_loc, + iftable_offset, + /*emit_read_barrier*/ false); + NearLabel is_null; + // Null iftable means it is empty. + __ testl(temp_loc.AsRegister<CpuRegister>(), temp_loc.AsRegister<CpuRegister>()); + __ j(kZero, &is_null); + + // Loop through the iftable and check if any class matches. + __ movl(maybe_temp2_loc.AsRegister<CpuRegister>(), + Address(temp_loc.AsRegister<CpuRegister>(), array_length_offset)); + + NearLabel start_loop; + __ Bind(&start_loop); + __ cmpl(cls.AsRegister<CpuRegister>(), + Address(temp_loc.AsRegister<CpuRegister>(), object_array_data_offset)); + __ j(kEqual, &done); // Return if same class. + // Go to next interface. + __ addq(temp_loc.AsRegister<CpuRegister>(), Immediate(2 * kHeapReferenceSize)); + __ subq(maybe_temp2_loc.AsRegister<CpuRegister>(), Immediate(2)); + __ j(kNotZero, &start_loop); + __ Bind(&is_null); + } + + // Since we clobbered temp_loc holding the class, we need to reload it. + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kEmitCompilerReadBarrier); __ jmp(type_check_slow_path->GetEntryLabel()); __ Bind(&done); break; @@ -6397,10 +6497,11 @@ void InstructionCodeGeneratorX86_64::GenerateReferenceLoadOneRegister(HInstructi void InstructionCodeGeneratorX86_64::GenerateReferenceLoadTwoRegisters(HInstruction* instruction, Location out, Location obj, - uint32_t offset) { + uint32_t offset, + bool emit_read_barrier) { CpuRegister out_reg = out.AsRegister<CpuRegister>(); CpuRegister obj_reg = obj.AsRegister<CpuRegister>(); - if (kEmitCompilerReadBarrier) { + if (emit_read_barrier) { if (kUseBakerReadBarrier) { // Load with fast path based Baker's read barrier. // /* HeapReference<Object> */ out = *(obj + offset) diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 8b19dad0d0..5a6dc54e7a 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -248,7 +248,8 @@ class InstructionCodeGeneratorX86_64 : public InstructionCodeGenerator { void GenerateReferenceLoadTwoRegisters(HInstruction* instruction, Location out, Location obj, - uint32_t offset); + uint32_t offset, + bool emit_read_barrier); // Generate a GC root reference load: // // root <- *address diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc index 04e063c92e..c2b1374f62 100644 --- a/compiler/optimizing/instruction_simplifier_shared.cc +++ b/compiler/optimizing/instruction_simplifier_shared.cc @@ -231,15 +231,6 @@ bool TryExtractArrayAccessAddress(HInstruction* access, HInstruction* array, HInstruction* index, size_t data_offset) { - if (kEmitCompilerReadBarrier) { - // The read barrier instrumentation does not support the - // HIntermediateAddress instruction yet. - // - // TODO: Handle this case properly in the ARM64 and ARM code generator and - // re-enable this optimization; otherwise, remove this TODO. - // b/26601270 - return false; - } if (index->IsConstant() || (index->IsBoundsCheck() && index->AsBoundsCheck()->GetIndex()->IsConstant())) { // When the index is a constant all the addressing can be fitted in the @@ -251,14 +242,20 @@ bool TryExtractArrayAccessAddress(HInstruction* access, // The access may require a runtime call or the original array pointer. return false; } + if (kEmitCompilerReadBarrier && + access->IsArrayGet() && + access->GetType() == Primitive::kPrimNot) { + // For object arrays, the read barrier instrumentation requires + // the original array pointer. + return false; + } // Proceed to extract the base address computation. HGraph* graph = access->GetBlock()->GetGraph(); ArenaAllocator* arena = graph->GetArena(); HIntConstant* offset = graph->GetIntConstant(data_offset); - HIntermediateAddress* address = - new (arena) HIntermediateAddress(array, offset, kNoDexPc); + HIntermediateAddress* address = new (arena) HIntermediateAddress(array, offset, kNoDexPc); // TODO: Is it ok to not have this on the intermediate address? // address->SetReferenceTypeInfo(array->GetReferenceTypeInfo()); access->GetBlock()->InsertInstructionBefore(address, access); diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc index d157509758..a9fe209063 100644 --- a/compiler/optimizing/locations.cc +++ b/compiler/optimizing/locations.cc @@ -16,11 +16,16 @@ #include "locations.h" +#include <type_traits> + #include "nodes.h" #include "code_generator.h" namespace art { +// Verify that Location is trivially copyable. +static_assert(std::is_trivially_copyable<Location>::value, "Location should be trivially copyable"); + LocationSummary::LocationSummary(HInstruction* instruction, CallKind call_kind, bool intrinsified) diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h index da27928ef2..52747c0cc4 100644 --- a/compiler/optimizing/locations.h +++ b/compiler/optimizing/locations.h @@ -91,12 +91,9 @@ class Location : public ValueObject { DCHECK(!IsValid()); } - Location(const Location& other) : value_(other.value_) {} + Location(const Location& other) = default; - Location& operator=(const Location& other) { - value_ = other.value_; - return *this; - } + Location& operator=(const Location& other) = default; bool IsConstant() const { return (value_ & kLocationConstantMask) == kConstant; @@ -328,7 +325,6 @@ class Location : public ValueObject { LOG(FATAL) << "Should not use this location kind"; } UNREACHABLE(); - return "?"; } // Unallocated locations. diff --git a/compiler/optimizing/register_allocation_resolver.cc b/compiler/optimizing/register_allocation_resolver.cc index caf66474eb..5991791a15 100644 --- a/compiler/optimizing/register_allocation_resolver.cc +++ b/compiler/optimizing/register_allocation_resolver.cc @@ -374,7 +374,9 @@ void RegisterAllocationResolver::ConnectSiblings(LiveInterval* interval) { if (current->GetType() == Primitive::kPrimNot) { DCHECK(interval->GetDefinedBy()->IsActualObject()) << interval->GetDefinedBy()->DebugName() - << "@" << safepoint_position->GetInstruction()->DebugName(); + << '(' << interval->GetDefinedBy()->GetId() << ')' + << "@" << safepoint_position->GetInstruction()->DebugName() + << '(' << safepoint_position->GetInstruction()->GetId() << ')'; LocationSummary* locations = safepoint_position->GetLocations(); if (current->GetParent()->HasSpillSlot()) { locations->SetStackBit(current->GetParent()->GetSpillSlot() / kVRegSize); diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h index 9c65280407..b34e125866 100644 --- a/compiler/utils/assembler_test.h +++ b/compiler/utils/assembler_test.h @@ -51,30 +51,30 @@ class AssemblerTest : public testing::Test { typedef std::string (*TestFn)(AssemblerTest* assembler_test, Ass* assembler); - void DriverFn(TestFn f, std::string test_name) { + void DriverFn(TestFn f, const std::string& test_name) { DriverWrapper(f(this, assembler_.get()), test_name); } // This driver assumes the assembler has already been called. - void DriverStr(std::string assembly_string, std::string test_name) { + void DriverStr(const std::string& assembly_string, const std::string& test_name) { DriverWrapper(assembly_string, test_name); } - std::string RepeatR(void (Ass::*f)(Reg), std::string fmt) { + std::string RepeatR(void (Ass::*f)(Reg), const std::string& fmt) { return RepeatTemplatedRegister<Reg>(f, GetRegisters(), &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, fmt); } - std::string Repeatr(void (Ass::*f)(Reg), std::string fmt) { + std::string Repeatr(void (Ass::*f)(Reg), const std::string& fmt) { return RepeatTemplatedRegister<Reg>(f, GetRegisters(), &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, fmt); } - std::string RepeatRR(void (Ass::*f)(Reg, Reg), std::string fmt) { + std::string RepeatRR(void (Ass::*f)(Reg, Reg), const std::string& fmt) { return RepeatTemplatedRegisters<Reg, Reg>(f, GetRegisters(), GetRegisters(), @@ -83,7 +83,7 @@ class AssemblerTest : public testing::Test { fmt); } - std::string RepeatRRNoDupes(void (Ass::*f)(Reg, Reg), std::string fmt) { + std::string RepeatRRNoDupes(void (Ass::*f)(Reg, Reg), const std::string& fmt) { return RepeatTemplatedRegistersNoDupes<Reg, Reg>(f, GetRegisters(), GetRegisters(), @@ -92,7 +92,7 @@ class AssemblerTest : public testing::Test { fmt); } - std::string Repeatrr(void (Ass::*f)(Reg, Reg), std::string fmt) { + std::string Repeatrr(void (Ass::*f)(Reg, Reg), const std::string& fmt) { return RepeatTemplatedRegisters<Reg, Reg>(f, GetRegisters(), GetRegisters(), @@ -101,7 +101,7 @@ class AssemblerTest : public testing::Test { fmt); } - std::string RepeatRRR(void (Ass::*f)(Reg, Reg, Reg), std::string fmt) { + std::string RepeatRRR(void (Ass::*f)(Reg, Reg, Reg), const std::string& fmt) { return RepeatTemplatedRegisters<Reg, Reg, Reg>(f, GetRegisters(), GetRegisters(), @@ -112,7 +112,7 @@ class AssemblerTest : public testing::Test { fmt); } - std::string Repeatrb(void (Ass::*f)(Reg, Reg), std::string fmt) { + std::string Repeatrb(void (Ass::*f)(Reg, Reg), const std::string& fmt) { return RepeatTemplatedRegisters<Reg, Reg>(f, GetRegisters(), GetRegisters(), @@ -121,7 +121,7 @@ class AssemblerTest : public testing::Test { fmt); } - std::string RepeatRr(void (Ass::*f)(Reg, Reg), std::string fmt) { + std::string RepeatRr(void (Ass::*f)(Reg, Reg), const std::string& fmt) { return RepeatTemplatedRegisters<Reg, Reg>(f, GetRegisters(), GetRegisters(), @@ -130,11 +130,11 @@ class AssemblerTest : public testing::Test { fmt); } - std::string RepeatRI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, std::string fmt) { + std::string RepeatRI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, const std::string& fmt) { return RepeatRegisterImm<RegisterView::kUsePrimaryName>(f, imm_bytes, fmt); } - std::string Repeatri(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, std::string fmt) { + std::string Repeatri(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, const std::string& fmt) { return RepeatRegisterImm<RegisterView::kUseSecondaryName>(f, imm_bytes, fmt); } @@ -145,7 +145,7 @@ class AssemblerTest : public testing::Test { const std::vector<Reg2*> reg2_registers, std::string (AssemblerTest::*GetName1)(const Reg1&), std::string (AssemblerTest::*GetName2)(const Reg2&), - std::string fmt) { + const std::string& fmt) { std::string str; std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0)); @@ -195,7 +195,7 @@ class AssemblerTest : public testing::Test { std::string (AssemblerTest::*GetName1)(const Reg1&), std::string (AssemblerTest::*GetName2)(const Reg2&), int imm_bits, - std::string fmt) { + const std::string& fmt) { std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0)); WarnOnCombinations(reg1_registers.size() * reg2_registers.size() * imms.size()); @@ -245,7 +245,7 @@ class AssemblerTest : public testing::Test { int imm_bits, const std::vector<Reg*> registers, std::string (AssemblerTest::*GetName)(const RegType&), - std::string fmt) { + const std::string& fmt) { std::string str; std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0)); @@ -281,7 +281,7 @@ class AssemblerTest : public testing::Test { } template <typename ImmType> - std::string RepeatRRIb(void (Ass::*f)(Reg, Reg, ImmType), int imm_bits, std::string fmt) { + std::string RepeatRRIb(void (Ass::*f)(Reg, Reg, ImmType), int imm_bits, const std::string& fmt) { return RepeatTemplatedRegistersImmBits<Reg, Reg, ImmType>(f, imm_bits, GetRegisters(), @@ -292,7 +292,7 @@ class AssemblerTest : public testing::Test { } template <typename ImmType> - std::string RepeatRIb(void (Ass::*f)(Reg, ImmType), int imm_bits, std::string fmt) { + std::string RepeatRIb(void (Ass::*f)(Reg, ImmType), int imm_bits, const std::string& fmt) { return RepeatTemplatedRegisterImmBits<Reg, ImmType>(f, imm_bits, GetRegisters(), @@ -301,7 +301,9 @@ class AssemblerTest : public testing::Test { } template <typename ImmType> - std::string RepeatFRIb(void (Ass::*f)(FPReg, Reg, ImmType), int imm_bits, std::string fmt) { + std::string RepeatFRIb(void (Ass::*f)(FPReg, Reg, ImmType), + int imm_bits, + const std::string& fmt) { return RepeatTemplatedRegistersImmBits<FPReg, Reg, ImmType>(f, imm_bits, GetFPRegisters(), @@ -311,7 +313,7 @@ class AssemblerTest : public testing::Test { fmt); } - std::string RepeatFF(void (Ass::*f)(FPReg, FPReg), std::string fmt) { + std::string RepeatFF(void (Ass::*f)(FPReg, FPReg), const std::string& fmt) { return RepeatTemplatedRegisters<FPReg, FPReg>(f, GetFPRegisters(), GetFPRegisters(), @@ -320,7 +322,7 @@ class AssemblerTest : public testing::Test { fmt); } - std::string RepeatFFF(void (Ass::*f)(FPReg, FPReg, FPReg), std::string fmt) { + std::string RepeatFFF(void (Ass::*f)(FPReg, FPReg, FPReg), const std::string& fmt) { return RepeatTemplatedRegisters<FPReg, FPReg, FPReg>(f, GetFPRegisters(), GetFPRegisters(), @@ -331,7 +333,7 @@ class AssemblerTest : public testing::Test { fmt); } - std::string RepeatFFR(void (Ass::*f)(FPReg, FPReg, Reg), std::string fmt) { + std::string RepeatFFR(void (Ass::*f)(FPReg, FPReg, Reg), const std::string& fmt) { return RepeatTemplatedRegisters<FPReg, FPReg, Reg>( f, GetFPRegisters(), @@ -345,7 +347,7 @@ class AssemblerTest : public testing::Test { std::string RepeatFFI(void (Ass::*f)(FPReg, FPReg, const Imm&), size_t imm_bytes, - std::string fmt) { + const std::string& fmt) { return RepeatTemplatedRegistersImm<FPReg, FPReg>(f, GetFPRegisters(), GetFPRegisters(), @@ -356,7 +358,9 @@ class AssemblerTest : public testing::Test { } template <typename ImmType> - std::string RepeatFFIb(void (Ass::*f)(FPReg, FPReg, ImmType), int imm_bits, std::string fmt) { + std::string RepeatFFIb(void (Ass::*f)(FPReg, FPReg, ImmType), + int imm_bits, + const std::string& fmt) { return RepeatTemplatedRegistersImmBits<FPReg, FPReg, ImmType>(f, imm_bits, GetFPRegisters(), @@ -367,7 +371,9 @@ class AssemblerTest : public testing::Test { } template <typename ImmType> - std::string RepeatIbFF(void (Ass::*f)(ImmType, FPReg, FPReg), int imm_bits, std::string fmt) { + std::string RepeatIbFF(void (Ass::*f)(ImmType, FPReg, FPReg), + int imm_bits, + const std::string& fmt) { return RepeatTemplatedImmBitsRegisters<ImmType, FPReg, FPReg>(f, GetFPRegisters(), GetFPRegisters(), @@ -377,7 +383,7 @@ class AssemblerTest : public testing::Test { fmt); } - std::string RepeatFR(void (Ass::*f)(FPReg, Reg), std::string fmt) { + std::string RepeatFR(void (Ass::*f)(FPReg, Reg), const std::string& fmt) { return RepeatTemplatedRegisters<FPReg, Reg>(f, GetFPRegisters(), GetRegisters(), @@ -386,7 +392,7 @@ class AssemblerTest : public testing::Test { fmt); } - std::string RepeatFr(void (Ass::*f)(FPReg, Reg), std::string fmt) { + std::string RepeatFr(void (Ass::*f)(FPReg, Reg), const std::string& fmt) { return RepeatTemplatedRegisters<FPReg, Reg>(f, GetFPRegisters(), GetRegisters(), @@ -395,7 +401,7 @@ class AssemblerTest : public testing::Test { fmt); } - std::string RepeatRF(void (Ass::*f)(Reg, FPReg), std::string fmt) { + std::string RepeatRF(void (Ass::*f)(Reg, FPReg), const std::string& fmt) { return RepeatTemplatedRegisters<Reg, FPReg>(f, GetRegisters(), GetFPRegisters(), @@ -404,7 +410,7 @@ class AssemblerTest : public testing::Test { fmt); } - std::string RepeatrF(void (Ass::*f)(Reg, FPReg), std::string fmt) { + std::string RepeatrF(void (Ass::*f)(Reg, FPReg), const std::string& fmt) { return RepeatTemplatedRegisters<Reg, FPReg>(f, GetRegisters(), GetFPRegisters(), @@ -413,7 +419,9 @@ class AssemblerTest : public testing::Test { fmt); } - std::string RepeatI(void (Ass::*f)(const Imm&), size_t imm_bytes, std::string fmt, + std::string RepeatI(void (Ass::*f)(const Imm&), + size_t imm_bytes, + const std::string& fmt, bool as_uint = false) { std::string str; std::vector<int64_t> imms = CreateImmediateValues(imm_bytes, as_uint); @@ -651,7 +659,7 @@ class AssemblerTest : public testing::Test { std::string RepeatTemplatedRegister(void (Ass::*f)(RegType), const std::vector<RegType*> registers, std::string (AssemblerTest::*GetName)(const RegType&), - std::string fmt) { + const std::string& fmt) { std::string str; for (auto reg : registers) { (assembler_.get()->*f)(*reg); @@ -679,7 +687,7 @@ class AssemblerTest : public testing::Test { const std::vector<Reg2*> reg2_registers, std::string (AssemblerTest::*GetName1)(const Reg1&), std::string (AssemblerTest::*GetName2)(const Reg2&), - std::string fmt) { + const std::string& fmt) { WarnOnCombinations(reg1_registers.size() * reg2_registers.size()); std::string str; @@ -717,7 +725,7 @@ class AssemblerTest : public testing::Test { const std::vector<Reg2*> reg2_registers, std::string (AssemblerTest::*GetName1)(const Reg1&), std::string (AssemblerTest::*GetName2)(const Reg2&), - std::string fmt) { + const std::string& fmt) { WarnOnCombinations(reg1_registers.size() * reg2_registers.size()); std::string str; @@ -758,7 +766,7 @@ class AssemblerTest : public testing::Test { std::string (AssemblerTest::*GetName1)(const Reg1&), std::string (AssemblerTest::*GetName2)(const Reg2&), std::string (AssemblerTest::*GetName3)(const Reg3&), - std::string fmt) { + const std::string& fmt) { std::string str; for (auto reg1 : reg1_registers) { for (auto reg2 : reg2_registers) { @@ -803,7 +811,7 @@ class AssemblerTest : public testing::Test { std::string (AssemblerTest::*GetName1)(const Reg1&), std::string (AssemblerTest::*GetName2)(const Reg2&), size_t imm_bytes, - std::string fmt) { + const std::string& fmt) { std::vector<int64_t> imms = CreateImmediateValues(imm_bytes); WarnOnCombinations(reg1_registers.size() * reg2_registers.size() * imms.size()); @@ -895,8 +903,9 @@ class AssemblerTest : public testing::Test { private: template <RegisterView kRegView> - std::string RepeatRegisterImm(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, - std::string fmt) { + std::string RepeatRegisterImm(void (Ass::*f)(Reg, const Imm&), + size_t imm_bytes, + const std::string& fmt) { const std::vector<Reg*> registers = GetRegisters(); std::string str; std::vector<int64_t> imms = CreateImmediateValues(imm_bytes); @@ -938,7 +947,7 @@ class AssemblerTest : public testing::Test { virtual void Pad(std::vector<uint8_t>& data ATTRIBUTE_UNUSED) { } - void DriverWrapper(std::string assembly_text, std::string test_name) { + void DriverWrapper(const std::string& assembly_text, const std::string& test_name) { assembler_->FinalizeCode(); size_t cs = assembler_->CodeSize(); std::unique_ptr<std::vector<uint8_t>> data(new std::vector<uint8_t>(cs)); diff --git a/compiler/utils/assembler_test_base.h b/compiler/utils/assembler_test_base.h index 8c71292465..ac24ee95eb 100644 --- a/compiler/utils/assembler_test_base.h +++ b/compiler/utils/assembler_test_base.h @@ -106,7 +106,9 @@ class AssemblerTestInfrastructure { // Driver() assembles and compares the results. If the results are not equal and we have a // disassembler, disassemble both and check whether they have the same mnemonics (in which case // we just warn). - void Driver(const std::vector<uint8_t>& data, std::string assembly_text, std::string test_name) { + void Driver(const std::vector<uint8_t>& data, + const std::string& assembly_text, + const std::string& test_name) { EXPECT_NE(assembly_text.length(), 0U) << "Empty assembly"; NativeAssemblerResult res; @@ -229,7 +231,7 @@ class AssemblerTestInfrastructure { bool success = Exec(args, error_msg); if (!success) { LOG(ERROR) << "Assembler command line:"; - for (std::string arg : args) { + for (const std::string& arg : args) { LOG(ERROR) << arg; } } @@ -238,7 +240,7 @@ class AssemblerTestInfrastructure { // Runs objdump -h on the binary file and extracts the first line with .text. // Returns "" on failure. - std::string Objdump(std::string file) { + std::string Objdump(const std::string& file) { bool have_objdump = FileExists(FindTool(objdump_cmd_name_)); EXPECT_TRUE(have_objdump) << "Cannot find objdump: " << GetObjdumpCommand(); if (!have_objdump) { @@ -287,8 +289,9 @@ class AssemblerTestInfrastructure { } // Disassemble both binaries and compare the text. - bool DisassembleBinaries(const std::vector<uint8_t>& data, const std::vector<uint8_t>& as, - std::string test_name) { + bool DisassembleBinaries(const std::vector<uint8_t>& data, + const std::vector<uint8_t>& as, + const std::string& test_name) { std::string disassembler = GetDisassembleCommand(); if (disassembler.length() == 0) { LOG(WARNING) << "No dissassembler command."; @@ -324,7 +327,7 @@ class AssemblerTestInfrastructure { return result; } - bool DisassembleBinary(std::string file, std::string* error_msg) { + bool DisassembleBinary(const std::string& file, std::string* error_msg) { std::vector<std::string> args; // Encaspulate the whole command line in a single string passed to @@ -345,7 +348,7 @@ class AssemblerTestInfrastructure { return Exec(args, error_msg); } - std::string WriteToFile(const std::vector<uint8_t>& buffer, std::string test_name) { + std::string WriteToFile(const std::vector<uint8_t>& buffer, const std::string& test_name) { std::string file_name = GetTmpnam() + std::string("---") + test_name; const char* data = reinterpret_cast<const char*>(buffer.data()); std::ofstream s_out(file_name + ".o"); @@ -354,7 +357,7 @@ class AssemblerTestInfrastructure { return file_name + ".o"; } - bool CompareFiles(std::string f1, std::string f2) { + bool CompareFiles(const std::string& f1, const std::string& f2) { std::ifstream f1_in(f1); std::ifstream f2_in(f2); @@ -369,7 +372,9 @@ class AssemblerTestInfrastructure { } // Compile the given assembly code and extract the binary, if possible. Put result into res. - bool Compile(std::string assembly_code, NativeAssemblerResult* res, std::string test_name) { + bool Compile(const std::string& assembly_code, + NativeAssemblerResult* res, + const std::string& test_name) { res->ok = false; res->code.reset(nullptr); @@ -438,7 +443,7 @@ class AssemblerTestInfrastructure { // Check whether file exists. Is used for commands, so strips off any parameters: anything after // the first space. We skip to the last slash for this, so it should work with directories with // spaces. - static bool FileExists(std::string file) { + static bool FileExists(const std::string& file) { if (file.length() == 0) { return false; } @@ -478,7 +483,7 @@ class AssemblerTestInfrastructure { return getcwd(temp, 1024) ? std::string(temp) + "/" : std::string(""); } - std::string FindTool(std::string tool_name) { + std::string FindTool(const std::string& tool_name) { // Find the current tool. Wild-card pattern is "arch-string*tool-name". std::string gcc_path = GetRootPath() + GetGCCRootPath(); std::vector<std::string> args; @@ -522,7 +527,8 @@ class AssemblerTestInfrastructure { // Helper for below. If name_predicate is empty, search for all files, otherwise use it for the // "-name" option. - static void FindToolDumpPrintout(std::string name_predicate, std::string tmp_file) { + static void FindToolDumpPrintout(const std::string& name_predicate, + const std::string& tmp_file) { std::string gcc_path = GetRootPath() + GetGCCRootPath(); std::vector<std::string> args; args.push_back("find"); @@ -562,7 +568,7 @@ class AssemblerTestInfrastructure { } // For debug purposes. - void FindToolDump(std::string tool_name) { + void FindToolDump(const std::string& tool_name) { // Check with the tool name. FindToolDumpPrintout(architecture_string_ + "*" + tool_name, GetTmpnam()); FindToolDumpPrintout("", GetTmpnam()); diff --git a/compiler/utils/jni_macro_assembler_test.h b/compiler/utils/jni_macro_assembler_test.h index 829f34b4b7..293f4cde9c 100644 --- a/compiler/utils/jni_macro_assembler_test.h +++ b/compiler/utils/jni_macro_assembler_test.h @@ -39,12 +39,12 @@ class JNIMacroAssemblerTest : public testing::Test { typedef std::string (*TestFn)(JNIMacroAssemblerTest* assembler_test, Ass* assembler); - void DriverFn(TestFn f, std::string test_name) { + void DriverFn(TestFn f, const std::string& test_name) { DriverWrapper(f(this, assembler_.get()), test_name); } // This driver assumes the assembler has already been called. - void DriverStr(std::string assembly_string, std::string test_name) { + void DriverStr(const std::string& assembly_string, const std::string& test_name) { DriverWrapper(assembly_string, test_name); } @@ -128,7 +128,7 @@ class JNIMacroAssemblerTest : public testing::Test { virtual void Pad(std::vector<uint8_t>& data ATTRIBUTE_UNUSED) { } - void DriverWrapper(std::string assembly_text, std::string test_name) { + void DriverWrapper(const std::string& assembly_text, const std::string& test_name) { assembler_->FinalizeCode(); size_t cs = assembler_->CodeSize(); std::unique_ptr<std::vector<uint8_t>> data(new std::vector<uint8_t>(cs)); diff --git a/compiler/utils/managed_register.h b/compiler/utils/managed_register.h index bb30f460c3..184cdf5050 100644 --- a/compiler/utils/managed_register.h +++ b/compiler/utils/managed_register.h @@ -17,8 +17,11 @@ #ifndef ART_COMPILER_UTILS_MANAGED_REGISTER_H_ #define ART_COMPILER_UTILS_MANAGED_REGISTER_H_ +#include <type_traits> #include <vector> +#include "base/value_object.h" + namespace art { namespace arm { @@ -42,17 +45,14 @@ namespace x86_64 { class X86_64ManagedRegister; } -class ManagedRegister { +class ManagedRegister : public ValueObject { public: // ManagedRegister is a value class. There exists no method to change the // internal state. We therefore allow a copy constructor and an // assignment-operator. - constexpr ManagedRegister(const ManagedRegister& other) : id_(other.id_) { } + constexpr ManagedRegister(const ManagedRegister& other) = default; - ManagedRegister& operator=(const ManagedRegister& other) { - id_ = other.id_; - return *this; - } + ManagedRegister& operator=(const ManagedRegister& other) = default; constexpr arm::ArmManagedRegister AsArm() const; constexpr arm64::Arm64ManagedRegister AsArm64() const; @@ -85,6 +85,9 @@ class ManagedRegister { int id_; }; +static_assert(std::is_trivially_copyable<ManagedRegister>::value, + "ManagedRegister should be trivially copyable"); + class ManagedRegisterSpill : public ManagedRegister { public: // ManagedRegisterSpill contains information about data type size and location in caller frame diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc index 3ef2f9440c..a52f519439 100644 --- a/compiler/utils/mips/assembler_mips32r6_test.cc +++ b/compiler/utils/mips/assembler_mips32r6_test.cc @@ -219,7 +219,7 @@ class AssemblerMIPS32r6Test : public AssemblerTest<mips::MipsAssembler, void BranchCondTwoRegsHelper(void (mips::MipsAssembler::*f)(mips::Register, mips::Register, mips::MipsLabel*), - std::string instr_name) { + const std::string& instr_name) { mips::MipsLabel label; (Base::GetAssembler()->*f)(mips::A0, mips::A1, &label); constexpr size_t kAdduCount1 = 63; diff --git a/compiler/utils/mips/assembler_mips_test.cc b/compiler/utils/mips/assembler_mips_test.cc index 75149cf242..c24e1b16fb 100644 --- a/compiler/utils/mips/assembler_mips_test.cc +++ b/compiler/utils/mips/assembler_mips_test.cc @@ -188,7 +188,7 @@ class AssemblerMIPSTest : public AssemblerTest<mips::MipsAssembler, void BranchCondOneRegHelper(void (mips::MipsAssembler::*f)(mips::Register, mips::MipsLabel*), - std::string instr_name) { + const std::string& instr_name) { mips::MipsLabel label; (Base::GetAssembler()->*f)(mips::A0, &label); constexpr size_t kAdduCount1 = 63; @@ -217,7 +217,7 @@ class AssemblerMIPSTest : public AssemblerTest<mips::MipsAssembler, void BranchCondTwoRegsHelper(void (mips::MipsAssembler::*f)(mips::Register, mips::Register, mips::MipsLabel*), - std::string instr_name) { + const std::string& instr_name) { mips::MipsLabel label; (Base::GetAssembler()->*f)(mips::A0, mips::A1, &label); constexpr size_t kAdduCount1 = 63; diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc index 1fdef96fe4..ba8f25ea77 100644 --- a/compiler/utils/mips64/assembler_mips64_test.cc +++ b/compiler/utils/mips64/assembler_mips64_test.cc @@ -212,7 +212,7 @@ class AssemblerMIPS64Test : public AssemblerTest<mips64::Mips64Assembler, void BranchCondOneRegHelper(void (mips64::Mips64Assembler::*f)(mips64::GpuRegister, mips64::Mips64Label*), - std::string instr_name) { + const std::string& instr_name) { mips64::Mips64Label label; (Base::GetAssembler()->*f)(mips64::A0, &label); constexpr size_t kAdduCount1 = 63; @@ -241,7 +241,7 @@ class AssemblerMIPS64Test : public AssemblerTest<mips64::Mips64Assembler, void BranchCondTwoRegsHelper(void (mips64::Mips64Assembler::*f)(mips64::GpuRegister, mips64::GpuRegister, mips64::Mips64Label*), - std::string instr_name) { + const std::string& instr_name) { mips64::Mips64Label label; (Base::GetAssembler()->*f)(mips64::A0, mips64::A1, &label); constexpr size_t kAdduCount1 = 63; diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index e3f39cdbae..fc549ecb04 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -1481,31 +1481,6 @@ DEFINE_FUNCTION art_quick_unlock_object_no_inline END_FUNCTION art_quick_unlock_object_no_inline DEFINE_FUNCTION art_quick_check_cast - testl LITERAL(ACCESS_FLAGS_CLASS_IS_INTERFACE), MIRROR_CLASS_ACCESS_FLAGS_OFFSET(%rdi) - jz .Lnot_interface - - // There are no read barriers since the iftable is immutable. There can be false negatives for - // the read barrier case if classes in the IfTable are in the from-space. In the case where - // we do not find a matching interface we call into artIsAssignableFromCode which will have - // read barriers. - movl MIRROR_CLASS_IF_TABLE_OFFSET(%rsi), %ecx - UNPOISON_HEAP_REF ecx - testl %ecx, %ecx - jz .Lnot_interface - movl MIRROR_ARRAY_LENGTH_OFFSET(%rcx), %r8d -.Lstart_loop: - // Re-poison before comparing to prevent rare possible false positives. This is done inside - // the loop since heap poisoning is only for testing builds. - POISON_HEAP_REF edi - cmpl MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rcx), %edi - je .Lreturn // Return if same class. - UNPOISON_HEAP_REF edi - // Go to next interface. - add LITERAL(COMPRESSED_REFERENCE_SIZE * 2), %rcx - sub LITERAL(2), %r8 - jnz .Lstart_loop - -.Lnot_interface: // We could check the super classes here but that is usually already checked in the caller. PUSH rdi // Save args for exc PUSH rsi diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index bde03277b7..5d922989d9 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -42,7 +42,6 @@ Mutex* Locks::deoptimization_lock_ = nullptr; ReaderWriterMutex* Locks::heap_bitmap_lock_ = nullptr; Mutex* Locks::instrument_entrypoints_lock_ = nullptr; Mutex* Locks::intern_table_lock_ = nullptr; -Mutex* Locks::interpreter_string_init_map_lock_ = nullptr; Mutex* Locks::jni_libraries_lock_ = nullptr; Mutex* Locks::logging_lock_ = nullptr; Mutex* Locks::mem_maps_lock_ = nullptr; diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 3f2c5a9e74..74b786c38e 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -79,7 +79,6 @@ enum LockLevel { kAllocSpaceLock, kBumpPointerSpaceBlockLock, kArenaPoolLock, - kDexFileToMethodInlinerMapLock, kInternTableLock, kOatFileSecondaryLookupLock, kHostDlOpenHandlesLock, @@ -92,12 +91,10 @@ enum LockLevel { kDefaultMutexLevel, kDexLock, kMarkSweepLargeObjectLock, - kPinTableLock, kJdwpObjectRegistryLock, kModifyLdtLock, kAllocatedThreadIdsLock, kMonitorPoolLock, - kMethodVerifiersLock, kClassLinkerClassesLock, // TODO rename. kJitCodeCacheLock, kBreakpointLock, @@ -630,12 +627,9 @@ class Locks { // TODO: improve name, perhaps instrumentation_update_lock_. static Mutex* deoptimization_lock_ ACQUIRED_AFTER(alloc_tracker_lock_); - // Guards String initializer register map in interpreter. - static Mutex* interpreter_string_init_map_lock_ ACQUIRED_AFTER(deoptimization_lock_); - // The thread_list_lock_ guards ThreadList::list_. It is also commonly held to stop threads // attaching and detaching. - static Mutex* thread_list_lock_ ACQUIRED_AFTER(interpreter_string_init_map_lock_); + static Mutex* thread_list_lock_ ACQUIRED_AFTER(deoptimization_lock_); // Signaled when threads terminate. Used to determine when all non-daemons have terminated. static ConditionVariable* thread_exit_cond_ GUARDED_BY(Locks::thread_list_lock_); diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 827e85e9b6..17e3729a20 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -52,7 +52,7 @@ class ScratchFile { ScratchFile(const ScratchFile& other, const char* suffix); - explicit ScratchFile(ScratchFile&& other); + ScratchFile(ScratchFile&& other); ScratchFile& operator=(ScratchFile&& other); diff --git a/runtime/obj_ptr.h b/runtime/obj_ptr.h index 93182323d6..d24c6fbd2c 100644 --- a/runtime/obj_ptr.h +++ b/runtime/obj_ptr.h @@ -20,6 +20,7 @@ #include <ostream> #include <type_traits> +#include "base/macros.h" #include "base/mutex.h" // For Locks::mutator_lock_. #include "globals.h" @@ -41,17 +42,26 @@ class ObjPtr { public: ALWAYS_INLINE ObjPtr() REQUIRES_SHARED(Locks::mutator_lock_) : reference_(0u) {} - ALWAYS_INLINE ObjPtr(std::nullptr_t) REQUIRES_SHARED(Locks::mutator_lock_) : reference_(0u) {} + // Note: The following constructors allow implicit conversion. This simplifies code that uses + // them, e.g., for parameter passing. However, in general, implicit-conversion constructors + // are discouraged and detected by cpplint and clang-tidy. So mark these constructors + // as NOLINT (without category, as the categories are different). + + ALWAYS_INLINE ObjPtr(std::nullptr_t) // NOLINT + REQUIRES_SHARED(Locks::mutator_lock_) + : reference_(0u) {} template <typename Type> - ALWAYS_INLINE ObjPtr(Type* ptr) REQUIRES_SHARED(Locks::mutator_lock_) + ALWAYS_INLINE ObjPtr(Type* ptr) // NOLINT + REQUIRES_SHARED(Locks::mutator_lock_) : reference_(Encode(static_cast<MirrorType*>(ptr))) { static_assert(std::is_base_of<MirrorType, Type>::value, "Input type must be a subtype of the ObjPtr type"); } template <typename Type> - ALWAYS_INLINE ObjPtr(const ObjPtr<Type, kPoison>& other) REQUIRES_SHARED(Locks::mutator_lock_) + ALWAYS_INLINE ObjPtr(const ObjPtr<Type, kPoison>& other) // NOLINT + REQUIRES_SHARED(Locks::mutator_lock_) : reference_(Encode(static_cast<MirrorType*>(other.Ptr()))) { static_assert(std::is_base_of<MirrorType, Type>::value, "Input type must be a subtype of the ObjPtr type"); @@ -154,6 +164,9 @@ class ObjPtr { uintptr_t reference_; }; +static_assert(std::is_trivially_copyable<ObjPtr<void>>::value, + "ObjPtr should be trivially copyable"); + // Hash function for stl data structures. class HashObjPtr { public: diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc index 56d737f4ee..41329af138 100644 --- a/test/115-native-bridge/nativebridge.cc +++ b/test/115-native-bridge/nativebridge.cc @@ -466,7 +466,7 @@ extern "C" int native_bridge_unloadLibrary(void* handle ATTRIBUTE_UNUSED) { return 0; } -extern "C" char* native_bridge_getError() { +extern "C" const char* native_bridge_getError() { printf("dlerror() in native bridge.\n"); return nullptr; } diff --git a/test/527-checker-array-access-split/info.txt b/test/527-checker-array-access-split/info.txt index 920680462d..a39bea37cf 100644 --- a/test/527-checker-array-access-split/info.txt +++ b/test/527-checker-array-access-split/info.txt @@ -1 +1 @@ -Test arm64-specific array access optimization. +Test arm- and arm64-specific array access optimization. diff --git a/test/957-methodhandle-transforms/src/Main.java b/test/957-methodhandle-transforms/src/Main.java index e50391b299..6c977f4f34 100644 --- a/test/957-methodhandle-transforms/src/Main.java +++ b/test/957-methodhandle-transforms/src/Main.java @@ -26,6 +26,10 @@ public class Main { testDropArguments(); testCatchException(); testGuardWithTest(); + testArrayElementGetter(); + testArrayElementSetter(); + testIdentity(); + testConstant(); } public static void testThrowException() throws Throwable { @@ -247,6 +251,424 @@ public class Main { assertEquals("target", returnVal); } + public static void testArrayElementGetter() throws Throwable { + MethodHandle getter = MethodHandles.arrayElementGetter(int[].class); + + { + int[] array = new int[1]; + array[0] = 42; + int value = (int) getter.invoke(array, 0); + if (value != 42) { + System.out.println("Unexpected value: " + value); + } + + try { + value = (int) getter.invoke(array, -1); + fail(); + } catch (ArrayIndexOutOfBoundsException expected) { + } + + try { + value = (int) getter.invoke(null, -1); + fail(); + } catch (NullPointerException expected) { + } + } + + { + getter = MethodHandles.arrayElementGetter(long[].class); + long[] array = new long[1]; + array[0] = 42; + long value = (long) getter.invoke(array, 0); + if (value != 42l) { + System.out.println("Unexpected value: " + value); + } + } + + { + getter = MethodHandles.arrayElementGetter(short[].class); + short[] array = new short[1]; + array[0] = 42; + short value = (short) getter.invoke(array, 0); + if (value != 42l) { + System.out.println("Unexpected value: " + value); + } + } + + { + getter = MethodHandles.arrayElementGetter(char[].class); + char[] array = new char[1]; + array[0] = 42; + char value = (char) getter.invoke(array, 0); + if (value != 42l) { + System.out.println("Unexpected value: " + value); + } + } + + { + getter = MethodHandles.arrayElementGetter(byte[].class); + byte[] array = new byte[1]; + array[0] = (byte) 0x8; + byte value = (byte) getter.invoke(array, 0); + if (value != (byte) 0x8) { + System.out.println("Unexpected value: " + value); + } + } + + { + getter = MethodHandles.arrayElementGetter(boolean[].class); + boolean[] array = new boolean[1]; + array[0] = true; + boolean value = (boolean) getter.invoke(array, 0); + if (!value) { + System.out.println("Unexpected value: " + value); + } + } + + { + getter = MethodHandles.arrayElementGetter(float[].class); + float[] array = new float[1]; + array[0] = 42.0f; + float value = (float) getter.invoke(array, 0); + if (value != 42.0f) { + System.out.println("Unexpected value: " + value); + } + } + + { + getter = MethodHandles.arrayElementGetter(double[].class); + double[] array = new double[1]; + array[0] = 42.0; + double value = (double) getter.invoke(array, 0); + if (value != 42.0) { + System.out.println("Unexpected value: " + value); + } + } + + { + getter = MethodHandles.arrayElementGetter(String[].class); + String[] array = new String[3]; + array[0] = "42"; + array[1] = "48"; + array[2] = "54"; + String value = (String) getter.invoke(array, 0); + assertEquals("42", value); + value = (String) getter.invoke(array, 1); + assertEquals("48", value); + value = (String) getter.invoke(array, 2); + assertEquals("54", value); + } + } + + public static void testArrayElementSetter() throws Throwable { + MethodHandle setter = MethodHandles.arrayElementSetter(int[].class); + + { + int[] array = new int[2]; + setter.invoke(array, 0, 42); + setter.invoke(array, 1, 43); + + if (array[0] != 42) { + System.out.println("Unexpected value: " + array[0]); + } + if (array[1] != 43) { + System.out.println("Unexpected value: " + array[1]); + } + + try { + setter.invoke(array, -1, 42); + fail(); + } catch (ArrayIndexOutOfBoundsException expected) { + } + + try { + setter.invoke(null, 0, 42); + fail(); + } catch (NullPointerException expected) { + } + } + + { + setter = MethodHandles.arrayElementSetter(long[].class); + long[] array = new long[1]; + setter.invoke(array, 0, 42l); + if (array[0] != 42l) { + System.out.println("Unexpected value: " + array[0]); + } + } + + { + setter = MethodHandles.arrayElementSetter(short[].class); + short[] array = new short[1]; + setter.invoke(array, 0, (short) 42); + if (array[0] != 42l) { + System.out.println("Unexpected value: " + array[0]); + } + } + + { + setter = MethodHandles.arrayElementSetter(char[].class); + char[] array = new char[1]; + setter.invoke(array, 0, (char) 42); + if (array[0] != 42) { + System.out.println("Unexpected value: " + array[0]); + } + } + + { + setter = MethodHandles.arrayElementSetter(byte[].class); + byte[] array = new byte[1]; + setter.invoke(array, 0, (byte) 0x8); + if (array[0] != (byte) 0x8) { + System.out.println("Unexpected value: " + array[0]); + } + } + + { + setter = MethodHandles.arrayElementSetter(boolean[].class); + boolean[] array = new boolean[1]; + setter.invoke(array, 0, true); + if (!array[0]) { + System.out.println("Unexpected value: " + array[0]); + } + } + + { + setter = MethodHandles.arrayElementSetter(float[].class); + float[] array = new float[1]; + setter.invoke(array, 0, 42.0f); + if (array[0] != 42.0f) { + System.out.println("Unexpected value: " + array[0]); + } + } + + { + setter = MethodHandles.arrayElementSetter(double[].class); + double[] array = new double[1]; + setter.invoke(array, 0, 42.0); + if (array[0] != 42.0) { + System.out.println("Unexpected value: " + array[0]); + } + } + + { + setter = MethodHandles.arrayElementSetter(String[].class); + String[] array = new String[3]; + setter.invoke(array, 0, "42"); + setter.invoke(array, 1, "48"); + setter.invoke(array, 2, "54"); + assertEquals("42", array[0]); + assertEquals("48", array[1]); + assertEquals("54", array[2]); + } + } + + public static void testIdentity() throws Throwable { + { + MethodHandle identity = MethodHandles.identity(boolean.class); + boolean value = (boolean) identity.invoke(false); + if (value) { + System.out.println("Unexpected value: " + value); + } + } + + { + MethodHandle identity = MethodHandles.identity(byte.class); + byte value = (byte) identity.invoke((byte) 0x8); + if (value != (byte) 0x8) { + System.out.println("Unexpected value: " + value); + } + } + + { + MethodHandle identity = MethodHandles.identity(char.class); + char value = (char) identity.invoke((char) -56); + if (value != (char) -56) { + System.out.println("Unexpected value: " + value); + } + } + + { + MethodHandle identity = MethodHandles.identity(short.class); + short value = (short) identity.invoke((short) -59); + if (value != (short) -59) { + System.out.println("Unexpected value: " + value); + } + } + + { + MethodHandle identity = MethodHandles.identity(int.class); + int value = (int) identity.invoke(52); + if (value != 52) { + System.out.println("Unexpected value: " + value); + } + } + + { + MethodHandle identity = MethodHandles.identity(long.class); + long value = (long) identity.invoke(-76l); + if (value != (long) -76) { + System.out.println("Unexpected value: " + value); + } + } + + { + MethodHandle identity = MethodHandles.identity(float.class); + float value = (float) identity.invoke(56.0f); + if (value != (float) 56.0f) { + System.out.println("Unexpected value: " + value); + } + } + + { + MethodHandle identity = MethodHandles.identity(double.class); + double value = (double) identity.invoke((double) 72.0); + if (value != (double) 72.0) { + System.out.println("Unexpected value: " + value); + } + } + + { + MethodHandle identity = MethodHandles.identity(String.class); + String value = (String) identity.invoke("bazman"); + assertEquals("bazman", value); + } + } + + public static void testConstant() throws Throwable { + // int constants. + { + MethodHandle constant = MethodHandles.constant(int.class, 56); + int value = (int) constant.invoke(); + if (value != 56) { + System.out.println("Unexpected value: " + value); + } + + // short constant values are converted to int. + constant = MethodHandles.constant(int.class, (short) 52); + value = (int) constant.invoke(); + if (value != 52) { + System.out.println("Unexpected value: " + value); + } + + // char constant values are converted to int. + constant = MethodHandles.constant(int.class, (char) 'b'); + value = (int) constant.invoke(); + if (value != (int) 'b') { + System.out.println("Unexpected value: " + value); + } + + // int constant values are converted to int. + constant = MethodHandles.constant(int.class, (byte) 0x1); + value = (int) constant.invoke(); + if (value != 1) { + System.out.println("Unexpected value: " + value); + } + + // boolean, float, double and long primitive constants are not convertible + // to int, so the handle creation must fail with a CCE. + try { + MethodHandles.constant(int.class, false); + fail(); + } catch (ClassCastException expected) { + } + + try { + MethodHandles.constant(int.class, 0.1f); + fail(); + } catch (ClassCastException expected) { + } + + try { + MethodHandles.constant(int.class, 0.2); + fail(); + } catch (ClassCastException expected) { + } + + try { + MethodHandles.constant(int.class, 73l); + fail(); + } catch (ClassCastException expected) { + } + } + + // long constants. + { + MethodHandle constant = MethodHandles.constant(long.class, 56l); + long value = (long) constant.invoke(); + if (value != 56l) { + System.out.println("Unexpected value: " + value); + } + + constant = MethodHandles.constant(long.class, (int) 56); + value = (long) constant.invoke(); + if (value != 56l) { + System.out.println("Unexpected value: " + value); + } + } + + // byte constants. + { + MethodHandle constant = MethodHandles.constant(byte.class, (byte) 0x12); + byte value = (byte) constant.invoke(); + if (value != (byte) 0x12) { + System.out.println("Unexpected value: " + value); + } + } + + // boolean constants. + { + MethodHandle constant = MethodHandles.constant(boolean.class, true); + boolean value = (boolean) constant.invoke(); + if (!value) { + System.out.println("Unexpected value: " + value); + } + } + + // char constants. + { + MethodHandle constant = MethodHandles.constant(char.class, 'f'); + char value = (char) constant.invoke(); + if (value != 'f') { + System.out.println("Unexpected value: " + value); + } + } + + // short constants. + { + MethodHandle constant = MethodHandles.constant(short.class, (short) 123); + short value = (short) constant.invoke(); + if (value != (short) 123) { + System.out.println("Unexpected value: " + value); + } + } + + // float constants. + { + MethodHandle constant = MethodHandles.constant(float.class, 56.0f); + float value = (float) constant.invoke(); + if (value != 56.0f) { + System.out.println("Unexpected value: " + value); + } + } + + // double constants. + { + MethodHandle constant = MethodHandles.constant(double.class, 256.0); + double value = (double) constant.invoke(); + if (value != 256.0) { + System.out.println("Unexpected value: " + value); + } + } + + // reference constants. + { + MethodHandle constant = MethodHandles.constant(String.class, "256.0"); + String value = (String) constant.invoke(); + assertEquals("256.0", value); + } + } + public static void fail() { System.out.println("FAIL"); Thread.dumpStack(); diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 11425e1377..8f8f99832c 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -608,14 +608,8 @@ TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS := # Tests that should fail in the read barrier configuration with the Optimizing compiler (AOT). # 484: Baker's fast path based read barrier compiler instrumentation generates code containing # more parallel moves on x86, thus some Checker assertions may fail. -# 527: On ARM64 and ARM, the read barrier instrumentation does not support the HIntermediateAddress -# instruction yet (b/26601270). -# 562: On ARM64 and ARM, the read barrier instrumentation does not support the HIntermediateAddress -# instruction yet (b/26601270). TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := \ - 484-checker-register-hints \ - 527-checker-array-access-split \ - 562-checker-no-intermediate + 484-checker-register-hints # Tests that should fail in the read barrier configuration with JIT (Optimizing compiler). TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS := diff --git a/test/run-test b/test/run-test index 7a4afafa4e..37eefb34d3 100755 --- a/test/run-test +++ b/test/run-test @@ -758,8 +758,8 @@ fi if [ "$run_checker" = "yes" -a "$target_mode" = "yes" ]; then # We will need to `adb pull` the .cfg output from the target onto the host to # run checker on it. This file can be big. - build_file_size_limit=24576 - run_file_size_limit=24576 + build_file_size_limit=32768 + run_file_size_limit=32768 fi if [ ${USE_JACK} = "false" ]; then # Set ulimit if we build with dx only, Jack can generate big temp files. |