diff options
author | 2018-05-18 08:33:20 +0100 | |
---|---|---|
committer | 2018-06-11 10:57:17 +0100 | |
commit | 4c8e12e66968929b36fac6a2237ca4b04160161e (patch) | |
tree | d8bbfd72a978c69ef2eef98c37e7869673c52295 | |
parent | 20c64f8d802cc575cc9a1a1f6c493a611b23e2ee (diff) |
ART: Adds an entrypoint for invoke-custom
Add support for the compiler to call into the runtime for
invoke-custom bytecodes.
Bug: 35337872
Test: art/test.py --host -r -t 952
Test: art/test.py --target --64 -r -t 952
Test: art/test.py --target --32 -r -t 952
Change-Id: I821432e7e5248c91b8e1d36c3112974c34171803
38 files changed, 826 insertions, 158 deletions
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 9f2346db3c..b3feb787a9 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -544,6 +544,7 @@ void CodeGenerator::GenerateInvokeStaticOrDirectRuntimeCall( case kVirtual: case kInterface: case kPolymorphic: + case kCustom: LOG(FATAL) << "Unexpected invoke type: " << invoke->GetInvokeType(); UNREACHABLE(); } @@ -572,6 +573,7 @@ void CodeGenerator::GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invok entrypoint = kQuickInvokeInterfaceTrampolineWithAccessCheck; break; case kPolymorphic: + case kCustom: LOG(FATAL) << "Unexpected invoke type: " << invoke->GetInvokeType(); UNREACHABLE(); } @@ -586,6 +588,12 @@ void CodeGenerator::GenerateInvokePolymorphicCall(HInvokePolymorphic* invoke) { InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), nullptr); } +void CodeGenerator::GenerateInvokeCustomCall(HInvokeCustom* invoke) { + MoveConstant(invoke->GetLocations()->GetTemp(0), invoke->GetCallSiteIndex()); + QuickEntrypointEnum entrypoint = kQuickInvokeCustom; + InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), nullptr); +} + void CodeGenerator::CreateUnresolvedFieldLocationSummary( HInstruction* field_access, DataType::Type field_type, diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index a340446ac3..b3c29aa804 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -542,10 +542,13 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { void GenerateInvokeStaticOrDirectRuntimeCall( HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path); + void GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invoke); void GenerateInvokePolymorphicCall(HInvokePolymorphic* invoke); + void GenerateInvokeCustomCall(HInvokeCustom* invoke); + void CreateUnresolvedFieldLocationSummary( HInstruction* field_access, DataType::Type field_type, diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 6f173e19f5..5f0533cbe9 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -4695,6 +4695,15 @@ void InstructionCodeGeneratorARM64::VisitInvokePolymorphic(HInvokePolymorphic* i codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__); } +void LocationsBuilderARM64::VisitInvokeCustom(HInvokeCustom* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorARM64::VisitInvokeCustom(HInvokeCustom* invoke) { + codegen_->GenerateInvokeCustomCall(invoke); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__); +} + vixl::aarch64::Label* CodeGeneratorARM64::NewBootImageRelRoPatch( uint32_t boot_image_offset, vixl::aarch64::Label* adrp_label) { diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 859e1597c6..91c13154bb 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -3742,6 +3742,15 @@ void InstructionCodeGeneratorARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 9); } +void LocationsBuilderARMVIXL::VisitInvokeCustom(HInvokeCustom* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorARMVIXL::VisitInvokeCustom(HInvokeCustom* invoke) { + codegen_->GenerateInvokeCustomCall(invoke); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 10); +} + void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall); @@ -5493,7 +5502,7 @@ void InstructionCodeGeneratorARMVIXL::VisitNewInstance(HNewInstance* instruction codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>(); } - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 10); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 11); } void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) { @@ -5513,7 +5522,7 @@ void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) { codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc()); CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>(); DCHECK(!codegen_->IsLeafMethod()); - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 11); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 12); } void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) { @@ -7084,7 +7093,7 @@ void InstructionCodeGeneratorARMVIXL::VisitSuspendCheck(HSuspendCheck* instructi return; } GenerateSuspendCheck(instruction, nullptr); - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 12); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 13); } void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction, @@ -7437,7 +7446,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ HLoadClass::LoadKind load_kind = cls->GetLoadKind(); if (load_kind == HLoadClass::LoadKind::kRuntimeCall) { codegen_->GenerateLoadClassRuntimeCall(cls); - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 13); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 14); return; } DCHECK(!cls->NeedsAccessCheck()); @@ -7523,7 +7532,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ } else { __ Bind(slow_path->GetExitLabel()); } - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 14); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 15); } } @@ -7732,7 +7741,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE codegen_->AddSlowPath(slow_path); __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 15); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 16); return; } case HLoadString::LoadKind::kJitTableAddress: { @@ -7754,7 +7763,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_); codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc()); CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 16); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 17); } static int32_t GetExceptionTlsOffset() { @@ -8384,7 +8393,7 @@ void InstructionCodeGeneratorARMVIXL::VisitMonitorOperation(HMonitorOperation* i } else { CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>(); } - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 17); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 18); } void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) { @@ -8883,7 +8892,7 @@ void CodeGeneratorARMVIXL::GenerateGcRootFieldLoad( // Note that GC roots are not affected by heap poisoning, thus we // do not have to unpoison `root_reg` here. } - MaybeGenerateMarkingRegisterCheck(/* code */ 18); + MaybeGenerateMarkingRegisterCheck(/* code */ 19); } void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -8963,7 +8972,7 @@ void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* i narrow ? BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET : BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET); } - MaybeGenerateMarkingRegisterCheck(/* code */ 19, /* temp_loc */ LocationFrom(ip)); + MaybeGenerateMarkingRegisterCheck(/* code */ 20, /* temp_loc */ LocationFrom(ip)); return; } @@ -9041,7 +9050,7 @@ void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(HInstruction* i DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(), BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET); } - MaybeGenerateMarkingRegisterCheck(/* code */ 20, /* temp_loc */ LocationFrom(ip)); + MaybeGenerateMarkingRegisterCheck(/* code */ 21, /* temp_loc */ LocationFrom(ip)); return; } @@ -9095,7 +9104,7 @@ void CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier(HInstructio // Fast path: the GC is not marking: just load the reference. GenerateRawReferenceLoad(instruction, ref, obj, offset, index, scale_factor, needs_null_check); __ Bind(slow_path->GetExitLabel()); - MaybeGenerateMarkingRegisterCheck(/* code */ 21); + MaybeGenerateMarkingRegisterCheck(/* code */ 22); } void CodeGeneratorARMVIXL::UpdateReferenceFieldWithBakerReadBarrier(HInstruction* instruction, @@ -9150,7 +9159,7 @@ void CodeGeneratorARMVIXL::UpdateReferenceFieldWithBakerReadBarrier(HInstruction // Fast path: the GC is not marking: nothing to do (the field is // up-to-date, and we don't need to load the reference). __ Bind(slow_path->GetExitLabel()); - MaybeGenerateMarkingRegisterCheck(/* code */ 22); + MaybeGenerateMarkingRegisterCheck(/* code */ 23); } void CodeGeneratorARMVIXL::GenerateRawReferenceLoad(HInstruction* instruction, diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 8be84a15bd..507db364b5 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -7795,6 +7795,14 @@ void InstructionCodeGeneratorMIPS::VisitInvokePolymorphic(HInvokePolymorphic* in codegen_->GenerateInvokePolymorphicCall(invoke); } +void LocationsBuilderMIPS::VisitInvokeCustom(HInvokeCustom* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorMIPS::VisitInvokeCustom(HInvokeCustom* invoke) { + codegen_->GenerateInvokeCustomCall(invoke); +} + static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS* codegen) { if (invoke->GetLocations()->Intrinsified()) { IntrinsicCodeGeneratorMIPS intrinsic(codegen); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index cd9e0e521e..08a6512feb 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -5908,6 +5908,14 @@ void InstructionCodeGeneratorMIPS64::VisitInvokePolymorphic(HInvokePolymorphic* codegen_->GenerateInvokePolymorphicCall(invoke); } +void LocationsBuilderMIPS64::VisitInvokeCustom(HInvokeCustom* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorMIPS64::VisitInvokeCustom(HInvokeCustom* invoke) { + codegen_->GenerateInvokeCustomCall(invoke); +} + static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS64* codegen) { if (invoke->GetLocations()->Intrinsified()) { IntrinsicCodeGeneratorMIPS64 intrinsic(codegen); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 9e315381b1..9f42ac76f5 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -2311,6 +2311,14 @@ void InstructionCodeGeneratorX86::VisitInvokePolymorphic(HInvokePolymorphic* inv codegen_->GenerateInvokePolymorphicCall(invoke); } +void LocationsBuilderX86::VisitInvokeCustom(HInvokeCustom* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorX86::VisitInvokeCustom(HInvokeCustom* invoke) { + codegen_->GenerateInvokeCustomCall(invoke); +} + void LocationsBuilderX86::VisitNeg(HNeg* neg) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index f7397046d7..05194b15d5 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -2501,6 +2501,14 @@ void InstructionCodeGeneratorX86_64::VisitInvokePolymorphic(HInvokePolymorphic* codegen_->GenerateInvokePolymorphicCall(invoke); } +void LocationsBuilderX86_64::VisitInvokeCustom(HInvokeCustom* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorX86_64::VisitInvokeCustom(HInvokeCustom* invoke) { + codegen_->GenerateInvokeCustomCall(invoke); +} + void LocationsBuilderX86_64::VisitNeg(HNeg* neg) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall); diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 6900cd883a..7dd756e13a 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -460,9 +460,10 @@ static bool AlwaysThrows(CompilerDriver* const compiler_driver, ArtMethod* metho bool HInliner::TryInline(HInvoke* invoke_instruction) { if (invoke_instruction->IsInvokeUnresolved() || - invoke_instruction->IsInvokePolymorphic()) { - return false; // Don't bother to move further if we know the method is unresolved or an - // invoke-polymorphic. + invoke_instruction->IsInvokePolymorphic() || + invoke_instruction->IsInvokeCustom()) { + return false; // Don't bother to move further if we know the method is unresolved or the + // invocation is polymorphic (invoke-{polymorphic,custom}). } ScopedObjectAccess soa(Thread::Current()); diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 24dc2ee9b4..be8f2b1c74 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -449,11 +449,7 @@ void HInstructionBuilder::BuildIntrinsic(ArtMethod* method) { target_method, HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); RangeInstructionOperands operands(graph_->GetNumberOfVRegs() - in_vregs, in_vregs); - HandleInvoke(invoke, - operands, - dex_file_->GetMethodShorty(method_idx), - /* clinit_check */ nullptr, - /* is_unresolved */ false); + HandleInvoke(invoke, operands, dex_file_->GetMethodShorty(method_idx), /* is_unresolved */ false); // Add the return instruction. if (return_type_ == DataType::Type::kVoid) { @@ -916,11 +912,11 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, uint32_t method_idx, const InstructionOperands& operands) { InvokeType invoke_type = GetInvokeTypeFromOpCode(instruction.Opcode()); - const char* descriptor = dex_file_->GetMethodShorty(method_idx); - DataType::Type return_type = DataType::FromShorty(descriptor[0]); + const char* shorty = dex_file_->GetMethodShorty(method_idx); + DataType::Type return_type = DataType::FromShorty(shorty[0]); // Remove the return type from the 'proto'. - size_t number_of_arguments = strlen(descriptor) - 1; + size_t number_of_arguments = strlen(shorty) - 1; if (invoke_type != kStatic) { // instance call // One extra argument for 'this'. number_of_arguments++; @@ -937,11 +933,7 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, dex_pc, method_idx, invoke_type); - return HandleInvoke(invoke, - operands, - descriptor, - nullptr /* clinit_check */, - true /* is_unresolved */); + return HandleInvoke(invoke, operands, shorty, /* is_unresolved */ true); } // Replace calls to String.<init> with StringFactory. @@ -968,7 +960,7 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, invoke_type, target_method, HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit); - return HandleStringInit(invoke, operands, descriptor); + return HandleStringInit(invoke, operands, shorty); } // Potential class initialization check, in the case of a static method call. @@ -1028,29 +1020,39 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, resolved_method, ImTable::GetImtIndex(resolved_method)); } - - return HandleInvoke(invoke, operands, descriptor, clinit_check, false /* is_unresolved */); + return HandleInvoke(invoke, operands, shorty, /* is_unresolved */ false, clinit_check); } -bool HInstructionBuilder::BuildInvokePolymorphic(const Instruction& instruction ATTRIBUTE_UNUSED, - uint32_t dex_pc, +bool HInstructionBuilder::BuildInvokePolymorphic(uint32_t dex_pc, uint32_t method_idx, dex::ProtoIndex proto_idx, const InstructionOperands& operands) { - const char* descriptor = dex_file_->GetShorty(proto_idx); - DCHECK_EQ(1 + ArtMethod::NumArgRegisters(descriptor), operands.GetNumberOfOperands()); - DataType::Type return_type = DataType::FromShorty(descriptor[0]); - size_t number_of_arguments = strlen(descriptor); + const char* shorty = dex_file_->GetShorty(proto_idx); + DCHECK_EQ(1 + ArtMethod::NumArgRegisters(shorty), operands.GetNumberOfOperands()); + DataType::Type return_type = DataType::FromShorty(shorty[0]); + size_t number_of_arguments = strlen(shorty); HInvoke* invoke = new (allocator_) HInvokePolymorphic(allocator_, number_of_arguments, return_type, dex_pc, method_idx); - return HandleInvoke(invoke, - operands, - descriptor, - nullptr /* clinit_check */, - false /* is_unresolved */); + return HandleInvoke(invoke, operands, shorty, /* is_unresolved */ false); +} + + +bool HInstructionBuilder::BuildInvokeCustom(uint32_t dex_pc, + uint32_t call_site_idx, + const InstructionOperands& operands) { + dex::ProtoIndex proto_idx = dex_file_->GetProtoIndexForCallSite(call_site_idx); + const char* shorty = dex_file_->GetShorty(proto_idx); + DataType::Type return_type = DataType::FromShorty(shorty[0]); + size_t number_of_arguments = strlen(shorty) - 1; + HInvoke* invoke = new (allocator_) HInvokeCustom(allocator_, + number_of_arguments, + call_site_idx, + return_type, + dex_pc); + return HandleInvoke(invoke, operands, shorty, /* is_unresolved */ false); } HNewInstance* HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, uint32_t dex_pc) { @@ -1197,10 +1199,10 @@ HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke( bool HInstructionBuilder::SetupInvokeArguments(HInvoke* invoke, const InstructionOperands& operands, - const char* descriptor, + const char* shorty, size_t start_index, size_t* argument_index) { - uint32_t descriptor_index = 1; // Skip the return type. + uint32_t shorty_index = 1; // Skip the return type. const size_t number_of_operands = operands.GetNumberOfOperands(); for (size_t i = start_index; // Make sure we don't go over the expected arguments or over the number of @@ -1208,7 +1210,7 @@ bool HInstructionBuilder::SetupInvokeArguments(HInvoke* invoke, // it hasn't been properly checked. (i < number_of_operands) && (*argument_index < invoke->GetNumberOfArguments()); i++, (*argument_index)++) { - DataType::Type type = DataType::FromShorty(descriptor[descriptor_index++]); + DataType::Type type = DataType::FromShorty(shorty[shorty_index++]); bool is_wide = (type == DataType::Type::kInt64) || (type == DataType::Type::kFloat64); if (is_wide && ((i + 1 == number_of_operands) || (operands.GetOperand(i) + 1 != operands.GetOperand(i + 1)))) { @@ -1250,9 +1252,9 @@ bool HInstructionBuilder::SetupInvokeArguments(HInvoke* invoke, bool HInstructionBuilder::HandleInvoke(HInvoke* invoke, const InstructionOperands& operands, - const char* descriptor, - HClinitCheck* clinit_check, - bool is_unresolved) { + const char* shorty, + bool is_unresolved, + HClinitCheck* clinit_check) { DCHECK(!invoke->IsInvokeStaticOrDirect() || !invoke->AsInvokeStaticOrDirect()->IsStringInit()); size_t start_index = 0; @@ -1267,7 +1269,7 @@ bool HInstructionBuilder::HandleInvoke(HInvoke* invoke, argument_index = 1; } - if (!SetupInvokeArguments(invoke, operands, descriptor, start_index, &argument_index)) { + if (!SetupInvokeArguments(invoke, operands, shorty, start_index, &argument_index)) { return false; } @@ -1288,13 +1290,13 @@ bool HInstructionBuilder::HandleInvoke(HInvoke* invoke, bool HInstructionBuilder::HandleStringInit(HInvoke* invoke, const InstructionOperands& operands, - const char* descriptor) { + const char* shorty) { DCHECK(invoke->IsInvokeStaticOrDirect()); DCHECK(invoke->AsInvokeStaticOrDirect()->IsStringInit()); size_t start_index = 1; size_t argument_index = 0; - if (!SetupInvokeArguments(invoke, operands, descriptor, start_index, &argument_index)) { + if (!SetupInvokeArguments(invoke, operands, shorty, start_index, &argument_index)) { return false; } @@ -2144,14 +2146,28 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, uint32_t args[5]; uint32_t number_of_vreg_arguments = instruction.GetVarArgs(args); VarArgsInstructionOperands operands(args, number_of_vreg_arguments); - return BuildInvokePolymorphic(instruction, dex_pc, method_idx, proto_idx, operands); + return BuildInvokePolymorphic(dex_pc, method_idx, proto_idx, operands); } case Instruction::INVOKE_POLYMORPHIC_RANGE: { uint16_t method_idx = instruction.VRegB_4rcc(); dex::ProtoIndex proto_idx(instruction.VRegH_4rcc()); RangeInstructionOperands operands(instruction.VRegC_4rcc(), instruction.VRegA_4rcc()); - return BuildInvokePolymorphic(instruction, dex_pc, method_idx, proto_idx, operands); + return BuildInvokePolymorphic(dex_pc, method_idx, proto_idx, operands); + } + + case Instruction::INVOKE_CUSTOM: { + uint16_t call_site_idx = instruction.VRegB_35c(); + uint32_t args[5]; + uint32_t number_of_vreg_arguments = instruction.GetVarArgs(args); + VarArgsInstructionOperands operands(args, number_of_vreg_arguments); + return BuildInvokeCustom(dex_pc, call_site_idx, operands); + } + + case Instruction::INVOKE_CUSTOM_RANGE: { + uint16_t call_site_idx = instruction.VRegB_3rc(); + RangeInstructionOperands operands(instruction.VRegC_3rc(), instruction.VRegA_3rc()); + return BuildInvokeCustom(dex_pc, call_site_idx, operands); } case Instruction::NEG_INT: { @@ -2933,7 +2949,21 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, break; } - default: + case Instruction::UNUSED_3E: + case Instruction::UNUSED_3F: + case Instruction::UNUSED_40: + case Instruction::UNUSED_41: + case Instruction::UNUSED_42: + case Instruction::UNUSED_43: + case Instruction::UNUSED_79: + case Instruction::UNUSED_7A: + case Instruction::UNUSED_F3: + case Instruction::UNUSED_F4: + case Instruction::UNUSED_F5: + case Instruction::UNUSED_F6: + case Instruction::UNUSED_F7: + case Instruction::UNUSED_F8: + case Instruction::UNUSED_F9: { VLOG(compiler) << "Did not compile " << dex_file_->PrettyMethod(dex_compilation_unit_->GetDexMethodIndex()) << " because of unhandled instruction " @@ -2941,6 +2971,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, MaybeRecordStat(compilation_stats_, MethodCompilationStat::kNotCompiledUnhandledInstruction); return false; + } } return true; } // NOLINT(readability/fn_size) diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index 2218a691ea..af7092a0cf 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -173,12 +173,17 @@ class HInstructionBuilder : public ValueObject { // Builds an invocation node for invoke-polymorphic and returns whether the // instruction is supported. - bool BuildInvokePolymorphic(const Instruction& instruction, - uint32_t dex_pc, + bool BuildInvokePolymorphic(uint32_t dex_pc, uint32_t method_idx, dex::ProtoIndex proto_idx, const InstructionOperands& operands); + // Builds an invocation node for invoke-custom and returns whether the + // instruction is supported. + bool BuildInvokeCustom(uint32_t dex_pc, + uint32_t call_site_idx, + const InstructionOperands& operands); + // Builds a new array node and the instructions that fill it. HNewArray* BuildFilledNewArray(uint32_t dex_pc, dex::TypeIndex type_index, @@ -253,19 +258,19 @@ class HInstructionBuilder : public ValueObject { bool SetupInvokeArguments(HInvoke* invoke, const InstructionOperands& operands, - const char* descriptor, + const char* shorty, size_t start_index, size_t* argument_index); bool HandleInvoke(HInvoke* invoke, const InstructionOperands& operands, - const char* descriptor, - HClinitCheck* clinit_check, - bool is_unresolved); + const char* shorty, + bool is_unresolved, + HClinitCheck* clinit_check = nullptr); bool HandleStringInit(HInvoke* invoke, const InstructionOperands& operands, - const char* descriptor); + const char* shorty); void HandleStringInitResult(HInvokeStaticOrDirect* invoke); HClinitCheck* ProcessClinitCheckForInvoke( diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 056f533398..4ffde7bc52 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -142,6 +142,7 @@ static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) case kSuper: case kInterface: case kPolymorphic: + case kCustom: return false; } LOG(FATAL) << "Unknown intrinsic invoke type: " << intrinsic_type; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 3fd5b6b02d..2037879726 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1380,6 +1380,7 @@ class HLoopInformationOutwardIterator : public ValueObject { M(InvokeStaticOrDirect, Invoke) \ M(InvokeVirtual, Invoke) \ M(InvokePolymorphic, Invoke) \ + M(InvokeCustom, Invoke) \ M(LessThan, Condition) \ M(LessThanOrEqual, Condition) \ M(LoadClass, Instruction) \ @@ -4382,6 +4383,38 @@ class HInvokePolymorphic FINAL : public HInvoke { DEFAULT_COPY_CONSTRUCTOR(InvokePolymorphic); }; +class HInvokeCustom FINAL : public HInvoke { + public: + HInvokeCustom(ArenaAllocator* allocator, + uint32_t number_of_arguments, + uint32_t call_site_index, + DataType::Type return_type, + uint32_t dex_pc) + : HInvoke(kInvokeCustom, + allocator, + number_of_arguments, + /* number_of_other_inputs */ 0u, + return_type, + dex_pc, + /* dex_method_index */ dex::kDexNoIndex, + /* resolved_method */ nullptr, + kStatic), + call_site_index_(call_site_index) { + } + + uint32_t GetCallSiteIndex() const { return call_site_index_; } + + bool IsClonable() const OVERRIDE { return true; } + + DECLARE_INSTRUCTION(InvokeCustom); + + protected: + DEFAULT_COPY_CONSTRUCTOR(InvokeCustom); + + private: + uint32_t call_site_index_; +}; + class HInvokeStaticOrDirect FINAL : public HInvoke { public: // Requirements of this method call regarding the class @@ -6510,9 +6543,9 @@ inline void HLoadString::AddSpecialInput(HInstruction* special_input) { class HLoadMethodHandle FINAL : public HInstruction { public: HLoadMethodHandle(HCurrentMethod* current_method, - uint16_t method_handle_idx, - const DexFile& dex_file, - uint32_t dex_pc) + uint16_t method_handle_idx, + const DexFile& dex_file, + uint32_t dex_pc) : HInstruction(kLoadMethodHandle, DataType::Type::kReference, SideEffectsForArchRuntimeCalls(), diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc index 19c405e517..e76e98a2a3 100644 --- a/compiler/utils/assembler_thumb_test_expected.cc.inc +++ b/compiler/utils/assembler_thumb_test_expected.cc.inc @@ -153,7 +153,7 @@ const char* const VixlJniHelpersResults[] = { " 21c: f8d9 8034 ldr.w r8, [r9, #52] ; 0x34\n", " 220: 4770 bx lr\n", " 222: 4660 mov r0, ip\n", - " 224: f8d9 c2cc ldr.w ip, [r9, #716] ; 0x2cc\n", + " 224: f8d9 c2d0 ldr.w ip, [r9, #720] ; 0x2d0\n", " 228: 47e0 blx ip\n", nullptr }; diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index d0a6eb9ff2..2bc286a7f4 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -497,7 +497,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(76U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(24U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(164 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), + EXPECT_EQ(165 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), sizeof(QuickEntryPoints)); } diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc index f570158dfb..f1f896058c 100644 --- a/libdexfile/dex/dex_file.cc +++ b/libdexfile/dex/dex_file.cc @@ -605,6 +605,15 @@ std::string DexFile::PrettyType(dex::TypeIndex type_idx) const { return PrettyDescriptor(GetTypeDescriptor(type_id)); } +dex::ProtoIndex DexFile::GetProtoIndexForCallSite(uint32_t call_site_idx) const { + const DexFile::CallSiteIdItem& csi = GetCallSiteId(call_site_idx); + CallSiteArrayValueIterator it(*this, csi); + it.Next(); + it.Next(); + DCHECK_EQ(EncodedArrayValueIterator::ValueType::kMethodType, it.GetValueType()); + return dex::ProtoIndex(it.GetJavaValue().i); +} + // Checks that visibility is as expected. Includes special behavior for M and // before to allow runtime and build visibility when expecting runtime. std::ostream& operator<<(std::ostream& os, const DexFile& dex_file) { diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index ed219808d2..a8cd1871b4 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -737,6 +737,8 @@ class DexFile { return DataBegin() + call_site_id.data_off_; } + dex::ProtoIndex GetProtoIndexForCallSite(uint32_t call_site_idx) const; + static const TryItem* GetTryItems(const DexInstructionIterator& code_item_end, uint32_t offset); // Get the base of the encoded data for the given DexCode. diff --git a/libdexfile/dex/invoke_type.h b/libdexfile/dex/invoke_type.h index 9b3af673a8..1740c079bb 100644 --- a/libdexfile/dex/invoke_type.h +++ b/libdexfile/dex/invoke_type.h @@ -28,7 +28,8 @@ enum InvokeType : uint32_t { kSuper, // <<super>> kInterface, // <<interface>> kPolymorphic, // <<polymorphic>> - kMaxInvokeType = kPolymorphic + kCustom, // <<custom>> + kMaxInvokeType = kCustom }; std::ostream& operator<<(std::ostream& os, const InvokeType& rhs); diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 2ef30c0d26..ccff9f6a7b 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -2693,10 +2693,24 @@ ENTRY art_quick_invoke_polymorphic str r1, [sp, 72] @ r0:r1 := Result. Copy r1 to context. RESTORE_SAVE_REFS_AND_ARGS_FRAME REFRESH_MARKING_REGISTER - vmov d0, r0, r1 @ Put result r0:r1 into floating point return register. + vmov d0, r0, r1 @ Put result r0:r1 into floating point return register. RETURN_OR_DELIVER_PENDING_EXCEPTION_REG r2 END art_quick_invoke_polymorphic +.extern artInvokeCustom +ENTRY art_quick_invoke_custom + SETUP_SAVE_REFS_AND_ARGS_FRAME r1 + @ r0 := call_site_idx + mov r1, rSELF @ r1 := Thread::Current + mov r2, sp @ r2 := SP + bl artInvokeCustom @ artInvokeCustom(call_site_idx, Thread*, SP) + str r1, [sp, #72] @ Save r1 to context (r0:r1 = result) + RESTORE_SAVE_REFS_AND_ARGS_FRAME + REFRESH_MARKING_REGISTER + vmov d0, r0, r1 @ Put result r0:r1 into floating point return register. + RETURN_OR_DELIVER_PENDING_EXCEPTION_REG r2 +END art_quick_invoke_custom + // Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding. // Argument 0: r0: The context pointer for ExecuteSwitchImpl. // Argument 1: r1: Pointer to the templated ExecuteSwitchImpl to call. diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 5e540dd5e9..80d5fce423 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -2855,6 +2855,19 @@ ENTRY art_quick_invoke_polymorphic RETURN_OR_DELIVER_PENDING_EXCEPTION END art_quick_invoke_polymorphic +.extern artInvokeCustom +ENTRY art_quick_invoke_custom + SETUP_SAVE_REFS_AND_ARGS_FRAME // Save callee saves in case allocation triggers GC. + // x0 := call_site_idx + mov x1, xSELF // x1 := Thread::Current() + mov x2, sp // x2 := SP + bl artInvokeCustom // artInvokeCustom(call_site_idx, thread, save_area) + RESTORE_SAVE_REFS_AND_ARGS_FRAME + REFRESH_MARKING_REGISTER + fmov d0, x0 // Copy result to double result register. + RETURN_OR_DELIVER_PENDING_EXCEPTION +END art_quick_invoke_custom + // Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding. // Argument 0: x0: The context pointer for ExecuteSwitchImpl. // Argument 1: x1: Pointer to the templated ExecuteSwitchImpl to call. diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 5d6e410101..2b69c1753b 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -410,6 +410,9 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { static_assert(!IsDirectEntrypoint(kQuickInvokeVirtualTrampolineWithAccessCheck), "Non-direct C stub marked direct."); qpoints->pInvokePolymorphic = art_quick_invoke_polymorphic; + static_assert(!IsDirectEntrypoint(kQuickInvokePolymorphic), "Non-direct C stub marked direct."); + qpoints->pInvokeCustom = art_quick_invoke_custom; + static_assert(!IsDirectEntrypoint(kQuickInvokeCustom), "Non-direct C stub marked direct."); // Thread qpoints->pTestSuspend = art_quick_test_suspend; diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index c9bdc969ee..508a2013b7 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -3270,3 +3270,24 @@ ENTRY art_quick_invoke_polymorphic 1: DELIVER_PENDING_EXCEPTION END art_quick_invoke_polymorphic + + /* + * InvokeCustom invocation. + * On entry: + * a0 = call_site_idx + */ +.extern artInvokeCustom +ENTRY art_quick_invoke_custom + SETUP_SAVE_REFS_AND_ARGS_FRAME + move $a1, rSELF # Make $a1 an alias for the current Thread. + la $t9, artInvokeCustom # Invoke artInvokeCustom + jalr $t9 # with args (call_site_idx, Thread*, context). + addiu $a2, $sp, ARG_SLOT_SIZE # Make $a2 a pointer to the saved frame context. + lw $t7, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_ + RESTORE_SAVE_REFS_AND_ARGS_FRAME + bnez $t7, 1f + # don't care if $v0 and/or $v1 are modified, when exception branch taken + MTD $v0, $v1, $f0, $f1 # move float value to return value + jalr $zero, $ra + nop +END art_quick_invoke_custom diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 1800056f77..258acddd47 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -3070,4 +3070,25 @@ ENTRY art_quick_invoke_polymorphic DELIVER_PENDING_EXCEPTION END art_quick_invoke_polymorphic + /* + * InvokeCustom invocation. + * On entry: + * a0 = call_site_idx + */ +.extern artInvokeCustom +ENTRY art_quick_invoke_custom + SETUP_SAVE_REFS_AND_ARGS_FRAME + move $a1, rSELF # Make $a1 an alias for the current Thread. + jal artInvokeCustom # Call artInvokeCustom(call_site_idx, Thread*, context). + move $a2, $sp # Make $a1 a pointer to the saved frame context. + ld $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_ + daddiu $sp, $sp, REFS_AND_ARGS_MINUS_REFS_SIZE # skip a0-a7 and f12-f19 + RESTORE_SAVE_REFS_ONLY_FRAME + bne $t0, $zero, 1f + dmtc1 $v0, $f0 # place return value to FP return value + jalr $zero, $ra + dmtc1 $v1, $f1 # place return value to FP return value +1: + DELIVER_PENDING_EXCEPTION +END art_quick_invoke_polymorphic .set pop diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index e392198d44..e1b3df8621 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -2455,6 +2455,28 @@ DEFINE_FUNCTION art_quick_invoke_polymorphic RETURN_OR_DELIVER_PENDING_EXCEPTION END_FUNCTION art_quick_invoke_polymorphic +DEFINE_FUNCTION art_quick_invoke_custom + SETUP_SAVE_REFS_AND_ARGS_FRAME ebx, ebx // Save frame. + // EAX := call_site_index + mov %esp, %ecx // Remember SP. + subl LITERAL(4), %esp // Alignment padding. + CFI_ADJUST_CFA_OFFSET(4) + push %ecx // pass SP + CFI_ADJUST_CFA_OFFSET(4) + pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() + CFI_ADJUST_CFA_OFFSET(4) + push %eax // pass call_site_index + CFI_ADJUST_CFA_OFFSET(4) + call SYMBOL(artInvokeCustom) // artInvokeCustom(call_site_index, Thread*, SP) + addl LITERAL(16), %esp // Pop arguments. + CFI_ADJUST_CFA_OFFSET(-16) + mov %eax, 4(%esp) // Result is in EAX:EDX. Copy to saved FP state. + mov %edx, 8(%esp) + mov %edx, 40(%esp) // Copy EDX to saved context + RESTORE_SAVE_REFS_AND_ARGS_FRAME + RETURN_OR_DELIVER_PENDING_EXCEPTION +END_FUNCTION art_quick_invoke_custom + // Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding. // Argument 0: ESP+4: The context pointer for ExecuteSwitchImpl. // Argument 1: ESP+8: Pointer to the templated ExecuteSwitchImpl to call. diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 3f5d4f669a..9980966967 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -2430,6 +2430,17 @@ DEFINE_FUNCTION art_quick_invoke_polymorphic RETURN_OR_DELIVER_PENDING_EXCEPTION END_FUNCTION art_quick_invoke_polymorphic +DEFINE_FUNCTION art_quick_invoke_custom + SETUP_SAVE_REFS_AND_ARGS_FRAME // save callee saves + // RDI := call_site_index + movq %gs:THREAD_SELF_OFFSET, %rsi // RSI := Thread::Current() + movq %rsp, %rdx // RDX := SP + call SYMBOL(artInvokeCustom) // artInvokeCustom(Thread*, SP) + RESTORE_SAVE_REFS_AND_ARGS_FRAME + movq %rax, %xmm0 // Result is in RAX. Copy to FP result register. + RETURN_OR_DELIVER_PENDING_EXCEPTION +END_FUNCTION art_quick_invoke_custom + // Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding. // Argument 0: RDI: The context pointer for ExecuteSwitchImpl. // Argument 1: RSI: Pointer to the templated ExecuteSwitchImpl to call. diff --git a/runtime/asm_support.h b/runtime/asm_support.h index 70ff40d32c..ffbff88421 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -73,7 +73,7 @@ ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, // Offset of field Thread::tlsPtr_.mterp_current_ibase. #define THREAD_CURRENT_IBASE_OFFSET \ - (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 164) * __SIZEOF_POINTER__) + (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 165) * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET, art::Thread::MterpCurrentIBaseOffset<POINTER_SIZE>().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_default_ibase. diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h index 1804d9e64d..938489b730 100644 --- a/runtime/entrypoints/quick/quick_default_externs.h +++ b/runtime/entrypoints/quick/quick_default_externs.h @@ -114,9 +114,9 @@ extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, vo extern "C" void art_quick_invoke_virtual_trampoline_with_access_check(uint32_t, void*); -// Invoke polymorphic entrypoint. Return type is dynamic and may be void, a primitive value, or -// reference return type. +// Polymorphic invoke entrypoints. extern "C" void art_quick_invoke_polymorphic(uint32_t, void*); +extern "C" void art_quick_invoke_custom(uint32_t, void*); // Thread entrypoints. extern "C" void art_quick_test_suspend(); diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h index 3f66045576..5dcece4208 100644 --- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h +++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h @@ -106,6 +106,7 @@ static void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qp qpoints->pInvokeVirtualTrampolineWithAccessCheck = art_quick_invoke_virtual_trampoline_with_access_check; qpoints->pInvokePolymorphic = art_quick_invoke_polymorphic; + qpoints->pInvokeCustom = art_quick_invoke_custom; // Thread qpoints->pTestSuspend = art_quick_test_suspend; diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h index 3a8faca11d..415a158326 100644 --- a/runtime/entrypoints/quick/quick_entrypoints_list.h +++ b/runtime/entrypoints/quick/quick_entrypoints_list.h @@ -134,6 +134,7 @@ V(InvokeSuperTrampolineWithAccessCheck, void, uint32_t, void*) \ V(InvokeVirtualTrampolineWithAccessCheck, void, uint32_t, void*) \ V(InvokePolymorphic, void, uint32_t, void*) \ + V(InvokeCustom, void, uint32_t, void*) \ \ V(TestSuspend, void, void) \ \ diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index d2b8a98de2..2b1623ac5a 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -35,6 +35,7 @@ #include "index_bss_mapping.h" #include "instrumentation.h" #include "interpreter/interpreter.h" +#include "interpreter/interpreter_common.h" #include "interpreter/shadow_frame-inl.h" #include "jit/jit.h" #include "linear_alloc.h" @@ -2843,4 +2844,62 @@ extern "C" uint64_t artInvokePolymorphic(mirror::Object* raw_receiver, Thread* s return result.GetJ(); } +// Returns uint64_t representing raw bits from JValue. +extern "C" uint64_t artInvokeCustom(uint32_t call_site_idx, Thread* self, ArtMethod** sp) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); + DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs)); + + // invoke-custom is effectively a static call (no receiver). + static constexpr bool kMethodIsStatic = true; + + // Start new JNI local reference state + JNIEnvExt* env = self->GetJniEnv(); + ScopedObjectAccessUnchecked soa(env); + ScopedJniEnvLocalRefState env_state(env); + + const char* old_cause = self->StartAssertNoThreadSuspension("Making stack arguments safe."); + + // From the instruction, get the |callsite_shorty| and expose arguments on the stack to the GC. + ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp); + uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp); + const DexFile* dex_file = caller_method->GetDexFile(); + const dex::ProtoIndex proto_idx(dex_file->GetProtoIndexForCallSite(call_site_idx)); + const char* shorty = caller_method->GetDexFile()->GetShorty(proto_idx); + const uint32_t shorty_len = strlen(shorty); + + // Construct the shadow frame placing arguments consecutively from |first_arg|. + const size_t first_arg = 0; + const size_t num_vregs = ArtMethod::NumArgRegisters(shorty); + ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr = + CREATE_SHADOW_FRAME(num_vregs, /* link */ nullptr, caller_method, dex_pc); + ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get(); + ScopedStackedShadowFramePusher + frame_pusher(self, shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction); + BuildQuickShadowFrameVisitor shadow_frame_builder(sp, + kMethodIsStatic, + shorty, + shorty_len, + shadow_frame, + first_arg); + shadow_frame_builder.VisitArguments(); + + // Push a transition back into managed code onto the linked list in thread. + ManagedStack fragment; + self->PushManagedStackFragment(&fragment); + self->EndAssertNoThreadSuspension(old_cause); + + // Perform the invoke-custom operation. + RangeInstructionOperands operands(first_arg, num_vregs); + JValue result; + bool success = + interpreter::DoInvokeCustom(self, *shadow_frame, call_site_idx, &operands, &result); + DCHECK(success || self->IsExceptionPending()); + + // Pop transition record. + self->PopManagedStackFragment(fragment); + + return result.GetJ(); +} + } // namespace art diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index 1337cd5fb2..dda3ddeb76 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -287,8 +287,8 @@ class EntrypointsOrderTest : public CommonRuntimeTest { pInvokeVirtualTrampolineWithAccessCheck, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokeVirtualTrampolineWithAccessCheck, pInvokePolymorphic, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokePolymorphic, - pTestSuspend, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokePolymorphic, pInvokeCustom, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokeCustom, pTestSuspend, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pTestSuspend, pDeliverException, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pDeliverException, pThrowArrayBounds, sizeof(void*)); diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 27f761a144..92d4731480 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -1164,7 +1164,7 @@ static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self, ShadowFrame& shadow_frame, uint32_t call_site_idx) REQUIRES_SHARED(Locks::mutator_lock_) { - StackHandleScope<7> hs(self); + StackHandleScope<5> hs(self); // There are three mandatory arguments expected from the call site // value array in the DEX file: the bootstrap method handle, the // method name to pass to the bootstrap method, and the method type @@ -1358,75 +1358,80 @@ static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self, } // Check the call site target is not null as we're going to invoke it. - Handle<mirror::CallSite> call_site = - hs.NewHandle(ObjPtr<mirror::CallSite>::DownCast(ObjPtr<mirror::Object>(result.GetL()))); - Handle<mirror::MethodHandle> target = hs.NewHandle(call_site->GetTarget()); - if (UNLIKELY(target.IsNull())) { + ObjPtr<mirror::CallSite> call_site = + ObjPtr<mirror::CallSite>::DownCast(ObjPtr<mirror::Object>(result.GetL())); + ObjPtr<mirror::MethodHandle> target = call_site->GetTarget(); + if (UNLIKELY(target == nullptr)) { ThrowClassCastException("Bootstrap method returned a CallSite with a null target"); return nullptr; } - return call_site.Get(); + return call_site; } -template<bool is_range> +namespace { + +ObjPtr<mirror::CallSite> DoResolveCallSite(Thread* self, + ShadowFrame& shadow_frame, + uint32_t call_site_idx) + REQUIRES_SHARED(Locks::mutator_lock_) { + StackHandleScope<1> hs(self); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(shadow_frame.GetMethod()->GetDexCache())); + + // Get the call site from the DexCache if present. + ObjPtr<mirror::CallSite> call_site = dex_cache->GetResolvedCallSite(call_site_idx); + if (LIKELY(call_site != nullptr)) { + return call_site; + } + + // Invoke the bootstrap method to get a candidate call site. + call_site = InvokeBootstrapMethod(self, shadow_frame, call_site_idx); + if (UNLIKELY(call_site == nullptr)) { + if (!self->GetException()->IsError()) { + // Use a BootstrapMethodError if the exception is not an instance of java.lang.Error. + ThrowWrappedBootstrapMethodError("Exception from call site #%u bootstrap method", + call_site_idx); + } + return nullptr; + } + + // Attempt to place the candidate call site into the DexCache, return the winning call site. + return dex_cache->SetResolvedCallSite(call_site_idx, call_site); +} + +} // namespace + bool DoInvokeCustom(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) - REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t call_site_idx, + const InstructionOperands* operands, + JValue* result) { // Make sure to check for async exceptions if (UNLIKELY(self->ObserveAsyncException())) { return false; } + // invoke-custom is not supported in transactions. In transactions // there is a limited set of types supported. invoke-custom allows // running arbitrary code and instantiating arbitrary types. CHECK(!Runtime::Current()->IsActiveTransaction()); - StackHandleScope<4> hs(self); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(shadow_frame.GetMethod()->GetDexCache())); - const uint32_t call_site_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); - MutableHandle<mirror::CallSite> - call_site(hs.NewHandle(dex_cache->GetResolvedCallSite(call_site_idx))); + + ObjPtr<mirror::CallSite> call_site = DoResolveCallSite(self, shadow_frame, call_site_idx); if (call_site.IsNull()) { - call_site.Assign(InvokeBootstrapMethod(self, shadow_frame, call_site_idx)); - if (UNLIKELY(call_site.IsNull())) { - CHECK(self->IsExceptionPending()); - if (!self->GetException()->IsError()) { - // Use a BootstrapMethodError if the exception is not an instance of java.lang.Error. - ThrowWrappedBootstrapMethodError("Exception from call site #%u bootstrap method", - call_site_idx); - } - result->SetJ(0); - return false; - } - mirror::CallSite* winning_call_site = - dex_cache->SetResolvedCallSite(call_site_idx, call_site.Get()); - call_site.Assign(winning_call_site); + DCHECK(self->IsExceptionPending()); + return false; } + StackHandleScope<2> hs(self); Handle<mirror::MethodHandle> target = hs.NewHandle(call_site->GetTarget()); Handle<mirror::MethodType> target_method_type = hs.NewHandle(target->GetMethodType()); - DCHECK_EQ(static_cast<size_t>(inst->VRegA()), target_method_type->NumberOfVRegs()); - if (is_range) { - RangeInstructionOperands operands(inst->VRegC_3rc(), inst->VRegA_3rc()); - return MethodHandleInvokeExact(self, - shadow_frame, - target, - target_method_type, - &operands, - result); - } else { - uint32_t args[Instruction::kMaxVarArgRegs]; - inst->GetVarArgs(args, inst_data); - VarArgsInstructionOperands operands(args, inst->VRegA_35c()); - return MethodHandleInvokeExact(self, - shadow_frame, - target, - target_method_type, - &operands, - result); - } + DCHECK_EQ(operands->GetNumberOfOperands(), target_method_type->NumberOfVRegs()) + << " call_site_idx" << call_site_idx; + return MethodHandleInvokeExact(self, + shadow_frame, + target, + target_method_type, + operands, + result); } // Assign register 'src_reg' from shadow_frame to register 'dest_reg' into new_shadow_frame. @@ -1847,16 +1852,6 @@ EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false); EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true); #undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL -// Explicit DoInvokeCustom template function declarations. -#define EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(_is_range) \ - template REQUIRES_SHARED(Locks::mutator_lock_) \ - bool DoInvokeCustom<_is_range>( \ - Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, \ - uint16_t inst_data, JValue* result) -EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(false); -EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(true); -#undef EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL - // Explicit DoFilledNewArray template function declarations. #define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check, _transaction_active) \ template REQUIRES_SHARED(Locks::mutator_lock_) \ diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 60bf50546f..b324b4c99d 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -242,7 +242,15 @@ bool DoInvokePolymorphic(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data, - JValue* result); + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_); + +bool DoInvokeCustom(Thread* self, + ShadowFrame& shadow_frame, + uint32_t call_site_idx, + const InstructionOperands* operands, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_); // Performs a custom invoke (invoke-custom/invoke-custom-range). template<bool is_range> @@ -250,7 +258,19 @@ bool DoInvokeCustom(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data, - JValue* result); + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + const uint32_t call_site_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); + if (is_range) { + RangeInstructionOperands operands(inst->VRegC_3rc(), inst->VRegA_3rc()); + return DoInvokeCustom(self, shadow_frame, call_site_idx, &operands, result); + } else { + uint32_t args[Instruction::kMaxVarArgRegs]; + inst->GetVarArgs(args, inst_data); + VarArgsInstructionOperands operands(args, inst->VRegA_35c()); + return DoInvokeCustom(self, shadow_frame, call_site_idx, &operands, result); + } +} // Handles invoke-virtual-quick and invoke-virtual-quick-range instructions. // Returns true on success, otherwise throws an exception and returns false. diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index 96778aa98d..e64a325024 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -157,7 +157,8 @@ inline CallSite* DexCache::GetResolvedCallSite(uint32_t call_site_idx) { return ref.load(std::memory_order_seq_cst).Read(); } -inline CallSite* DexCache::SetResolvedCallSite(uint32_t call_site_idx, CallSite* call_site) { +inline ObjPtr<CallSite> DexCache::SetResolvedCallSite(uint32_t call_site_idx, + ObjPtr<CallSite> call_site) { DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds()); diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index bb86004a90..941248edf7 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -320,8 +320,8 @@ class MANAGED DexCache FINAL : public Object { // because multiple threads can invoke the bootstrap method each // producing a call site, but the method handle invocation on the // call site must be on a common agreed value. - CallSite* SetResolvedCallSite(uint32_t call_site_idx, CallSite* resolved) WARN_UNUSED - REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr<CallSite> SetResolvedCallSite(uint32_t call_site_idx, ObjPtr<CallSite> resolved) + REQUIRES_SHARED(Locks::mutator_lock_) WARN_UNUSED; StringDexCacheType* GetStrings() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldPtr64<StringDexCacheType*>(StringsOffset()); diff --git a/runtime/oat.h b/runtime/oat.h index 72eb27d69e..40f4edd8bb 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: Rewrite TypeLookupTable. - static constexpr uint8_t kOatVersion[] = { '1', '4', '7', '\0' }; + // Last oat version changed reason: compiler support invoke-custom + static constexpr uint8_t kOatVersion[] = { '1', '4', '8', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index a62271df77..61ddded9f2 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3053,10 +3053,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { // Step 2. Check the register arguments correspond to the expected arguments for the // method handle produced by step 1. The dex file verifier has checked ranges for // the first three arguments and CheckCallSite has checked the method handle type. - CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx)); - it.Next(); // Skip to name. - it.Next(); // Skip to method type of the method handle - const dex::ProtoIndex proto_idx(it.GetJavaValue().c); + const dex::ProtoIndex proto_idx = dex_file_->GetProtoIndexForCallSite(call_site_idx); const DexFile::ProtoId& proto_id = dex_file_->GetProtoId(proto_idx); DexFileParameterIterator param_it(*dex_file_, proto_id); // Treat method as static as it has yet to be determined. @@ -3072,8 +3069,6 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_)); } just_set_result = true; - // TODO: Add compiler support for invoke-custom (b/35337872). - Fail(VERIFY_ERROR_FORCE_INTERPRETER); break; } case Instruction::NEG_INT: @@ -4007,24 +4002,41 @@ bool MethodVerifier::CheckCallSite(uint32_t call_site_idx) { CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx)); // Check essential arguments are provided. The dex file verifier has verified indicies of the // main values (method handle, name, method_type). - if (it.Size() < 3) { + static const size_t kRequiredArguments = 3; + if (it.Size() < kRequiredArguments) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx << " has too few arguments: " - << it.Size() << " < 3"; + << it.Size() << " < " << kRequiredArguments; return false; } - // Get and check the first argument: the method handle (index range - // checked by the dex file verifier). - uint32_t method_handle_idx = static_cast<uint32_t>(it.GetJavaValue().i); - if (method_handle_idx > dex_file_->NumMethodHandles()) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site id #" << call_site_idx - << " method handle index invalid " << method_handle_idx - << " >= " << dex_file_->NumMethodHandles(); - return false; + std::pair<const EncodedArrayValueIterator::ValueType, size_t> type_and_max[kRequiredArguments] = + { { EncodedArrayValueIterator::ValueType::kMethodHandle, dex_file_->NumMethodHandles() }, + { EncodedArrayValueIterator::ValueType::kString, dex_file_->NumStringIds() }, + { EncodedArrayValueIterator::ValueType::kMethodType, dex_file_->NumProtoIds() } + }; + uint32_t index[kRequiredArguments]; + + // Check arguments have expected types and are within permitted ranges. + for (size_t i = 0; i < kRequiredArguments; ++i) { + if (it.GetValueType() != type_and_max[i].first) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site id #" << call_site_idx + << " argument " << i << " has wrong type " + << it.GetValueType() << "!=" << type_and_max[i].first; + return false; + } + index[i] = static_cast<uint32_t>(it.GetJavaValue().i); + if (index[i] >= type_and_max[i].second) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site id #" << call_site_idx + << " argument " << i << " bad index " + << index[i] << " >= " << type_and_max[i].second; + return false; + } + it.Next(); } - const DexFile::MethodHandleItem& mh = dex_file_->GetMethodHandle(method_handle_idx); + // Check method handle kind is valid. + const DexFile::MethodHandleItem& mh = dex_file_->GetMethodHandle(index[0]); if (mh.method_handle_type_ != static_cast<uint16_t>(DexFile::MethodHandleType::kInvokeStatic)) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx << " argument 0 method handle type is not InvokeStatic: " diff --git a/test/952-invoke-custom/src/TestReturnValues.java b/test/952-invoke-custom/src/TestReturnValues.java new file mode 100644 index 0000000000..8450a44310 --- /dev/null +++ b/test/952-invoke-custom/src/TestReturnValues.java @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2018 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. + */ + +import annotations.BootstrapMethod; +import annotations.CalledByIndy; +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +class TestReturnValues extends TestBase { + static CallSite bsm(MethodHandles.Lookup lookup, String name, MethodType methodType) + throws Throwable { + MethodHandle mh = lookup.findStatic(TestReturnValues.class, name, methodType); + return new ConstantCallSite(mh); + } + + // + // Methods that pass through a single argument. + // Used to check return path. + // + static byte passThrough(byte value) { + return value; + } + + static char passThrough(char value) { + return value; + } + + static double passThrough(double value) { + return value; + } + + static float passThrough(float value) { + return value; + } + + static int passThrough(int value) { + return value; + } + + static Object passThrough(Object value) { + return value; + } + + static Object[] passThrough(Object[] value) { + return value; + } + + static long passThrough(long value) { + return value; + } + + static short passThrough(short value) { + return value; + } + + static void passThrough() {} + + static boolean passThrough(boolean value) { + return value; + } + + // byte + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = byte.class, + parameterTypes = {byte.class}) + private static byte passThroughCallSite(byte value) { + assertNotReached(); + return (byte) 0; + } + + // char + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = char.class, + parameterTypes = {char.class}) + private static char passThroughCallSite(char value) { + assertNotReached(); + return 'Z'; + } + + // double + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = double.class, + parameterTypes = {double.class}) + private static double passThroughCallSite(double value) { + assertNotReached(); + return Double.NaN; + } + + // float + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = float.class, + parameterTypes = {float.class}) + private static float passThroughCallSite(float value) { + assertNotReached(); + return Float.NaN; + } + + // int + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = int.class, + parameterTypes = {int.class}) + private static int passThroughCallSite(int value) { + assertNotReached(); + return 0; + } + + // long + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = long.class, + parameterTypes = {long.class}) + private static long passThroughCallSite(long value) { + assertNotReached(); + return Long.MIN_VALUE; + } + + // Object + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = Object.class, + parameterTypes = {Object.class}) + private static Object passThroughCallSite(Object value) { + assertNotReached(); + return null; + } + + // Object[] + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = Object[].class, + parameterTypes = {Object[].class}) + private static Object[] passThroughCallSite(Object[] value) { + assertNotReached(); + return null; + } + + // short + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = short.class, + parameterTypes = {short.class}) + private static short passThroughCallSite(short value) { + assertNotReached(); + return (short) 0; + } + + // void + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = void.class, + parameterTypes = {}) + private static void passThroughCallSite() { + assertNotReached(); + } + + // boolean + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = boolean.class, + parameterTypes = {boolean.class}) + private static boolean passThroughCallSite(boolean value) { + assertNotReached(); + return false; + } + + private static void testByteReturnValues() { + byte[] values = {Byte.MIN_VALUE, Byte.MAX_VALUE}; + for (byte value : values) { + assertEquals(value, (byte) passThroughCallSite(value)); + } + } + + private static void testCharReturnValues() { + char[] values = { + Character.MIN_VALUE, + Character.MAX_HIGH_SURROGATE, + Character.MAX_LOW_SURROGATE, + Character.MAX_VALUE + }; + for (char value : values) { + assertEquals(value, (char) passThroughCallSite(value)); + } + } + + private static void testDoubleReturnValues() { + double[] values = { + Double.MIN_VALUE, + Double.MIN_NORMAL, + Double.NaN, + Double.POSITIVE_INFINITY, + Double.NEGATIVE_INFINITY, + Double.MAX_VALUE + }; + for (double value : values) { + assertEquals(value, (double) passThroughCallSite(value)); + } + } + + private static void testFloatReturnValues() { + float[] values = { + Float.MIN_VALUE, + Float.MIN_NORMAL, + Float.NaN, + Float.POSITIVE_INFINITY, + Float.NEGATIVE_INFINITY, + Float.MAX_VALUE + }; + for (float value : values) { + assertEquals(value, (float) passThroughCallSite(value)); + } + } + + private static void testIntReturnValues() { + int[] values = {Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.SIZE, -Integer.SIZE}; + for (int value : values) { + assertEquals(value, (int) passThroughCallSite(value)); + } + } + + private static void testLongReturnValues() { + long[] values = {Long.MIN_VALUE, Long.MAX_VALUE, (long) Long.SIZE, (long) -Long.SIZE}; + for (long value : values) { + assertEquals(value, (long) passThroughCallSite(value)); + } + } + + private static void testObjectReturnValues() { + Object[] values = {null, "abc", Integer.valueOf(123)}; + for (Object value : values) { + assertEquals(value, (Object) passThroughCallSite(value)); + } + + Object[] otherValues = (Object[]) passThroughCallSite(values); + assertEquals(values.length, otherValues.length); + for (int i = 0; i < otherValues.length; ++i) { + assertEquals(values[i], otherValues[i]); + } + } + + private static void testShortReturnValues() { + short[] values = { + Short.MIN_VALUE, Short.MAX_VALUE, (short) Short.SIZE, (short) -Short.SIZE + }; + for (short value : values) { + assertEquals(value, (short) passThroughCallSite(value)); + } + } + + private static void testVoidReturnValues() { + long l = Long.MIN_VALUE; + double d = Double.MIN_VALUE; + passThroughCallSite(); // Initializes call site + assertEquals(Long.MIN_VALUE, l); + assertEquals(Double.MIN_VALUE, d); + + l = Long.MAX_VALUE; + d = Double.MAX_VALUE; + passThroughCallSite(); // re-uses existing call site + assertEquals(Long.MAX_VALUE, l); + assertEquals(Double.MAX_VALUE, d); + } + + private static void testBooleanReturnValues() { + boolean[] values = {true, false, true, false, false}; + for (boolean value : values) { + assertEquals(value, (boolean) passThroughCallSite(value)); + } + } + + public static void test() { + System.out.println(TestReturnValues.class.getName()); + // Two passes here - the first is for the call site creation and invoke path, the second + // for the lookup and invoke path. + for (int pass = 0; pass < 2; ++pass) { + testByteReturnValues(); // B + testCharReturnValues(); // C + testDoubleReturnValues(); // D + testFloatReturnValues(); // F + testIntReturnValues(); // I + testLongReturnValues(); // J + testObjectReturnValues(); // L + testShortReturnValues(); // S + testVoidReturnValues(); // S + testBooleanReturnValues(); // Z + } + } +} |