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
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 9f2346d..b3feb78 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -544,6 +544,7 @@
case kVirtual:
case kInterface:
case kPolymorphic:
+ case kCustom:
LOG(FATAL) << "Unexpected invoke type: " << invoke->GetInvokeType();
UNREACHABLE();
}
@@ -572,6 +573,7 @@
entrypoint = kQuickInvokeInterfaceTrampolineWithAccessCheck;
break;
case kPolymorphic:
+ case kCustom:
LOG(FATAL) << "Unexpected invoke type: " << invoke->GetInvokeType();
UNREACHABLE();
}
@@ -586,6 +588,12 @@
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 a340446..b3c29aa 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -542,10 +542,13 @@
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 6f173e1..5f0533c 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -4695,6 +4695,15 @@
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 859e159..91c1315 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -3742,6 +3742,15 @@
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 @@
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 @@
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 @@
return;
}
GenerateSuspendCheck(instruction, nullptr);
- codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 12);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 13);
}
void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction,
@@ -7437,7 +7446,7 @@
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 @@
} else {
__ Bind(slow_path->GetExitLabel());
}
- codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 14);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 15);
}
}
@@ -7732,7 +7741,7 @@
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 @@
__ 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 @@
} else {
CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
}
- codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 17);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 18);
}
void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) {
@@ -8883,7 +8892,7 @@
// 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 @@
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 @@
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 @@
// 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 @@
// 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 8be84a1..507db36 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -7795,6 +7795,14 @@
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 cd9e0e5..08a6512 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -5908,6 +5908,14 @@
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 9e31538..9f42ac7 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -2311,6 +2311,14 @@
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 f739704..05194b1 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -2501,6 +2501,14 @@
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 6900cd8..7dd756e 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -460,9 +460,10 @@
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 24dc2ee..be8f2b1 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -449,11 +449,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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::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 @@
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::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 @@
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 @@
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 @@
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 2218a69..af7092a 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -173,12 +173,17 @@
// 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 @@
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 056f533..4ffde7b 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -142,6 +142,7 @@
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 3fd5b6b..2037879 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1380,6 +1380,7 @@
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 @@
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 @@
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 19c405e..e76e98a 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -153,7 +153,7 @@
" 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 d0a6eb9..2bc286a 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -497,7 +497,7 @@
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 f570158..f1f8960 100644
--- a/libdexfile/dex/dex_file.cc
+++ b/libdexfile/dex/dex_file.cc
@@ -605,6 +605,15 @@
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 ed21980..a8cd187 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -737,6 +737,8 @@
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 9b3af67..1740c07 100644
--- a/libdexfile/dex/invoke_type.h
+++ b/libdexfile/dex/invoke_type.h
@@ -28,7 +28,8 @@
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 2ef30c0..ccff9f6 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -2693,10 +2693,24 @@
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 5e540dd..80d5fce 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -2855,6 +2855,19 @@
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 5d6e410..2b69c17 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -410,6 +410,9 @@
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 c9bdc96..508a201 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -3270,3 +3270,24 @@
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 1800056..258acdd 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -3070,4 +3070,25 @@
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 e392198..e1b3df8 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -2455,6 +2455,28 @@
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 3f5d4f6..9980966 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 @@
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 70ff40d..ffbff88 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -73,7 +73,7 @@
// 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 1804d9e..938489b 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_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 3f66045..5dcece4 100644
--- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -106,6 +106,7 @@
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 3a8faca..415a158 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 d2b8a98..2b1623a 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 @@
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 1337cd5..dda3dde 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -287,8 +287,8 @@
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 27f761a..92d4731 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -1164,7 +1164,7 @@
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 @@
}
// 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(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 60bf505..b324b4c 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -242,7 +242,15 @@
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 @@
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 96778aa..e64a325 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -157,7 +157,8 @@
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 bb86004..941248e 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -320,8 +320,8 @@
// 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 72eb27d..40f4edd 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
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 a62271d..61ddded 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3053,10 +3053,7 @@
// 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 @@
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 @@
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 0000000..8450a44
--- /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
+ }
+ }
+}