Merge "ART: More lenient lock merging in the verifier"
diff --git a/.gitignore b/.gitignore
index c4cf98b..4e806c6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
JIT_ART
+**/__pycache__/**
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 52df7de..f34b5ed 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -21,6 +21,7 @@
#include "utils.h"
#include <numeric>
#include "gtest/gtest.h"
+#include "runtime/experimental_flags.h"
#define EXPECT_NULL(expected) EXPECT_EQ(reinterpret_cast<const void*>(expected), \
reinterpret_cast<void*>(nullptr));
@@ -529,22 +530,32 @@
}
} // TEST_F
-/* -X[no]experimental-lambdas */
-TEST_F(CmdlineParserTest, TestExperimentalLambdas) {
+/* -Xexperimental:_ */
+TEST_F(CmdlineParserTest, TestExperimentalFlags) {
// Off by default
- EXPECT_SINGLE_PARSE_DEFAULT_VALUE(false,
+ EXPECT_SINGLE_PARSE_DEFAULT_VALUE(ExperimentalFlags::kNone,
"",
- M::ExperimentalLambdas);
+ M::Experimental);
// Disabled explicitly
- EXPECT_SINGLE_PARSE_VALUE(false,
- "-Xnoexperimental-lambdas",
- M::ExperimentalLambdas);
+ EXPECT_SINGLE_PARSE_VALUE(ExperimentalFlags::kNone,
+ "-Xexperimental:none",
+ M::Experimental);
// Enabled explicitly
- EXPECT_SINGLE_PARSE_VALUE(true,
- "-Xexperimental-lambdas",
- M::ExperimentalLambdas);
+ EXPECT_SINGLE_PARSE_VALUE(ExperimentalFlags::kLambdas,
+ "-Xexperimental:lambdas",
+ M::Experimental);
+ // Enabled explicitly
+ EXPECT_SINGLE_PARSE_VALUE(ExperimentalFlags::kDefaultMethods,
+ "-Xexperimental:default-methods",
+ M::Experimental);
+
+ // Enabled both
+ EXPECT_SINGLE_PARSE_VALUE(ExperimentalFlags::kDefaultMethods | ExperimentalFlags::kLambdas,
+ "-Xexperimental:default-methods "
+ "-Xexperimental:lambdas",
+ M::Experimental);
}
// -Xverify:_
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index a57b619..c594adb 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -28,6 +28,7 @@
#include "jdwp/jdwp.h"
#include "runtime/base/logging.h"
#include "runtime/base/time_utils.h"
+#include "runtime/experimental_flags.h"
#include "gc/collector_type.h"
#include "gc/space/large_object_space.h"
#include "profiler_options.h"
@@ -838,6 +839,23 @@
static constexpr bool kCanParseBlankless = true;
};
+template<>
+struct CmdlineType<ExperimentalFlags> : CmdlineTypeParser<ExperimentalFlags> {
+ Result ParseAndAppend(const std::string& option, ExperimentalFlags& existing) {
+ if (option == "none") {
+ existing = existing | ExperimentalFlags::kNone;
+ } else if (option == "lambdas") {
+ existing = existing | ExperimentalFlags::kLambdas;
+ } else if (option == "default-methods") {
+ existing = existing | ExperimentalFlags::kDefaultMethods;
+ } else {
+ return Result::Failure(std::string("Unknown option '") + option + "'");
+ }
+ return Result::SuccessNoValue();
+ }
+
+ static const char* Name() { return "ExperimentalFlags"; }
+};
} // namespace art
#endif // ART_CMDLINE_CMDLINE_TYPES_H_
diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc
index 1cd742a..c2fe553 100644
--- a/compiler/dex/quick/quick_compiler.cc
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -37,6 +37,7 @@
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
#include "elf_writer_quick.h"
+#include "experimental_flags.h"
#include "jni/quick/jni_compiler.h"
#include "mir_to_lir.h"
#include "mirror/object.h"
@@ -523,7 +524,8 @@
// All opcodes are supported no matter what. Usually not the case
// since experimental opcodes are not implemented in the quick compiler.
return true;
- } else if (LIKELY(!Runtime::Current()->AreExperimentalLambdasEnabled())) {
+ } else if (LIKELY(!Runtime::Current()->
+ AreExperimentalFlagsEnabled(ExperimentalFlags::kLambdas))) {
// Experimental opcodes are disabled.
//
// If all unsupported opcodes are experimental we don't need to do scanning.
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 2897006..6a743eb 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -41,6 +41,7 @@
#include "driver/dex_compilation_unit.h"
#include "gc_map_builder.h"
#include "graph_visualizer.h"
+#include "intrinsics.h"
#include "leb128.h"
#include "mapping_table.h"
#include "mirror/array-inl.h"
@@ -1381,4 +1382,57 @@
}
}
+void CodeGenerator::CreateSystemArrayCopyLocationSummary(HInvoke* invoke) {
+ // Check to see if we have known failures that will cause us to have to bail out
+ // to the runtime, and just generate the runtime call directly.
+ HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
+ HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
+
+ // The positions must be non-negative.
+ if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
+ (dest_pos != nullptr && dest_pos->GetValue() < 0)) {
+ // We will have to fail anyways.
+ return;
+ }
+
+ // The length must be >= 0.
+ HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
+ if (length != nullptr) {
+ int32_t len = length->GetValue();
+ if (len < 0) {
+ // Just call as normal.
+ return;
+ }
+ }
+
+ SystemArrayCopyOptimizations optimizations(invoke);
+
+ if (optimizations.GetDestinationIsSource()) {
+ if (src_pos != nullptr && dest_pos != nullptr && src_pos->GetValue() < dest_pos->GetValue()) {
+ // We only support backward copying if source and destination are the same.
+ return;
+ }
+ }
+
+ if (optimizations.GetDestinationIsPrimitiveArray() || optimizations.GetSourceIsPrimitiveArray()) {
+ // We currently don't intrinsify primitive copying.
+ return;
+ }
+
+ ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
+ LocationSummary* locations = new (allocator) LocationSummary(invoke,
+ LocationSummary::kCallOnSlowPath,
+ kIntrinsified);
+ // arraycopy(Object src, int src_pos, Object dest, int dest_pos, int length).
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetInAt(3, Location::RegisterOrConstant(invoke->InputAt(3)));
+ locations->SetInAt(4, Location::RegisterOrConstant(invoke->InputAt(4)));
+
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+}
+
} // namespace art
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index acce5b3..b04dfc0 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -421,6 +421,8 @@
Location runtime_type_index_location,
Location runtime_return_location);
+ static void CreateSystemArrayCopyLocationSummary(HInvoke* invoke);
+
void SetDisassemblyInformation(DisassemblyInformation* info) { disasm_info_ = info; }
DisassemblyInformation* GetDisassemblyInformation() const { return disasm_info_; }
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index d172fba..8c1820b 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1645,6 +1645,7 @@
DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck());
IntrinsicLocationsBuilderARM intrinsic(GetGraph()->GetArena(),
+ codegen_->GetAssembler(),
codegen_->GetInstructionSetFeatures());
if (intrinsic.TryDispatch(invoke)) {
return;
@@ -1684,6 +1685,7 @@
void LocationsBuilderARM::VisitInvokeVirtual(HInvokeVirtual* invoke) {
IntrinsicLocationsBuilderARM intrinsic(GetGraph()->GetArena(),
+ codegen_->GetAssembler(),
codegen_->GetInstructionSetFeatures());
if (intrinsic.TryDispatch(invoke)) {
return;
@@ -3512,6 +3514,47 @@
}
}
+Location LocationsBuilderARM::ArmEncodableConstantOrRegister(HInstruction* constant,
+ Opcode opcode) {
+ DCHECK(!Primitive::IsFloatingPointType(constant->GetType()));
+ if (constant->IsConstant() &&
+ CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) {
+ return Location::ConstantLocation(constant->AsConstant());
+ }
+ return Location::RequiresRegister();
+}
+
+bool LocationsBuilderARM::CanEncodeConstantAsImmediate(HConstant* input_cst,
+ Opcode opcode) {
+ uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst));
+ if (Primitive::Is64BitType(input_cst->GetType())) {
+ return CanEncodeConstantAsImmediate(Low32Bits(value), opcode) &&
+ CanEncodeConstantAsImmediate(High32Bits(value), opcode);
+ } else {
+ return CanEncodeConstantAsImmediate(Low32Bits(value), opcode);
+ }
+}
+
+bool LocationsBuilderARM::CanEncodeConstantAsImmediate(uint32_t value, Opcode opcode) {
+ ShifterOperand so;
+ ArmAssembler* assembler = codegen_->GetAssembler();
+ if (assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, opcode, value, &so)) {
+ return true;
+ }
+ Opcode neg_opcode = kNoOperand;
+ switch (opcode) {
+ case AND:
+ neg_opcode = BIC;
+ break;
+ case ORR:
+ neg_opcode = ORN;
+ break;
+ default:
+ return false;
+ }
+ return assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, neg_opcode, ~value, &so);
+}
+
void InstructionCodeGeneratorARM::HandleFieldGet(HInstruction* instruction,
const FieldInfo& field_info) {
DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
@@ -4912,17 +4955,18 @@
nullptr);
}
-void LocationsBuilderARM::VisitAnd(HAnd* instruction) { HandleBitwiseOperation(instruction); }
-void LocationsBuilderARM::VisitOr(HOr* instruction) { HandleBitwiseOperation(instruction); }
-void LocationsBuilderARM::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction); }
+void LocationsBuilderARM::VisitAnd(HAnd* instruction) { HandleBitwiseOperation(instruction, AND); }
+void LocationsBuilderARM::VisitOr(HOr* instruction) { HandleBitwiseOperation(instruction, ORR); }
+void LocationsBuilderARM::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction, EOR); }
-void LocationsBuilderARM::HandleBitwiseOperation(HBinaryOperation* instruction) {
+void LocationsBuilderARM::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
DCHECK(instruction->GetResultType() == Primitive::kPrimInt
|| instruction->GetResultType() == Primitive::kPrimLong);
+ // Note: GVN reorders commutative operations to have the constant on the right hand side.
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode));
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
@@ -4938,48 +4982,131 @@
HandleBitwiseOperation(instruction);
}
+void InstructionCodeGeneratorARM::GenerateAndConst(Register out, Register first, uint32_t value) {
+ // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier).
+ if (value == 0xffffffffu) {
+ if (out != first) {
+ __ mov(out, ShifterOperand(first));
+ }
+ return;
+ }
+ if (value == 0u) {
+ __ mov(out, ShifterOperand(0));
+ return;
+ }
+ ShifterOperand so;
+ if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, AND, value, &so)) {
+ __ and_(out, first, so);
+ } else {
+ DCHECK(__ ShifterOperandCanHold(kNoRegister, kNoRegister, BIC, ~value, &so));
+ __ bic(out, first, ShifterOperand(~value));
+ }
+}
+
+void InstructionCodeGeneratorARM::GenerateOrrConst(Register out, Register first, uint32_t value) {
+ // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier).
+ if (value == 0u) {
+ if (out != first) {
+ __ mov(out, ShifterOperand(first));
+ }
+ return;
+ }
+ if (value == 0xffffffffu) {
+ __ mvn(out, ShifterOperand(0));
+ return;
+ }
+ ShifterOperand so;
+ if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORR, value, &so)) {
+ __ orr(out, first, so);
+ } else {
+ DCHECK(__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORN, ~value, &so));
+ __ orn(out, first, ShifterOperand(~value));
+ }
+}
+
+void InstructionCodeGeneratorARM::GenerateEorConst(Register out, Register first, uint32_t value) {
+ // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier).
+ if (value == 0u) {
+ if (out != first) {
+ __ mov(out, ShifterOperand(first));
+ }
+ return;
+ }
+ __ eor(out, first, ShifterOperand(value));
+}
+
void InstructionCodeGeneratorARM::HandleBitwiseOperation(HBinaryOperation* instruction) {
LocationSummary* locations = instruction->GetLocations();
+ Location first = locations->InAt(0);
+ Location second = locations->InAt(1);
+ Location out = locations->Out();
+
+ if (second.IsConstant()) {
+ uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
+ uint32_t value_low = Low32Bits(value);
+ if (instruction->GetResultType() == Primitive::kPrimInt) {
+ Register first_reg = first.AsRegister<Register>();
+ Register out_reg = out.AsRegister<Register>();
+ if (instruction->IsAnd()) {
+ GenerateAndConst(out_reg, first_reg, value_low);
+ } else if (instruction->IsOr()) {
+ GenerateOrrConst(out_reg, first_reg, value_low);
+ } else {
+ DCHECK(instruction->IsXor());
+ GenerateEorConst(out_reg, first_reg, value_low);
+ }
+ } else {
+ DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
+ uint32_t value_high = High32Bits(value);
+ Register first_low = first.AsRegisterPairLow<Register>();
+ Register first_high = first.AsRegisterPairHigh<Register>();
+ Register out_low = out.AsRegisterPairLow<Register>();
+ Register out_high = out.AsRegisterPairHigh<Register>();
+ if (instruction->IsAnd()) {
+ GenerateAndConst(out_low, first_low, value_low);
+ GenerateAndConst(out_high, first_high, value_high);
+ } else if (instruction->IsOr()) {
+ GenerateOrrConst(out_low, first_low, value_low);
+ GenerateOrrConst(out_high, first_high, value_high);
+ } else {
+ DCHECK(instruction->IsXor());
+ GenerateEorConst(out_low, first_low, value_low);
+ GenerateEorConst(out_high, first_high, value_high);
+ }
+ }
+ return;
+ }
if (instruction->GetResultType() == Primitive::kPrimInt) {
- Register first = locations->InAt(0).AsRegister<Register>();
- Register second = locations->InAt(1).AsRegister<Register>();
- Register out = locations->Out().AsRegister<Register>();
+ Register first_reg = first.AsRegister<Register>();
+ ShifterOperand second_reg(second.AsRegister<Register>());
+ Register out_reg = out.AsRegister<Register>();
if (instruction->IsAnd()) {
- __ and_(out, first, ShifterOperand(second));
+ __ and_(out_reg, first_reg, second_reg);
} else if (instruction->IsOr()) {
- __ orr(out, first, ShifterOperand(second));
+ __ orr(out_reg, first_reg, second_reg);
} else {
DCHECK(instruction->IsXor());
- __ eor(out, first, ShifterOperand(second));
+ __ eor(out_reg, first_reg, second_reg);
}
} else {
DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
- Location first = locations->InAt(0);
- Location second = locations->InAt(1);
- Location out = locations->Out();
+ Register first_low = first.AsRegisterPairLow<Register>();
+ Register first_high = first.AsRegisterPairHigh<Register>();
+ ShifterOperand second_low(second.AsRegisterPairLow<Register>());
+ ShifterOperand second_high(second.AsRegisterPairHigh<Register>());
+ Register out_low = out.AsRegisterPairLow<Register>();
+ Register out_high = out.AsRegisterPairHigh<Register>();
if (instruction->IsAnd()) {
- __ and_(out.AsRegisterPairLow<Register>(),
- first.AsRegisterPairLow<Register>(),
- ShifterOperand(second.AsRegisterPairLow<Register>()));
- __ and_(out.AsRegisterPairHigh<Register>(),
- first.AsRegisterPairHigh<Register>(),
- ShifterOperand(second.AsRegisterPairHigh<Register>()));
+ __ and_(out_low, first_low, second_low);
+ __ and_(out_high, first_high, second_high);
} else if (instruction->IsOr()) {
- __ orr(out.AsRegisterPairLow<Register>(),
- first.AsRegisterPairLow<Register>(),
- ShifterOperand(second.AsRegisterPairLow<Register>()));
- __ orr(out.AsRegisterPairHigh<Register>(),
- first.AsRegisterPairHigh<Register>(),
- ShifterOperand(second.AsRegisterPairHigh<Register>()));
+ __ orr(out_low, first_low, second_low);
+ __ orr(out_high, first_high, second_high);
} else {
DCHECK(instruction->IsXor());
- __ eor(out.AsRegisterPairLow<Register>(),
- first.AsRegisterPairLow<Register>(),
- ShifterOperand(second.AsRegisterPairLow<Register>()));
- __ eor(out.AsRegisterPairHigh<Register>(),
- first.AsRegisterPairHigh<Register>(),
- ShifterOperand(second.AsRegisterPairHigh<Register>()));
+ __ eor(out_low, first_low, second_low);
+ __ eor(out_high, first_high, second_high);
}
}
}
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 16d1d38..6900933 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -169,11 +169,15 @@
private:
void HandleInvoke(HInvoke* invoke);
- void HandleBitwiseOperation(HBinaryOperation* operation);
+ void HandleBitwiseOperation(HBinaryOperation* operation, Opcode opcode);
void HandleShift(HBinaryOperation* operation);
void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
+ Location ArmEncodableConstantOrRegister(HInstruction* constant, Opcode opcode);
+ bool CanEncodeConstantAsImmediate(HConstant* input_cst, Opcode opcode);
+ bool CanEncodeConstantAsImmediate(uint32_t value, Opcode opcode);
+
CodeGeneratorARM* const codegen_;
InvokeDexCallingConventionVisitorARM parameter_visitor_;
@@ -205,6 +209,9 @@
// the suspend call.
void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor);
void GenerateClassInitializationCheck(SlowPathCode* slow_path, Register class_reg);
+ void GenerateAndConst(Register out, Register first, uint32_t value);
+ void GenerateOrrConst(Register out, Register first, uint32_t value);
+ void GenerateEorConst(Register out, Register first, uint32_t value);
void HandleBitwiseOperation(HBinaryOperation* operation);
void HandleShift(HBinaryOperation* operation);
void GenerateMemoryBarrier(MemBarrierKind kind);
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 5e8f9e7..7799437 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -117,7 +117,7 @@
return Location::RegisterLocation(A0);
}
Location GetReturnLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
- return Location::RegisterLocation(A0);
+ return Location::RegisterLocation(V0);
}
Location GetSetValueLocation(
Primitive::Type type ATTRIBUTE_UNUSED, bool is_instance) const OVERRIDE {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index f8be21a..b60eebf 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -521,7 +521,8 @@
move_resolver_(graph->GetArena(), this),
isa_features_(isa_features),
method_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
- relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
+ relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
// Use a fake return address register to mimic Quick.
AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister));
}
@@ -5669,6 +5670,51 @@
}
}
+void LocationsBuilderX86::VisitX86PackedSwitch(HX86PackedSwitch* switch_instr) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+
+ // Constant area pointer.
+ locations->SetInAt(1, Location::RequiresRegister());
+
+ // And the temporary we need.
+ locations->AddTemp(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorX86::VisitX86PackedSwitch(HX86PackedSwitch* switch_instr) {
+ int32_t lower_bound = switch_instr->GetStartValue();
+ int32_t num_entries = switch_instr->GetNumEntries();
+ LocationSummary* locations = switch_instr->GetLocations();
+ Register value_reg = locations->InAt(0).AsRegister<Register>();
+ HBasicBlock* default_block = switch_instr->GetDefaultBlock();
+
+ // Optimizing has a jump area.
+ Register temp_reg = locations->GetTemp(0).AsRegister<Register>();
+ Register constant_area = locations->InAt(1).AsRegister<Register>();
+
+ // Remove the bias, if needed.
+ if (lower_bound != 0) {
+ __ leal(temp_reg, Address(value_reg, -lower_bound));
+ value_reg = temp_reg;
+ }
+
+ // Is the value in range?
+ DCHECK_GE(num_entries, 1);
+ __ cmpl(value_reg, Immediate(num_entries - 1));
+ __ j(kAbove, codegen_->GetLabelOf(default_block));
+
+ // We are in the range of the table.
+ // Load (target-constant_area) from the jump table, indexing by the value.
+ __ movl(temp_reg, codegen_->LiteralCaseTable(switch_instr, constant_area, value_reg));
+
+ // Compute the actual target address by adding in constant_area.
+ __ addl(temp_reg, constant_area);
+
+ // And jump.
+ __ jmp(temp_reg);
+}
+
void LocationsBuilderX86::VisitX86ComputeBaseMethodAddress(
HX86ComputeBaseMethodAddress* insn) {
LocationSummary* locations =
@@ -5752,28 +5798,18 @@
}
}
-void CodeGeneratorX86::Finalize(CodeAllocator* allocator) {
- // Generate the constant area if needed.
- X86Assembler* assembler = GetAssembler();
- if (!assembler->IsConstantAreaEmpty()) {
- // Align to 4 byte boundary to reduce cache misses, as the data is 4 and 8
- // byte values.
- assembler->Align(4, 0);
- constant_area_start_ = assembler->CodeSize();
- assembler->AddConstantArea();
- }
-
- // And finish up.
- CodeGenerator::Finalize(allocator);
-}
-
/**
* Class to handle late fixup of offsets into constant area.
*/
class RIPFixup : public AssemblerFixup, public ArenaObject<kArenaAllocCodeGenerator> {
public:
- RIPFixup(const CodeGeneratorX86& codegen, int offset)
- : codegen_(codegen), offset_into_constant_area_(offset) {}
+ RIPFixup(CodeGeneratorX86& codegen, size_t offset)
+ : codegen_(&codegen), offset_into_constant_area_(offset) {}
+
+ protected:
+ void SetOffset(size_t offset) { offset_into_constant_area_ = offset; }
+
+ CodeGeneratorX86* codegen_;
private:
void Process(const MemoryRegion& region, int pos) OVERRIDE {
@@ -5781,19 +5817,77 @@
// last 4 bytes of the instruction.
// The value to patch is the distance from the offset in the constant area
// from the address computed by the HX86ComputeBaseMethodAddress instruction.
- int32_t constant_offset = codegen_.ConstantAreaStart() + offset_into_constant_area_;
- int32_t relative_position = constant_offset - codegen_.GetMethodAddressOffset();;
+ int32_t constant_offset = codegen_->ConstantAreaStart() + offset_into_constant_area_;
+ int32_t relative_position = constant_offset - codegen_->GetMethodAddressOffset();;
// Patch in the right value.
region.StoreUnaligned<int32_t>(pos - 4, relative_position);
}
- const CodeGeneratorX86& codegen_;
-
// Location in constant area that the fixup refers to.
- int offset_into_constant_area_;
+ int32_t offset_into_constant_area_;
};
+/**
+ * Class to handle late fixup of offsets to a jump table that will be created in the
+ * constant area.
+ */
+class JumpTableRIPFixup : public RIPFixup {
+ public:
+ JumpTableRIPFixup(CodeGeneratorX86& codegen, HX86PackedSwitch* switch_instr)
+ : RIPFixup(codegen, static_cast<size_t>(-1)), switch_instr_(switch_instr) {}
+
+ void CreateJumpTable() {
+ X86Assembler* assembler = codegen_->GetAssembler();
+
+ // Ensure that the reference to the jump table has the correct offset.
+ const int32_t offset_in_constant_table = assembler->ConstantAreaSize();
+ SetOffset(offset_in_constant_table);
+
+ // The label values in the jump table are computed relative to the
+ // instruction addressing the constant area.
+ const int32_t relative_offset = codegen_->GetMethodAddressOffset();
+
+ // Populate the jump table with the correct values for the jump table.
+ int32_t num_entries = switch_instr_->GetNumEntries();
+ HBasicBlock* block = switch_instr_->GetBlock();
+ const ArenaVector<HBasicBlock*>& successors = block->GetSuccessors();
+ // The value that we want is the target offset - the position of the table.
+ for (int32_t i = 0; i < num_entries; i++) {
+ HBasicBlock* b = successors[i];
+ Label* l = codegen_->GetLabelOf(b);
+ DCHECK(l->IsBound());
+ int32_t offset_to_block = l->Position() - relative_offset;
+ assembler->AppendInt32(offset_to_block);
+ }
+ }
+
+ private:
+ const HX86PackedSwitch* switch_instr_;
+};
+
+void CodeGeneratorX86::Finalize(CodeAllocator* allocator) {
+ // Generate the constant area if needed.
+ X86Assembler* assembler = GetAssembler();
+ if (!assembler->IsConstantAreaEmpty() || !fixups_to_jump_tables_.empty()) {
+ // Align to 4 byte boundary to reduce cache misses, as the data is 4 and 8
+ // byte values.
+ assembler->Align(4, 0);
+ constant_area_start_ = assembler->CodeSize();
+
+ // Populate any jump tables.
+ for (auto jump_table : fixups_to_jump_tables_) {
+ jump_table->CreateJumpTable();
+ }
+
+ // And now add the constant area to the generated code.
+ assembler->AddConstantArea();
+ }
+
+ // And finish up.
+ CodeGenerator::Finalize(allocator);
+}
+
Address CodeGeneratorX86::LiteralDoubleAddress(double v, Register reg) {
AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddDouble(v));
return Address(reg, kDummy32BitOffset, fixup);
@@ -5814,6 +5908,20 @@
return Address(reg, kDummy32BitOffset, fixup);
}
+Address CodeGeneratorX86::LiteralCaseTable(HX86PackedSwitch* switch_instr,
+ Register reg,
+ Register value) {
+ // Create a fixup to be used to create and address the jump table.
+ JumpTableRIPFixup* table_fixup =
+ new (GetGraph()->GetArena()) JumpTableRIPFixup(*this, switch_instr);
+
+ // We have to populate the jump tables.
+ fixups_to_jump_tables_.push_back(table_fixup);
+
+ // We want a scaled address, as we are extracting the correct offset from the table.
+ return Address(reg, value, TIMES_4, kDummy32BitOffset, table_fixup);
+}
+
/**
* Finds instructions that need the constant area base as an input.
*/
@@ -5864,6 +5972,21 @@
}
}
+ void VisitPackedSwitch(HPackedSwitch* switch_insn) OVERRIDE {
+ // We need to replace the HPackedSwitch with a HX86PackedSwitch in order to
+ // address the constant area.
+ InitializeConstantAreaPointer(switch_insn);
+ HGraph* graph = GetGraph();
+ HBasicBlock* block = switch_insn->GetBlock();
+ HX86PackedSwitch* x86_switch = new (graph->GetArena()) HX86PackedSwitch(
+ switch_insn->GetStartValue(),
+ switch_insn->GetNumEntries(),
+ switch_insn->InputAt(0),
+ base_,
+ switch_insn->GetDexPc());
+ block->ReplaceAndRemoveInstructionWith(switch_insn, x86_switch);
+ }
+
void InitializeConstantAreaPointer(HInstruction* user) {
// Ensure we only initialize the pointer once.
if (base_ != nullptr) {
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index ae2d84f..fdfc5ab 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -245,6 +245,8 @@
DISALLOW_COPY_AND_ASSIGN(InstructionCodeGeneratorX86);
};
+class JumpTableRIPFixup;
+
class CodeGeneratorX86 : public CodeGenerator {
public:
CodeGeneratorX86(HGraph* graph,
@@ -385,6 +387,8 @@
Address LiteralInt32Address(int32_t v, Register reg);
Address LiteralInt64Address(int64_t v, Register reg);
+ Address LiteralCaseTable(HX86PackedSwitch* switch_instr, Register reg, Register value);
+
void Finalize(CodeAllocator* allocator) OVERRIDE;
private:
@@ -405,6 +409,9 @@
// Used for fixups to the constant area.
int32_t constant_area_start_;
+ // Fixups for jump tables that need to be patched after the constant table is generated.
+ ArenaVector<JumpTableRIPFixup*> fixups_to_jump_tables_;
+
// If there is a HX86ComputeBaseMethodAddress instruction in the graph
// (which shall be the sole instruction of this kind), subtracting this offset
// from the value contained in the out register of this HX86ComputeBaseMethodAddress
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 21120a0..f0d9420 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -670,7 +670,8 @@
constant_area_start_(0),
method_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
- pc_rel_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
+ pc_rel_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister));
}
@@ -5322,31 +5323,43 @@
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
}
void InstructionCodeGeneratorX86_64::VisitPackedSwitch(HPackedSwitch* switch_instr) {
int32_t lower_bound = switch_instr->GetStartValue();
int32_t num_entries = switch_instr->GetNumEntries();
LocationSummary* locations = switch_instr->GetLocations();
- CpuRegister value_reg = locations->InAt(0).AsRegister<CpuRegister>();
+ CpuRegister value_reg_in = locations->InAt(0).AsRegister<CpuRegister>();
+ CpuRegister temp_reg = locations->GetTemp(0).AsRegister<CpuRegister>();
+ CpuRegister base_reg = locations->GetTemp(1).AsRegister<CpuRegister>();
+
+ // Remove the bias, if needed.
+ Register value_reg_out = value_reg_in.AsRegister();
+ if (lower_bound != 0) {
+ __ leal(temp_reg, Address(value_reg_in, -lower_bound));
+ value_reg_out = temp_reg.AsRegister();
+ }
+ CpuRegister value_reg(value_reg_out);
+
+ // Is the value in range?
HBasicBlock* default_block = switch_instr->GetDefaultBlock();
+ __ cmpl(value_reg, Immediate(num_entries - 1));
+ __ j(kAbove, codegen_->GetLabelOf(default_block));
- // Create a series of compare/jumps.
- const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
- for (int i = 0; i < num_entries; i++) {
- int32_t case_value = lower_bound + i;
- if (case_value == 0) {
- __ testl(value_reg, value_reg);
- } else {
- __ cmpl(value_reg, Immediate(case_value));
- }
- __ j(kEqual, codegen_->GetLabelOf(successors[i]));
- }
+ // We are in the range of the table.
+ // Load the address of the jump table in the constant area.
+ __ leaq(base_reg, codegen_->LiteralCaseTable(switch_instr));
- // And the default for any other value.
- if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
- __ jmp(codegen_->GetLabelOf(default_block));
- }
+ // Load the (signed) offset from the jump table.
+ __ movsxd(temp_reg, Address(base_reg, value_reg, TIMES_4, 0));
+
+ // Add the offset to the address of the table base.
+ __ addq(temp_reg, base_reg);
+
+ // And jump.
+ __ jmp(temp_reg);
}
void CodeGeneratorX86_64::Load64BitValue(CpuRegister dest, int64_t value) {
@@ -5372,15 +5385,85 @@
}
}
+/**
+ * Class to handle late fixup of offsets into constant area.
+ */
+class RIPFixup : public AssemblerFixup, public ArenaObject<kArenaAllocCodeGenerator> {
+ public:
+ RIPFixup(CodeGeneratorX86_64& codegen, size_t offset)
+ : codegen_(&codegen), offset_into_constant_area_(offset) {}
+
+ protected:
+ void SetOffset(size_t offset) { offset_into_constant_area_ = offset; }
+
+ CodeGeneratorX86_64* codegen_;
+
+ private:
+ void Process(const MemoryRegion& region, int pos) OVERRIDE {
+ // Patch the correct offset for the instruction. We use the address of the
+ // 'next' instruction, which is 'pos' (patch the 4 bytes before).
+ int32_t constant_offset = codegen_->ConstantAreaStart() + offset_into_constant_area_;
+ int32_t relative_position = constant_offset - pos;
+
+ // Patch in the right value.
+ region.StoreUnaligned<int32_t>(pos - 4, relative_position);
+ }
+
+ // Location in constant area that the fixup refers to.
+ size_t offset_into_constant_area_;
+};
+
+/**
+ t * Class to handle late fixup of offsets to a jump table that will be created in the
+ * constant area.
+ */
+class JumpTableRIPFixup : public RIPFixup {
+ public:
+ JumpTableRIPFixup(CodeGeneratorX86_64& codegen, HPackedSwitch* switch_instr)
+ : RIPFixup(codegen, -1), switch_instr_(switch_instr) {}
+
+ void CreateJumpTable() {
+ X86_64Assembler* assembler = codegen_->GetAssembler();
+
+ // Ensure that the reference to the jump table has the correct offset.
+ const int32_t offset_in_constant_table = assembler->ConstantAreaSize();
+ SetOffset(offset_in_constant_table);
+
+ // Compute the offset from the start of the function to this jump table.
+ const int32_t current_table_offset = assembler->CodeSize() + offset_in_constant_table;
+
+ // Populate the jump table with the correct values for the jump table.
+ int32_t num_entries = switch_instr_->GetNumEntries();
+ HBasicBlock* block = switch_instr_->GetBlock();
+ const ArenaVector<HBasicBlock*>& successors = block->GetSuccessors();
+ // The value that we want is the target offset - the position of the table.
+ for (int32_t i = 0; i < num_entries; i++) {
+ HBasicBlock* b = successors[i];
+ Label* l = codegen_->GetLabelOf(b);
+ DCHECK(l->IsBound());
+ int32_t offset_to_block = l->Position() - current_table_offset;
+ assembler->AppendInt32(offset_to_block);
+ }
+ }
+
+ private:
+ const HPackedSwitch* switch_instr_;
+};
+
void CodeGeneratorX86_64::Finalize(CodeAllocator* allocator) {
// Generate the constant area if needed.
X86_64Assembler* assembler = GetAssembler();
- if (!assembler->IsConstantAreaEmpty()) {
- // Align to 4 byte boundary to reduce cache misses, as the data is 4 and 8
- // byte values. If used for vectors at a later time, this will need to be
- // updated to 16 bytes with the appropriate offset.
+ if (!assembler->IsConstantAreaEmpty() || !fixups_to_jump_tables_.empty()) {
+ // Align to 4 byte boundary to reduce cache misses, as the data is 4 and 8 byte values.
assembler->Align(4, 0);
constant_area_start_ = assembler->CodeSize();
+
+ // Populate any jump tables.
+ for (auto jump_table : fixups_to_jump_tables_) {
+ jump_table->CreateJumpTable();
+ }
+
+ // And now add the constant area to the generated code.
assembler->AddConstantArea();
}
@@ -5388,31 +5471,6 @@
CodeGenerator::Finalize(allocator);
}
-/**
- * Class to handle late fixup of offsets into constant area.
- */
-class RIPFixup : public AssemblerFixup, public ArenaObject<kArenaAllocCodeGenerator> {
- public:
- RIPFixup(const CodeGeneratorX86_64& codegen, int offset)
- : codegen_(codegen), offset_into_constant_area_(offset) {}
-
- private:
- void Process(const MemoryRegion& region, int pos) OVERRIDE {
- // Patch the correct offset for the instruction. We use the address of the
- // 'next' instruction, which is 'pos' (patch the 4 bytes before).
- int constant_offset = codegen_.ConstantAreaStart() + offset_into_constant_area_;
- int relative_position = constant_offset - pos;
-
- // Patch in the right value.
- region.StoreUnaligned<int32_t>(pos - 4, relative_position);
- }
-
- const CodeGeneratorX86_64& codegen_;
-
- // Location in constant area that the fixup refers to.
- int offset_into_constant_area_;
-};
-
Address CodeGeneratorX86_64::LiteralDoubleAddress(double v) {
AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddDouble(v));
return Address::RIP(fixup);
@@ -5453,6 +5511,16 @@
GetMoveResolver()->EmitNativeCode(¶llel_move);
}
+Address CodeGeneratorX86_64::LiteralCaseTable(HPackedSwitch* switch_instr) {
+ // Create a fixup to be used to create and address the jump table.
+ JumpTableRIPFixup* table_fixup =
+ new (GetGraph()->GetArena()) JumpTableRIPFixup(*this, switch_instr);
+
+ // We have to populate the jump tables.
+ fixups_to_jump_tables_.push_back(table_fixup);
+ return Address::RIP(table_fixup);
+}
+
#undef __
} // namespace x86_64
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index d6a6a7e..dc86a48 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -234,6 +234,9 @@
DISALLOW_COPY_AND_ASSIGN(InstructionCodeGeneratorX86_64);
};
+// Class for fixups to jump tables.
+class JumpTableRIPFixup;
+
class CodeGeneratorX86_64 : public CodeGenerator {
public:
CodeGeneratorX86_64(HGraph* graph,
@@ -354,6 +357,7 @@
// Load a 64 bit value into a register in the most efficient manner.
void Load64BitValue(CpuRegister dest, int64_t value);
+ Address LiteralCaseTable(HPackedSwitch* switch_instr);
// Store a 64 bit value into a DoubleStackSlot in the most efficient manner.
void Store64BitValueToStack(Location dest, int64_t value);
@@ -391,6 +395,9 @@
// We will fix this up in the linker later to have the right value.
static constexpr int32_t kDummy32BitOffset = 256;
+ // Fixups for jump tables need to be handled specially.
+ ArenaVector<JumpTableRIPFixup*> fixups_to_jump_tables_;
+
DISALLOW_COPY_AND_ASSIGN(CodeGeneratorX86_64);
};
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 2793793..58e479a 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -1307,6 +1307,308 @@
__ Bind(slow_path->GetExitLabel());
}
+void IntrinsicLocationsBuilderARM::VisitSystemArrayCopy(HInvoke* invoke) {
+ CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke);
+ LocationSummary* locations = invoke->GetLocations();
+ if (locations == nullptr) {
+ return;
+ }
+
+ HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
+ HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
+ HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
+
+ if (src_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(src_pos->GetValue())) {
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
+ if (dest_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(dest_pos->GetValue())) {
+ locations->SetInAt(3, Location::RequiresRegister());
+ }
+ if (length != nullptr && !assembler_->ShifterOperandCanAlwaysHold(length->GetValue())) {
+ locations->SetInAt(4, Location::RequiresRegister());
+ }
+}
+
+static void CheckPosition(ArmAssembler* assembler,
+ Location pos,
+ Register input,
+ Location length,
+ SlowPathCode* slow_path,
+ Register input_len,
+ Register temp,
+ bool length_is_input_length = false) {
+ // Where is the length in the Array?
+ const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
+
+ if (pos.IsConstant()) {
+ int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
+ if (pos_const == 0) {
+ if (!length_is_input_length) {
+ // Check that length(input) >= length.
+ __ LoadFromOffset(kLoadWord, temp, input, length_offset);
+ if (length.IsConstant()) {
+ __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
+ } else {
+ __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
+ }
+ __ b(slow_path->GetEntryLabel(), LT);
+ }
+ } else {
+ // Check that length(input) >= pos.
+ __ LoadFromOffset(kLoadWord, input_len, input, length_offset);
+ __ subs(temp, input_len, ShifterOperand(pos_const));
+ __ b(slow_path->GetEntryLabel(), LT);
+
+ // Check that (length(input) - pos) >= length.
+ if (length.IsConstant()) {
+ __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
+ } else {
+ __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
+ }
+ __ b(slow_path->GetEntryLabel(), LT);
+ }
+ } else if (length_is_input_length) {
+ // The only way the copy can succeed is if pos is zero.
+ Register pos_reg = pos.AsRegister<Register>();
+ __ CompareAndBranchIfNonZero(pos_reg, slow_path->GetEntryLabel());
+ } else {
+ // Check that pos >= 0.
+ Register pos_reg = pos.AsRegister<Register>();
+ __ cmp(pos_reg, ShifterOperand(0));
+ __ b(slow_path->GetEntryLabel(), LT);
+
+ // Check that pos <= length(input).
+ __ LoadFromOffset(kLoadWord, temp, input, length_offset);
+ __ subs(temp, temp, ShifterOperand(pos_reg));
+ __ b(slow_path->GetEntryLabel(), LT);
+
+ // Check that (length(input) - pos) >= length.
+ if (length.IsConstant()) {
+ __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
+ } else {
+ __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
+ }
+ __ b(slow_path->GetEntryLabel(), LT);
+ }
+}
+
+void IntrinsicCodeGeneratorARM::VisitSystemArrayCopy(HInvoke* invoke) {
+ ArmAssembler* assembler = GetAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+ uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
+ uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
+ uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
+
+ Register src = locations->InAt(0).AsRegister<Register>();
+ Location src_pos = locations->InAt(1);
+ Register dest = locations->InAt(2).AsRegister<Register>();
+ Location dest_pos = locations->InAt(3);
+ Location length = locations->InAt(4);
+ Register temp1 = locations->GetTemp(0).AsRegister<Register>();
+ Register temp2 = locations->GetTemp(1).AsRegister<Register>();
+ Register temp3 = locations->GetTemp(2).AsRegister<Register>();
+
+ SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
+ codegen_->AddSlowPath(slow_path);
+
+ Label ok;
+ SystemArrayCopyOptimizations optimizations(invoke);
+
+ if (!optimizations.GetDestinationIsSource()) {
+ if (!src_pos.IsConstant() || !dest_pos.IsConstant()) {
+ __ cmp(src, ShifterOperand(dest));
+ }
+ }
+
+ // If source and destination are the same, we go to slow path if we need to do
+ // forward copying.
+ if (src_pos.IsConstant()) {
+ int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
+ if (dest_pos.IsConstant()) {
+ // Checked when building locations.
+ DCHECK(!optimizations.GetDestinationIsSource()
+ || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
+ } else {
+ if (!optimizations.GetDestinationIsSource()) {
+ __ b(&ok, NE);
+ }
+ __ cmp(dest_pos.AsRegister<Register>(), ShifterOperand(src_pos_constant));
+ __ b(slow_path->GetEntryLabel(), GT);
+ }
+ } else {
+ if (!optimizations.GetDestinationIsSource()) {
+ __ b(&ok, NE);
+ }
+ if (dest_pos.IsConstant()) {
+ int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
+ __ cmp(src_pos.AsRegister<Register>(), ShifterOperand(dest_pos_constant));
+ } else {
+ __ cmp(src_pos.AsRegister<Register>(), ShifterOperand(dest_pos.AsRegister<Register>()));
+ }
+ __ b(slow_path->GetEntryLabel(), LT);
+ }
+
+ __ Bind(&ok);
+
+ if (!optimizations.GetSourceIsNotNull()) {
+ // Bail out if the source is null.
+ __ CompareAndBranchIfZero(src, slow_path->GetEntryLabel());
+ }
+
+ if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
+ // Bail out if the destination is null.
+ __ CompareAndBranchIfZero(dest, slow_path->GetEntryLabel());
+ }
+
+ // If the length is negative, bail out.
+ // We have already checked in the LocationsBuilder for the constant case.
+ if (!length.IsConstant() &&
+ !optimizations.GetCountIsSourceLength() &&
+ !optimizations.GetCountIsDestinationLength()) {
+ __ cmp(length.AsRegister<Register>(), ShifterOperand(0));
+ __ b(slow_path->GetEntryLabel(), LT);
+ }
+
+ // Validity checks: source.
+ CheckPosition(assembler,
+ src_pos,
+ src,
+ length,
+ slow_path,
+ temp1,
+ temp2,
+ optimizations.GetCountIsSourceLength());
+
+ // Validity checks: dest.
+ CheckPosition(assembler,
+ dest_pos,
+ dest,
+ length,
+ slow_path,
+ temp1,
+ temp2,
+ optimizations.GetCountIsDestinationLength());
+
+ if (!optimizations.GetDoesNotNeedTypeCheck()) {
+ // Check whether all elements of the source array are assignable to the component
+ // type of the destination array. We do two checks: the classes are the same,
+ // or the destination is Object[]. If none of these checks succeed, we go to the
+ // slow path.
+ __ LoadFromOffset(kLoadWord, temp1, dest, class_offset);
+ __ LoadFromOffset(kLoadWord, temp2, src, class_offset);
+ bool did_unpoison = false;
+ if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
+ !optimizations.GetSourceIsNonPrimitiveArray()) {
+ // One or two of the references need to be unpoisoned. Unpoisoned them
+ // both to make the identity check valid.
+ __ MaybeUnpoisonHeapReference(temp1);
+ __ MaybeUnpoisonHeapReference(temp2);
+ did_unpoison = true;
+ }
+
+ if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
+ // Bail out if the destination is not a non primitive array.
+ __ LoadFromOffset(kLoadWord, temp3, temp1, component_offset);
+ __ CompareAndBranchIfZero(temp3, slow_path->GetEntryLabel());
+ __ MaybeUnpoisonHeapReference(temp3);
+ __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
+ static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
+ __ CompareAndBranchIfNonZero(temp3, slow_path->GetEntryLabel());
+ }
+
+ if (!optimizations.GetSourceIsNonPrimitiveArray()) {
+ // Bail out if the source is not a non primitive array.
+ // Bail out if the destination is not a non primitive array.
+ __ LoadFromOffset(kLoadWord, temp3, temp2, component_offset);
+ __ CompareAndBranchIfZero(temp3, slow_path->GetEntryLabel());
+ __ MaybeUnpoisonHeapReference(temp3);
+ __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
+ static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
+ __ CompareAndBranchIfNonZero(temp3, slow_path->GetEntryLabel());
+ }
+
+ __ cmp(temp1, ShifterOperand(temp2));
+
+ if (optimizations.GetDestinationIsTypedObjectArray()) {
+ Label do_copy;
+ __ b(&do_copy, EQ);
+ if (!did_unpoison) {
+ __ MaybeUnpoisonHeapReference(temp1);
+ }
+ __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
+ __ MaybeUnpoisonHeapReference(temp1);
+ __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
+ // No need to unpoison the result, we're comparing against null.
+ __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
+ __ Bind(&do_copy);
+ } else {
+ __ b(slow_path->GetEntryLabel(), NE);
+ }
+ } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
+ DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
+ // Bail out if the source is not a non primitive array.
+ __ LoadFromOffset(kLoadWord, temp1, src, class_offset);
+ __ MaybeUnpoisonHeapReference(temp1);
+ __ LoadFromOffset(kLoadWord, temp3, temp1, component_offset);
+ __ CompareAndBranchIfZero(temp3, slow_path->GetEntryLabel());
+ __ MaybeUnpoisonHeapReference(temp3);
+ __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
+ static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
+ __ CompareAndBranchIfNonZero(temp3, slow_path->GetEntryLabel());
+ }
+
+ // Compute base source address, base destination address, and end source address.
+
+ uint32_t element_size = sizeof(int32_t);
+ uint32_t offset = mirror::Array::DataOffset(element_size).Uint32Value();
+ if (src_pos.IsConstant()) {
+ int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
+ __ AddConstant(temp1, src, element_size * constant + offset);
+ } else {
+ __ add(temp1, src, ShifterOperand(src_pos.AsRegister<Register>(), LSL, 2));
+ __ AddConstant(temp1, offset);
+ }
+
+ if (dest_pos.IsConstant()) {
+ int32_t constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
+ __ AddConstant(temp2, dest, element_size * constant + offset);
+ } else {
+ __ add(temp2, dest, ShifterOperand(dest_pos.AsRegister<Register>(), LSL, 2));
+ __ AddConstant(temp2, offset);
+ }
+
+ if (length.IsConstant()) {
+ int32_t constant = length.GetConstant()->AsIntConstant()->GetValue();
+ __ AddConstant(temp3, temp1, element_size * constant);
+ } else {
+ __ add(temp3, temp1, ShifterOperand(length.AsRegister<Register>(), LSL, 2));
+ }
+
+ // Iterate over the arrays and do a raw copy of the objects. We don't need to
+ // poison/unpoison, nor do any read barrier as the next uses of the destination
+ // array will do it.
+ Label loop, done;
+ __ cmp(temp1, ShifterOperand(temp3));
+ __ b(&done, EQ);
+ __ Bind(&loop);
+ __ ldr(IP, Address(temp1, element_size, Address::PostIndex));
+ __ str(IP, Address(temp2, element_size, Address::PostIndex));
+ __ cmp(temp1, ShifterOperand(temp3));
+ __ b(&loop, NE);
+ __ Bind(&done);
+
+ // We only need one card marking on the destination array.
+ codegen_->MarkGCCard(temp1,
+ temp2,
+ dest,
+ Register(kNoRegister),
+ false);
+
+ __ Bind(slow_path->GetExitLabel());
+}
+
// Unimplemented intrinsics.
#define UNIMPLEMENTED_INTRINSIC(Name) \
@@ -1333,7 +1635,6 @@
UNIMPLEMENTED_INTRINSIC(MathRoundFloat) // Could be done by changing rounding mode, maybe?
UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) // High register pressure.
UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
-UNIMPLEMENTED_INTRINSIC(SystemArrayCopy)
UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
diff --git a/compiler/optimizing/intrinsics_arm.h b/compiler/optimizing/intrinsics_arm.h
index 2abb605..127e9a4 100644
--- a/compiler/optimizing/intrinsics_arm.h
+++ b/compiler/optimizing/intrinsics_arm.h
@@ -33,8 +33,10 @@
class IntrinsicLocationsBuilderARM FINAL : public IntrinsicVisitor {
public:
- IntrinsicLocationsBuilderARM(ArenaAllocator* arena, const ArmInstructionSetFeatures& features)
- : arena_(arena), features_(features) {}
+ IntrinsicLocationsBuilderARM(ArenaAllocator* arena,
+ ArmAssembler* assembler,
+ const ArmInstructionSetFeatures& features)
+ : arena_(arena), assembler_(assembler), features_(features) {}
// Define visitor methods.
@@ -52,6 +54,7 @@
private:
ArenaAllocator* arena_;
+ ArmAssembler* assembler_;
const ArmInstructionSetFeatures& features_;
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 1061aae..e0d88a9 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -914,55 +914,7 @@
void IntrinsicLocationsBuilderX86_64::VisitSystemArrayCopy(HInvoke* invoke) {
- // Check to see if we have known failures that will cause us to have to bail out
- // to the runtime, and just generate the runtime call directly.
- HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
- HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
-
- // The positions must be non-negative.
- if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
- (dest_pos != nullptr && dest_pos->GetValue() < 0)) {
- // We will have to fail anyways.
- return;
- }
-
- // The length must be > 0.
- HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
- if (length != nullptr) {
- int32_t len = length->GetValue();
- if (len < 0) {
- // Just call as normal.
- return;
- }
- }
-
- SystemArrayCopyOptimizations optimizations(invoke);
-
- if (optimizations.GetDestinationIsSource()) {
- if (src_pos != nullptr && dest_pos != nullptr && src_pos->GetValue() < dest_pos->GetValue()) {
- // We only support backward copying if source and destination are the same.
- return;
- }
- }
-
- if (optimizations.GetDestinationIsPrimitiveArray() || optimizations.GetSourceIsPrimitiveArray()) {
- // We currently don't intrinsify primitive copying.
- return;
- }
-
- LocationSummary* locations = new (arena_) LocationSummary(invoke,
- LocationSummary::kCallOnSlowPath,
- kIntrinsified);
- // arraycopy(Object src, int src_pos, Object dest, int dest_pos, int length).
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
- locations->SetInAt(2, Location::RequiresRegister());
- locations->SetInAt(3, Location::RegisterOrConstant(invoke->InputAt(3)));
- locations->SetInAt(4, Location::RegisterOrConstant(invoke->InputAt(4)));
-
- locations->AddTemp(Location::RequiresRegister());
- locations->AddTemp(Location::RequiresRegister());
- locations->AddTemp(Location::RequiresRegister());
+ CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke);
}
void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopy(HInvoke* invoke) {
@@ -990,7 +942,9 @@
SystemArrayCopyOptimizations optimizations(invoke);
if (!optimizations.GetDestinationIsSource()) {
- __ cmpl(src, dest);
+ if (!src_pos.IsConstant() || !dest_pos.IsConstant()) {
+ __ cmpl(src, dest);
+ }
}
// If source and destination are the same, we go to slow path if we need to do
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 24a89bc..ed401b6 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -606,8 +606,23 @@
void HBasicBlock::ReplaceAndRemoveInstructionWith(HInstruction* initial,
HInstruction* replacement) {
DCHECK(initial->GetBlock() == this);
- InsertInstructionBefore(replacement, initial);
- initial->ReplaceWith(replacement);
+ if (initial->IsControlFlow()) {
+ // We can only replace a control flow instruction with another control flow instruction.
+ DCHECK(replacement->IsControlFlow());
+ DCHECK_EQ(replacement->GetId(), -1);
+ DCHECK_EQ(replacement->GetType(), Primitive::kPrimVoid);
+ DCHECK_EQ(initial->GetBlock(), this);
+ DCHECK_EQ(initial->GetType(), Primitive::kPrimVoid);
+ DCHECK(initial->GetUses().IsEmpty());
+ DCHECK(initial->GetEnvUses().IsEmpty());
+ replacement->SetBlock(this);
+ replacement->SetId(GetGraph()->GetNextInstructionId());
+ instructions_.InsertInstructionBefore(replacement, initial);
+ UpdateInputsUsers(replacement);
+ } else {
+ InsertInstructionBefore(replacement, initial);
+ initial->ReplaceWith(replacement);
+ }
RemoveInstruction(initial);
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 82909c4..0d668e8 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1074,7 +1074,8 @@
#define FOR_EACH_CONCRETE_INSTRUCTION_X86(M) \
M(X86ComputeBaseMethodAddress, Instruction) \
- M(X86LoadFromConstantTable, Instruction)
+ M(X86LoadFromConstantTable, Instruction) \
+ M(X86PackedSwitch, Instruction)
#define FOR_EACH_CONCRETE_INSTRUCTION_X86_64(M)
diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h
index f7cc872..556217b 100644
--- a/compiler/optimizing/nodes_x86.h
+++ b/compiler/optimizing/nodes_x86.h
@@ -62,6 +62,45 @@
DISALLOW_COPY_AND_ASSIGN(HX86LoadFromConstantTable);
};
+// X86 version of HPackedSwitch that holds a pointer to the base method address.
+class HX86PackedSwitch : public HTemplateInstruction<2> {
+ public:
+ HX86PackedSwitch(int32_t start_value,
+ int32_t num_entries,
+ HInstruction* input,
+ HX86ComputeBaseMethodAddress* method_base,
+ uint32_t dex_pc)
+ : HTemplateInstruction(SideEffects::None(), dex_pc),
+ start_value_(start_value),
+ num_entries_(num_entries) {
+ SetRawInputAt(0, input);
+ SetRawInputAt(1, method_base);
+ }
+
+ bool IsControlFlow() const OVERRIDE { return true; }
+
+ int32_t GetStartValue() const { return start_value_; }
+
+ int32_t GetNumEntries() const { return num_entries_; }
+
+ HX86ComputeBaseMethodAddress* GetBaseMethodAddress() const {
+ return InputAt(1)->AsX86ComputeBaseMethodAddress();
+ }
+
+ HBasicBlock* GetDefaultBlock() const {
+ // Last entry is the default block.
+ return GetBlock()->GetSuccessors()[num_entries_];
+ }
+
+ DECLARE_INSTRUCTION(X86PackedSwitch);
+
+ private:
+ const int32_t start_value_;
+ const int32_t num_entries_;
+
+ DISALLOW_COPY_AND_ASSIGN(HX86PackedSwitch);
+};
+
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_NODES_X86_H_
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index 967b191..d59bc6b 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -470,6 +470,13 @@
orr(rd, rn, so, cond, kCcSet);
}
+ virtual void orn(Register rd, Register rn, const ShifterOperand& so,
+ Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
+
+ virtual void orns(Register rd, Register rn, const ShifterOperand& so, Condition cond = AL) {
+ orn(rd, rn, so, cond, kCcSet);
+ }
+
virtual void mov(Register rd, const ShifterOperand& so,
Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
@@ -832,6 +839,8 @@
uint32_t immediate,
ShifterOperand* shifter_op) = 0;
+ virtual bool ShifterOperandCanAlwaysHold(uint32_t immediate) = 0;
+
static bool IsInstructionForExceptionHandling(uintptr_t pc);
virtual void CompareAndBranchIfZero(Register r, Label* label) = 0;
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index f7772ae..6e7c828 100644
--- a/compiler/utils/arm/assembler_arm32.cc
+++ b/compiler/utils/arm/assembler_arm32.cc
@@ -48,6 +48,11 @@
return false;
}
+bool Arm32Assembler::ShifterOperandCanAlwaysHold(uint32_t immediate) {
+ ShifterOperand shifter_op;
+ return ShifterOperandCanHoldArm32(immediate, &shifter_op);
+}
+
bool Arm32Assembler::ShifterOperandCanHold(Register rd ATTRIBUTE_UNUSED,
Register rn ATTRIBUTE_UNUSED,
Opcode opcode ATTRIBUTE_UNUSED,
@@ -130,6 +135,15 @@
}
+void Arm32Assembler::orn(Register rd ATTRIBUTE_UNUSED,
+ Register rn ATTRIBUTE_UNUSED,
+ const ShifterOperand& so ATTRIBUTE_UNUSED,
+ Condition cond ATTRIBUTE_UNUSED,
+ SetCc set_cc ATTRIBUTE_UNUSED) {
+ LOG(FATAL) << "orn is not supported on ARM32";
+}
+
+
void Arm32Assembler::mov(Register rd, const ShifterOperand& so,
Condition cond, SetCc set_cc) {
EmitType01(cond, so.type(), MOV, set_cc, R0, rd, so);
diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h
index 3407369..4646538 100644
--- a/compiler/utils/arm/assembler_arm32.h
+++ b/compiler/utils/arm/assembler_arm32.h
@@ -74,6 +74,9 @@
virtual void orr(Register rd, Register rn, const ShifterOperand& so,
Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
+ virtual void orn(Register rd, Register rn, const ShifterOperand& so,
+ Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
+
virtual void mov(Register rd, const ShifterOperand& so,
Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
@@ -294,6 +297,7 @@
uint32_t immediate,
ShifterOperand* shifter_op) OVERRIDE;
+ bool ShifterOperandCanAlwaysHold(uint32_t immediate) OVERRIDE;
static bool IsInstructionForExceptionHandling(uintptr_t pc);
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 0f6c4f5..cc87856 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -390,6 +390,10 @@
EmitLiterals();
}
+bool Thumb2Assembler::ShifterOperandCanAlwaysHold(uint32_t immediate) {
+ return ArmAssembler::ModifiedImmediate(immediate) != kInvalidModifiedImmediate;
+}
+
bool Thumb2Assembler::ShifterOperandCanHold(Register rd ATTRIBUTE_UNUSED,
Register rn ATTRIBUTE_UNUSED,
Opcode opcode,
@@ -410,6 +414,7 @@
case MOV:
// TODO: Support less than or equal to 12bits.
return ArmAssembler::ModifiedImmediate(immediate) != kInvalidModifiedImmediate;
+
case MVN:
default:
return ArmAssembler::ModifiedImmediate(immediate) != kInvalidModifiedImmediate;
@@ -492,6 +497,12 @@
}
+void Thumb2Assembler::orn(Register rd, Register rn, const ShifterOperand& so,
+ Condition cond, SetCc set_cc) {
+ EmitDataProcessing(cond, ORN, set_cc, rn, rd, so);
+}
+
+
void Thumb2Assembler::mov(Register rd, const ShifterOperand& so,
Condition cond, SetCc set_cc) {
EmitDataProcessing(cond, MOV, set_cc, R0, rd, so);
@@ -1105,6 +1116,7 @@
rn_is_valid = false; // There is no Rn for these instructions.
break;
case TEQ:
+ case ORN:
return true;
case ADD:
case SUB:
@@ -1222,6 +1234,7 @@
case MOV: thumb_opcode = 2U /* 0b0010 */; rn = PC; break;
case BIC: thumb_opcode = 1U /* 0b0001 */; break;
case MVN: thumb_opcode = 3U /* 0b0011 */; rn = PC; break;
+ case ORN: thumb_opcode = 3U /* 0b0011 */; break;
default:
break;
}
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index a1a8927..055b137 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -98,6 +98,9 @@
virtual void orr(Register rd, Register rn, const ShifterOperand& so,
Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
+ virtual void orn(Register rd, Register rn, const ShifterOperand& so,
+ Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
+
virtual void mov(Register rd, const ShifterOperand& so,
Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
@@ -337,6 +340,8 @@
uint32_t immediate,
ShifterOperand* shifter_op) OVERRIDE;
+ bool ShifterOperandCanAlwaysHold(uint32_t immediate) OVERRIDE;
+
static bool IsInstructionForExceptionHandling(uintptr_t pc);
diff --git a/compiler/utils/arm/constants_arm.h b/compiler/utils/arm/constants_arm.h
index 6b4daed..2060064 100644
--- a/compiler/utils/arm/constants_arm.h
+++ b/compiler/utils/arm/constants_arm.h
@@ -148,7 +148,8 @@
MOV = 13, // Move
BIC = 14, // Bit Clear
MVN = 15, // Move Not
- kMaxOperand = 16
+ ORN = 16, // Logical OR NOT.
+ kMaxOperand = 17
};
std::ostream& operator<<(std::ostream& os, const Opcode& rhs);
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index b2a354b..2ae8841 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -238,6 +238,7 @@
__ sub(R0, R1, ShifterOperand(R2), AL, kCcKeep);
__ and_(R0, R1, ShifterOperand(R2), AL, kCcKeep);
__ orr(R0, R1, ShifterOperand(R2), AL, kCcKeep);
+ __ orn(R0, R1, ShifterOperand(R2), AL, kCcKeep);
__ eor(R0, R1, ShifterOperand(R2), AL, kCcKeep);
__ bic(R0, R1, ShifterOperand(R2), AL, kCcKeep);
__ adc(R0, R1, ShifterOperand(R2), AL, kCcKeep);
@@ -371,6 +372,7 @@
__ sub(R0, R1, ShifterOperand(0x55));
__ and_(R0, R1, ShifterOperand(0x55));
__ orr(R0, R1, ShifterOperand(0x55));
+ __ orn(R0, R1, ShifterOperand(0x55));
__ eor(R0, R1, ShifterOperand(0x55));
__ bic(R0, R1, ShifterOperand(0x55));
__ adc(R0, R1, ShifterOperand(0x55));
@@ -403,6 +405,7 @@
__ sub(R0, R1, ShifterOperand(0x550055));
__ and_(R0, R1, ShifterOperand(0x550055));
__ orr(R0, R1, ShifterOperand(0x550055));
+ __ orn(R0, R1, ShifterOperand(0x550055));
__ eor(R0, R1, ShifterOperand(0x550055));
__ bic(R0, R1, ShifterOperand(0x550055));
__ adc(R0, R1, ShifterOperand(0x550055));
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 82ad642..b79c2e4 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -23,109 +23,110 @@
" 8: eba1 0002 sub.w r0, r1, r2\n",
" c: ea01 0002 and.w r0, r1, r2\n",
" 10: ea41 0002 orr.w r0, r1, r2\n",
- " 14: ea81 0002 eor.w r0, r1, r2\n",
- " 18: ea21 0002 bic.w r0, r1, r2\n",
- " 1c: eb41 0002 adc.w r0, r1, r2\n",
- " 20: eb61 0002 sbc.w r0, r1, r2\n",
- " 24: ebc1 0002 rsb r0, r1, r2\n",
- " 28: ea90 0f01 teq r0, r1\n",
- " 2c: 0008 movs r0, r1\n",
- " 2e: 4608 mov r0, r1\n",
- " 30: 43c8 mvns r0, r1\n",
- " 32: 4408 add r0, r1\n",
- " 34: 1888 adds r0, r1, r2\n",
- " 36: 1a88 subs r0, r1, r2\n",
- " 38: 4148 adcs r0, r1\n",
- " 3a: 4188 sbcs r0, r1\n",
- " 3c: 4008 ands r0, r1\n",
- " 3e: 4308 orrs r0, r1\n",
- " 40: 4048 eors r0, r1\n",
- " 42: 4388 bics r0, r1\n",
- " 44: 4208 tst r0, r1\n",
- " 46: 4288 cmp r0, r1\n",
- " 48: 42c8 cmn r0, r1\n",
- " 4a: 4641 mov r1, r8\n",
- " 4c: 4681 mov r9, r0\n",
- " 4e: 46c8 mov r8, r9\n",
- " 50: 4441 add r1, r8\n",
- " 52: 4481 add r9, r0\n",
- " 54: 44c8 add r8, r9\n",
- " 56: 4548 cmp r0, r9\n",
- " 58: 4588 cmp r8, r1\n",
- " 5a: 45c1 cmp r9, r8\n",
- " 5c: 4248 negs r0, r1\n",
- " 5e: 4240 negs r0, r0\n",
- " 60: ea5f 0008 movs.w r0, r8\n",
- " 64: ea7f 0008 mvns.w r0, r8\n",
- " 68: eb01 0008 add.w r0, r1, r8\n",
- " 6c: eb11 0008 adds.w r0, r1, r8\n",
- " 70: ebb1 0008 subs.w r0, r1, r8\n",
- " 74: eb50 0008 adcs.w r0, r0, r8\n",
- " 78: eb70 0008 sbcs.w r0, r0, r8\n",
- " 7c: ea10 0008 ands.w r0, r0, r8\n",
- " 80: ea50 0008 orrs.w r0, r0, r8\n",
- " 84: ea90 0008 eors.w r0, r0, r8\n",
- " 88: ea30 0008 bics.w r0, r0, r8\n",
- " 8c: ea10 0f08 tst.w r0, r8\n",
- " 90: eb10 0f08 cmn.w r0, r8\n",
- " 94: f1d8 0000 rsbs r0, r8, #0\n",
- " 98: f1d8 0800 rsbs r8, r8, #0\n",
- " 9c: bf08 it eq\n",
- " 9e: ea7f 0001 mvnseq.w r0, r1\n",
- " a2: bf08 it eq\n",
- " a4: eb11 0002 addseq.w r0, r1, r2\n",
- " a8: bf08 it eq\n",
- " aa: ebb1 0002 subseq.w r0, r1, r2\n",
- " ae: bf08 it eq\n",
- " b0: eb50 0001 adcseq.w r0, r0, r1\n",
- " b4: bf08 it eq\n",
- " b6: eb70 0001 sbcseq.w r0, r0, r1\n",
- " ba: bf08 it eq\n",
- " bc: ea10 0001 andseq.w r0, r0, r1\n",
- " c0: bf08 it eq\n",
- " c2: ea50 0001 orrseq.w r0, r0, r1\n",
- " c6: bf08 it eq\n",
- " c8: ea90 0001 eorseq.w r0, r0, r1\n",
- " cc: bf08 it eq\n",
- " ce: ea30 0001 bicseq.w r0, r0, r1\n",
- " d2: bf08 it eq\n",
- " d4: 43c8 mvneq r0, r1\n",
+ " 14: ea61 0002 orn r0, r1, r2\n",
+ " 18: ea81 0002 eor.w r0, r1, r2\n",
+ " 1c: ea21 0002 bic.w r0, r1, r2\n",
+ " 20: eb41 0002 adc.w r0, r1, r2\n",
+ " 24: eb61 0002 sbc.w r0, r1, r2\n",
+ " 28: ebc1 0002 rsb r0, r1, r2\n",
+ " 2c: ea90 0f01 teq r0, r1\n",
+ " 30: 0008 movs r0, r1\n",
+ " 32: 4608 mov r0, r1\n",
+ " 34: 43c8 mvns r0, r1\n",
+ " 36: 4408 add r0, r1\n",
+ " 38: 1888 adds r0, r1, r2\n",
+ " 3a: 1a88 subs r0, r1, r2\n",
+ " 3c: 4148 adcs r0, r1\n",
+ " 3e: 4188 sbcs r0, r1\n",
+ " 40: 4008 ands r0, r1\n",
+ " 42: 4308 orrs r0, r1\n",
+ " 44: 4048 eors r0, r1\n",
+ " 46: 4388 bics r0, r1\n",
+ " 48: 4208 tst r0, r1\n",
+ " 4a: 4288 cmp r0, r1\n",
+ " 4c: 42c8 cmn r0, r1\n",
+ " 4e: 4641 mov r1, r8\n",
+ " 50: 4681 mov r9, r0\n",
+ " 52: 46c8 mov r8, r9\n",
+ " 54: 4441 add r1, r8\n",
+ " 56: 4481 add r9, r0\n",
+ " 58: 44c8 add r8, r9\n",
+ " 5a: 4548 cmp r0, r9\n",
+ " 5c: 4588 cmp r8, r1\n",
+ " 5e: 45c1 cmp r9, r8\n",
+ " 60: 4248 negs r0, r1\n",
+ " 62: 4240 negs r0, r0\n",
+ " 64: ea5f 0008 movs.w r0, r8\n",
+ " 68: ea7f 0008 mvns.w r0, r8\n",
+ " 6c: eb01 0008 add.w r0, r1, r8\n",
+ " 70: eb11 0008 adds.w r0, r1, r8\n",
+ " 74: ebb1 0008 subs.w r0, r1, r8\n",
+ " 78: eb50 0008 adcs.w r0, r0, r8\n",
+ " 7c: eb70 0008 sbcs.w r0, r0, r8\n",
+ " 80: ea10 0008 ands.w r0, r0, r8\n",
+ " 84: ea50 0008 orrs.w r0, r0, r8\n",
+ " 88: ea90 0008 eors.w r0, r0, r8\n",
+ " 8c: ea30 0008 bics.w r0, r0, r8\n",
+ " 90: ea10 0f08 tst.w r0, r8\n",
+ " 94: eb10 0f08 cmn.w r0, r8\n",
+ " 98: f1d8 0000 rsbs r0, r8, #0\n",
+ " 9c: f1d8 0800 rsbs r8, r8, #0\n",
+ " a0: bf08 it eq\n",
+ " a2: ea7f 0001 mvnseq.w r0, r1\n",
+ " a6: bf08 it eq\n",
+ " a8: eb11 0002 addseq.w r0, r1, r2\n",
+ " ac: bf08 it eq\n",
+ " ae: ebb1 0002 subseq.w r0, r1, r2\n",
+ " b2: bf08 it eq\n",
+ " b4: eb50 0001 adcseq.w r0, r0, r1\n",
+ " b8: bf08 it eq\n",
+ " ba: eb70 0001 sbcseq.w r0, r0, r1\n",
+ " be: bf08 it eq\n",
+ " c0: ea10 0001 andseq.w r0, r0, r1\n",
+ " c4: bf08 it eq\n",
+ " c6: ea50 0001 orrseq.w r0, r0, r1\n",
+ " ca: bf08 it eq\n",
+ " cc: ea90 0001 eorseq.w r0, r0, r1\n",
+ " d0: bf08 it eq\n",
+ " d2: ea30 0001 bicseq.w r0, r0, r1\n",
" d6: bf08 it eq\n",
- " d8: 1888 addeq r0, r1, r2\n",
+ " d8: 43c8 mvneq r0, r1\n",
" da: bf08 it eq\n",
- " dc: 1a88 subeq r0, r1, r2\n",
+ " dc: 1888 addeq r0, r1, r2\n",
" de: bf08 it eq\n",
- " e0: 4148 adceq r0, r1\n",
+ " e0: 1a88 subeq r0, r1, r2\n",
" e2: bf08 it eq\n",
- " e4: 4188 sbceq r0, r1\n",
+ " e4: 4148 adceq r0, r1\n",
" e6: bf08 it eq\n",
- " e8: 4008 andeq r0, r1\n",
+ " e8: 4188 sbceq r0, r1\n",
" ea: bf08 it eq\n",
- " ec: 4308 orreq r0, r1\n",
+ " ec: 4008 andeq r0, r1\n",
" ee: bf08 it eq\n",
- " f0: 4048 eoreq r0, r1\n",
+ " f0: 4308 orreq r0, r1\n",
" f2: bf08 it eq\n",
- " f4: 4388 biceq r0, r1\n",
- " f6: 4608 mov r0, r1\n",
- " f8: 43c8 mvns r0, r1\n",
- " fa: 4408 add r0, r1\n",
- " fc: 1888 adds r0, r1, r2\n",
- " fe: 1a88 subs r0, r1, r2\n",
- " 100: 4148 adcs r0, r1\n",
- " 102: 4188 sbcs r0, r1\n",
- " 104: 4008 ands r0, r1\n",
- " 106: 4308 orrs r0, r1\n",
- " 108: 4048 eors r0, r1\n",
- " 10a: 4388 bics r0, r1\n",
- " 10c: 4641 mov r1, r8\n",
- " 10e: 4681 mov r9, r0\n",
- " 110: 46c8 mov r8, r9\n",
- " 112: 4441 add r1, r8\n",
- " 114: 4481 add r9, r0\n",
- " 116: 44c8 add r8, r9\n",
- " 118: 4248 negs r0, r1\n",
- " 11a: 4240 negs r0, r0\n",
- " 11c: eb01 0c00 add.w ip, r1, r0\n",
+ " f4: 4048 eoreq r0, r1\n",
+ " f6: bf08 it eq\n",
+ " f8: 4388 biceq r0, r1\n",
+ " fa: 4608 mov r0, r1\n",
+ " fc: 43c8 mvns r0, r1\n",
+ " fe: 4408 add r0, r1\n",
+ " 100: 1888 adds r0, r1, r2\n",
+ " 102: 1a88 subs r0, r1, r2\n",
+ " 104: 4148 adcs r0, r1\n",
+ " 106: 4188 sbcs r0, r1\n",
+ " 108: 4008 ands r0, r1\n",
+ " 10a: 4308 orrs r0, r1\n",
+ " 10c: 4048 eors r0, r1\n",
+ " 10e: 4388 bics r0, r1\n",
+ " 110: 4641 mov r1, r8\n",
+ " 112: 4681 mov r9, r0\n",
+ " 114: 46c8 mov r8, r9\n",
+ " 116: 4441 add r1, r8\n",
+ " 118: 4481 add r9, r0\n",
+ " 11a: 44c8 add r8, r9\n",
+ " 11c: 4248 negs r0, r1\n",
+ " 11e: 4240 negs r0, r0\n",
+ " 120: eb01 0c00 add.w ip, r1, r0\n",
nullptr
};
const char* DataProcessingImmediateResults[] = {
@@ -135,21 +136,22 @@
" a: f2a1 0055 subw r0, r1, #85 ; 0x55\n",
" e: f001 0055 and.w r0, r1, #85 ; 0x55\n",
" 12: f041 0055 orr.w r0, r1, #85 ; 0x55\n",
- " 16: f081 0055 eor.w r0, r1, #85 ; 0x55\n",
- " 1a: f021 0055 bic.w r0, r1, #85 ; 0x55\n",
- " 1e: f141 0055 adc.w r0, r1, #85 ; 0x55\n",
- " 22: f161 0055 sbc.w r0, r1, #85 ; 0x55\n",
- " 26: f1c1 0055 rsb r0, r1, #85 ; 0x55\n",
- " 2a: f010 0f55 tst.w r0, #85 ; 0x55\n",
- " 2e: f090 0f55 teq r0, #85 ; 0x55\n",
- " 32: 2855 cmp r0, #85 ; 0x55\n",
- " 34: f110 0f55 cmn.w r0, #85 ; 0x55\n",
- " 38: 1d48 adds r0, r1, #5\n",
- " 3a: 1f48 subs r0, r1, #5\n",
- " 3c: 2055 movs r0, #85 ; 0x55\n",
- " 3e: f07f 0055 mvns.w r0, #85 ; 0x55\n",
- " 42: 1d48 adds r0, r1, #5\n",
- " 44: 1f48 subs r0, r1, #5\n",
+ " 16: f061 0055 orn r0, r1, #85 ; 0x55\n",
+ " 1a: f081 0055 eor.w r0, r1, #85 ; 0x55\n",
+ " 1e: f021 0055 bic.w r0, r1, #85 ; 0x55\n",
+ " 22: f141 0055 adc.w r0, r1, #85 ; 0x55\n",
+ " 26: f161 0055 sbc.w r0, r1, #85 ; 0x55\n",
+ " 2a: f1c1 0055 rsb r0, r1, #85 ; 0x55\n",
+ " 2e: f010 0f55 tst.w r0, #85 ; 0x55\n",
+ " 32: f090 0f55 teq r0, #85 ; 0x55\n",
+ " 36: 2855 cmp r0, #85 ; 0x55\n",
+ " 38: f110 0f55 cmn.w r0, #85 ; 0x55\n",
+ " 3c: 1d48 adds r0, r1, #5\n",
+ " 3e: 1f48 subs r0, r1, #5\n",
+ " 40: 2055 movs r0, #85 ; 0x55\n",
+ " 42: f07f 0055 mvns.w r0, #85 ; 0x55\n",
+ " 46: 1d48 adds r0, r1, #5\n",
+ " 48: 1f48 subs r0, r1, #5\n",
nullptr
};
const char* DataProcessingModifiedImmediateResults[] = {
@@ -159,15 +161,16 @@
" c: f1a1 1055 sub.w r0, r1, #5570645 ; 0x550055\n",
" 10: f001 1055 and.w r0, r1, #5570645 ; 0x550055\n",
" 14: f041 1055 orr.w r0, r1, #5570645 ; 0x550055\n",
- " 18: f081 1055 eor.w r0, r1, #5570645 ; 0x550055\n",
- " 1c: f021 1055 bic.w r0, r1, #5570645 ; 0x550055\n",
- " 20: f141 1055 adc.w r0, r1, #5570645 ; 0x550055\n",
- " 24: f161 1055 sbc.w r0, r1, #5570645 ; 0x550055\n",
- " 28: f1c1 1055 rsb r0, r1, #5570645 ; 0x550055\n",
- " 2c: f010 1f55 tst.w r0, #5570645 ; 0x550055\n",
- " 30: f090 1f55 teq r0, #5570645 ; 0x550055\n",
- " 34: f1b0 1f55 cmp.w r0, #5570645 ; 0x550055\n",
- " 38: f110 1f55 cmn.w r0, #5570645 ; 0x550055\n",
+ " 18: f061 1055 orn r0, r1, #5570645 ; 0x550055\n",
+ " 1c: f081 1055 eor.w r0, r1, #5570645 ; 0x550055\n",
+ " 20: f021 1055 bic.w r0, r1, #5570645 ; 0x550055\n",
+ " 24: f141 1055 adc.w r0, r1, #5570645 ; 0x550055\n",
+ " 28: f161 1055 sbc.w r0, r1, #5570645 ; 0x550055\n",
+ " 2c: f1c1 1055 rsb r0, r1, #5570645 ; 0x550055\n",
+ " 30: f010 1f55 tst.w r0, #5570645 ; 0x550055\n",
+ " 34: f090 1f55 teq r0, #5570645 ; 0x550055\n",
+ " 38: f1b0 1f55 cmp.w r0, #5570645 ; 0x550055\n",
+ " 3c: f110 1f55 cmn.w r0, #5570645 ; 0x550055\n",
nullptr
};
const char* DataProcessingModifiedImmediatesResults[] = {
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index 04e815a..5347bf0 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -2369,44 +2369,48 @@
}
}
-int ConstantArea::AddInt32(int32_t v) {
- for (size_t i = 0, e = buffer_.size(); i < e; i++) {
- if (v == buffer_[i]) {
- return i * kEntrySize;
- }
- }
-
- // Didn't match anything.
- int result = buffer_.size() * kEntrySize;
+size_t ConstantArea::AppendInt32(int32_t v) {
+ size_t result = buffer_.size() * elem_size_;
buffer_.push_back(v);
return result;
}
-int ConstantArea::AddInt64(int64_t v) {
+size_t ConstantArea::AddInt32(int32_t v) {
+ for (size_t i = 0, e = buffer_.size(); i < e; i++) {
+ if (v == buffer_[i]) {
+ return i * elem_size_;
+ }
+ }
+
+ // Didn't match anything.
+ return AppendInt32(v);
+}
+
+size_t ConstantArea::AddInt64(int64_t v) {
int32_t v_low = Low32Bits(v);
int32_t v_high = High32Bits(v);
if (buffer_.size() > 1) {
// Ensure we don't pass the end of the buffer.
for (size_t i = 0, e = buffer_.size() - 1; i < e; i++) {
if (v_low == buffer_[i] && v_high == buffer_[i + 1]) {
- return i * kEntrySize;
+ return i * elem_size_;
}
}
}
// Didn't match anything.
- int result = buffer_.size() * kEntrySize;
+ size_t result = buffer_.size() * elem_size_;
buffer_.push_back(v_low);
buffer_.push_back(v_high);
return result;
}
-int ConstantArea::AddDouble(double v) {
+size_t ConstantArea::AddDouble(double v) {
// Treat the value as a 64-bit integer value.
return AddInt64(bit_cast<int64_t, double>(v));
}
-int ConstantArea::AddFloat(float v) {
+size_t ConstantArea::AddFloat(float v) {
// Treat the value as a 32-bit integer value.
return AddInt32(bit_cast<int32_t, float>(v));
}
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index 93ecdf5..b50fda9 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -166,21 +166,6 @@
Init(base_in, disp.Int32Value());
}
- void Init(Register base_in, int32_t disp) {
- if (disp == 0 && base_in != EBP) {
- SetModRM(0, base_in);
- if (base_in == ESP) SetSIB(TIMES_1, ESP, base_in);
- } else if (disp >= -128 && disp <= 127) {
- SetModRM(1, base_in);
- if (base_in == ESP) SetSIB(TIMES_1, ESP, base_in);
- SetDisp8(disp);
- } else {
- SetModRM(2, base_in);
- if (base_in == ESP) SetSIB(TIMES_1, ESP, base_in);
- SetDisp32(disp);
- }
- }
-
Address(Register index_in, ScaleFactor scale_in, int32_t disp) {
CHECK_NE(index_in, ESP); // Illegal addressing mode.
SetModRM(0, ESP);
@@ -189,19 +174,15 @@
}
Address(Register base_in, Register index_in, ScaleFactor scale_in, int32_t disp) {
- CHECK_NE(index_in, ESP); // Illegal addressing mode.
- if (disp == 0 && base_in != EBP) {
- SetModRM(0, ESP);
- SetSIB(scale_in, index_in, base_in);
- } else if (disp >= -128 && disp <= 127) {
- SetModRM(1, ESP);
- SetSIB(scale_in, index_in, base_in);
- SetDisp8(disp);
- } else {
- SetModRM(2, ESP);
- SetSIB(scale_in, index_in, base_in);
- SetDisp32(disp);
- }
+ Init(base_in, index_in, scale_in, disp);
+ }
+
+ Address(Register base_in,
+ Register index_in,
+ ScaleFactor scale_in,
+ int32_t disp, AssemblerFixup *fixup) {
+ Init(base_in, index_in, scale_in, disp);
+ SetFixup(fixup);
}
static Address Absolute(uintptr_t addr) {
@@ -217,6 +198,37 @@
private:
Address() {}
+
+ void Init(Register base_in, int32_t disp) {
+ if (disp == 0 && base_in != EBP) {
+ SetModRM(0, base_in);
+ if (base_in == ESP) SetSIB(TIMES_1, ESP, base_in);
+ } else if (disp >= -128 && disp <= 127) {
+ SetModRM(1, base_in);
+ if (base_in == ESP) SetSIB(TIMES_1, ESP, base_in);
+ SetDisp8(disp);
+ } else {
+ SetModRM(2, base_in);
+ if (base_in == ESP) SetSIB(TIMES_1, ESP, base_in);
+ SetDisp32(disp);
+ }
+ }
+
+ void Init(Register base_in, Register index_in, ScaleFactor scale_in, int32_t disp) {
+ CHECK_NE(index_in, ESP); // Illegal addressing mode.
+ if (disp == 0 && base_in != EBP) {
+ SetModRM(0, ESP);
+ SetSIB(scale_in, index_in, base_in);
+ } else if (disp >= -128 && disp <= 127) {
+ SetModRM(1, ESP);
+ SetSIB(scale_in, index_in, base_in);
+ SetDisp8(disp);
+ } else {
+ SetModRM(2, ESP);
+ SetSIB(scale_in, index_in, base_in);
+ SetDisp32(disp);
+ }
+ }
};
@@ -252,40 +264,39 @@
// Add a double to the constant area, returning the offset into
// the constant area where the literal resides.
- int AddDouble(double v);
+ size_t AddDouble(double v);
// Add a float to the constant area, returning the offset into
// the constant area where the literal resides.
- int AddFloat(float v);
+ size_t AddFloat(float v);
// Add an int32_t to the constant area, returning the offset into
// the constant area where the literal resides.
- int AddInt32(int32_t v);
+ size_t AddInt32(int32_t v);
+
+ // Add an int32_t to the end of the constant area, returning the offset into
+ // the constant area where the literal resides.
+ size_t AppendInt32(int32_t v);
// Add an int64_t to the constant area, returning the offset into
// the constant area where the literal resides.
- int AddInt64(int64_t v);
+ size_t AddInt64(int64_t v);
bool IsEmpty() const {
return buffer_.size() == 0;
}
+ size_t GetSize() const {
+ return buffer_.size() * elem_size_;
+ }
+
const std::vector<int32_t>& GetBuffer() const {
return buffer_;
}
- void AddFixup(AssemblerFixup* fixup) {
- fixups_.push_back(fixup);
- }
-
- const std::vector<AssemblerFixup*>& GetFixups() const {
- return fixups_;
- }
-
private:
- static constexpr size_t kEntrySize = sizeof(int32_t);
+ static constexpr size_t elem_size_ = sizeof(int32_t);
std::vector<int32_t> buffer_;
- std::vector<AssemblerFixup*> fixups_;
};
class X86Assembler FINAL : public Assembler {
@@ -740,26 +751,36 @@
// Add a double to the constant area, returning the offset into
// the constant area where the literal resides.
- int AddDouble(double v) { return constant_area_.AddDouble(v); }
+ size_t AddDouble(double v) { return constant_area_.AddDouble(v); }
// Add a float to the constant area, returning the offset into
// the constant area where the literal resides.
- int AddFloat(float v) { return constant_area_.AddFloat(v); }
+ size_t AddFloat(float v) { return constant_area_.AddFloat(v); }
// Add an int32_t to the constant area, returning the offset into
// the constant area where the literal resides.
- int AddInt32(int32_t v) { return constant_area_.AddInt32(v); }
+ size_t AddInt32(int32_t v) {
+ return constant_area_.AddInt32(v);
+ }
+
+ // Add an int32_t to the end of the constant area, returning the offset into
+ // the constant area where the literal resides.
+ size_t AppendInt32(int32_t v) {
+ return constant_area_.AppendInt32(v);
+ }
// Add an int64_t to the constant area, returning the offset into
// the constant area where the literal resides.
- int AddInt64(int64_t v) { return constant_area_.AddInt64(v); }
+ size_t AddInt64(int64_t v) { return constant_area_.AddInt64(v); }
// Add the contents of the constant area to the assembler buffer.
void AddConstantArea();
// Is the constant area empty? Return true if there are no literals in the constant area.
bool IsConstantAreaEmpty() const { return constant_area_.IsEmpty(); }
- void AddConstantAreaFixup(AssemblerFixup* fixup) { constant_area_.AddFixup(fixup); }
+
+ // Return the current size of the constant area.
+ size_t ConstantAreaSize() const { return constant_area_.GetSize(); }
private:
inline void EmitUint8(uint8_t value);
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 6e7d74d..9eb5e67 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -3122,7 +3122,14 @@
}
}
-int ConstantArea::AddInt32(int32_t v) {
+size_t ConstantArea::AppendInt32(int32_t v) {
+ size_t result = buffer_.size() * elem_size_;
+ buffer_.push_back(v);
+ return result;
+}
+
+size_t ConstantArea::AddInt32(int32_t v) {
+ // Look for an existing match.
for (size_t i = 0, e = buffer_.size(); i < e; i++) {
if (v == buffer_[i]) {
return i * elem_size_;
@@ -3130,12 +3137,10 @@
}
// Didn't match anything.
- int result = buffer_.size() * elem_size_;
- buffer_.push_back(v);
- return result;
+ return AppendInt32(v);
}
-int ConstantArea::AddInt64(int64_t v) {
+size_t ConstantArea::AddInt64(int64_t v) {
int32_t v_low = v;
int32_t v_high = v >> 32;
if (buffer_.size() > 1) {
@@ -3148,18 +3153,18 @@
}
// Didn't match anything.
- int result = buffer_.size() * elem_size_;
+ size_t result = buffer_.size() * elem_size_;
buffer_.push_back(v_low);
buffer_.push_back(v_high);
return result;
}
-int ConstantArea::AddDouble(double v) {
+size_t ConstantArea::AddDouble(double v) {
// Treat the value as a 64-bit integer value.
return AddInt64(bit_cast<int64_t, double>(v));
}
-int ConstantArea::AddFloat(float v) {
+size_t ConstantArea::AddFloat(float v) {
// Treat the value as a 32-bit integer value.
return AddInt32(bit_cast<int32_t, float>(v));
}
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 255f551..01d28e3 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -269,36 +269,40 @@
* Class to handle constant area values.
*/
class ConstantArea {
- public:
- ConstantArea() {}
+ public:
+ ConstantArea() {}
- // Add a double to the constant area, returning the offset into
- // the constant area where the literal resides.
- int AddDouble(double v);
+ // Add a double to the constant area, returning the offset into
+ // the constant area where the literal resides.
+ size_t AddDouble(double v);
- // Add a float to the constant area, returning the offset into
- // the constant area where the literal resides.
- int AddFloat(float v);
+ // Add a float to the constant area, returning the offset into
+ // the constant area where the literal resides.
+ size_t AddFloat(float v);
- // Add an int32_t to the constant area, returning the offset into
- // the constant area where the literal resides.
- int AddInt32(int32_t v);
+ // Add an int32_t to the constant area, returning the offset into
+ // the constant area where the literal resides.
+ size_t AddInt32(int32_t v);
- // Add an int64_t to the constant area, returning the offset into
- // the constant area where the literal resides.
- int AddInt64(int64_t v);
+ // Add an int32_t to the end of the constant area, returning the offset into
+ // the constant area where the literal resides.
+ size_t AppendInt32(int32_t v);
- int GetSize() const {
- return buffer_.size() * elem_size_;
- }
+ // Add an int64_t to the constant area, returning the offset into
+ // the constant area where the literal resides.
+ size_t AddInt64(int64_t v);
- const std::vector<int32_t>& GetBuffer() const {
- return buffer_;
- }
+ size_t GetSize() const {
+ return buffer_.size() * elem_size_;
+ }
- private:
- static constexpr size_t elem_size_ = sizeof(int32_t);
- std::vector<int32_t> buffer_;
+ const std::vector<int32_t>& GetBuffer() const {
+ return buffer_;
+ }
+
+ private:
+ static constexpr size_t elem_size_ = sizeof(int32_t);
+ std::vector<int32_t> buffer_;
};
@@ -806,19 +810,27 @@
// Add a double to the constant area, returning the offset into
// the constant area where the literal resides.
- int AddDouble(double v) { return constant_area_.AddDouble(v); }
+ size_t AddDouble(double v) { return constant_area_.AddDouble(v); }
// Add a float to the constant area, returning the offset into
// the constant area where the literal resides.
- int AddFloat(float v) { return constant_area_.AddFloat(v); }
+ size_t AddFloat(float v) { return constant_area_.AddFloat(v); }
// Add an int32_t to the constant area, returning the offset into
// the constant area where the literal resides.
- int AddInt32(int32_t v) { return constant_area_.AddInt32(v); }
+ size_t AddInt32(int32_t v) {
+ return constant_area_.AddInt32(v);
+ }
+
+ // Add an int32_t to the end of the constant area, returning the offset into
+ // the constant area where the literal resides.
+ size_t AppendInt32(int32_t v) {
+ return constant_area_.AppendInt32(v);
+ }
// Add an int64_t to the constant area, returning the offset into
// the constant area where the literal resides.
- int AddInt64(int64_t v) { return constant_area_.AddInt64(v); }
+ size_t AddInt64(int64_t v) { return constant_area_.AddInt64(v); }
// Add the contents of the constant area to the assembler buffer.
void AddConstantArea();
@@ -826,6 +838,9 @@
// Is the constant area empty? Return true if there are no literals in the constant area.
bool IsConstantAreaEmpty() const { return constant_area_.GetSize() == 0; }
+ // Return the current size of the constant area.
+ size_t ConstantAreaSize() const { return constant_area_.GetSize(); }
+
//
// Heap poisoning.
//
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index d09631b..930bb2c 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -891,7 +891,109 @@
ONE_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
// Generate the allocation entrypoints for each allocator.
-GENERATE_ALL_ALLOC_ENTRYPOINTS
+GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB)
+// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc).
+ENTRY art_quick_alloc_object_rosalloc
+ // Fast path rosalloc allocation.
+ // r0: type_idx/return value, r1: ArtMethod*, r9: Thread::Current
+ // r2, r3, r12: free.
+ ldr r2, [r1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_32] // Load dex cache resolved types array
+ // Load the class (r2)
+ ldr r2, [r2, r0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT]
+ cbz r2, .Lart_quick_alloc_object_rosalloc_slow_path // Check null class
+ // Check class status.
+ ldr r3, [r2, #MIRROR_CLASS_STATUS_OFFSET]
+ cmp r3, #MIRROR_CLASS_STATUS_INITIALIZED
+ bne .Lart_quick_alloc_object_rosalloc_slow_path
+ // Add a fake dependence from the
+ // following access flag and size
+ // loads to the status load.
+ // This is to prevent those loads
+ // from being reordered above the
+ // status load and reading wrong
+ // values (an alternative is to use
+ // a load-acquire for the status).
+ eor r3, r3, r3
+ add r2, r2, r3
+ // Check access flags has
+ // kAccClassIsFinalizable
+ ldr r3, [r2, #MIRROR_CLASS_ACCESS_FLAGS_OFFSET]
+ tst r3, #ACCESS_FLAGS_CLASS_IS_FINALIZABLE
+ bne .Lart_quick_alloc_object_rosalloc_slow_path
+
+ ldr r3, [r9, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET] // Check if the thread local
+ // allocation stack has room.
+ // TODO: consider using ldrd.
+ ldr r12, [r9, #THREAD_LOCAL_ALLOC_STACK_END_OFFSET]
+ cmp r3, r12
+ bhs .Lart_quick_alloc_object_rosalloc_slow_path
+
+ ldr r3, [r2, #MIRROR_CLASS_OBJECT_SIZE_OFFSET] // Load the object size (r3)
+ cmp r3, #ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE // Check if the size is for a thread
+ // local allocation
+ bhs .Lart_quick_alloc_object_rosalloc_slow_path
+ // Compute the rosalloc bracket index
+ // from the size.
+ // Align up the size by the rosalloc
+ // bracket quantum size and divide
+ // by the quantum size and subtract
+ // by 1. This code is a shorter but
+ // equivalent version.
+ sub r3, r3, #1
+ lsr r3, r3, #ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT
+ // Load the rosalloc run (r12)
+ add r12, r9, r3, lsl #POINTER_SIZE_SHIFT
+ ldr r12, [r12, #THREAD_ROSALLOC_RUNS_OFFSET]
+ // Load the free list head (r3). This
+ // will be the return val.
+ ldr r3, [r12, #(ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)]
+ cbz r3, .Lart_quick_alloc_object_rosalloc_slow_path
+ // "Point of no slow path". Won't go to the slow path from here on. OK to clobber r0 and r1.
+ ldr r1, [r3, #ROSALLOC_SLOT_NEXT_OFFSET] // Load the next pointer of the head
+ // and update the list head with the
+ // next pointer.
+ str r1, [r12, #(ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)]
+ // Store the class pointer in the
+ // header. This also overwrites the
+ // next pointer. The offsets are
+ // asserted to match.
+#if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET
+#error "Class pointer needs to overwrite next pointer."
+#endif
+ str r2, [r3, #MIRROR_OBJECT_CLASS_OFFSET]
+ // Push the new object onto the thread
+ // local allocation stack and
+ // increment the thread local
+ // allocation stack top.
+ ldr r1, [r9, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET]
+ str r3, [r1], #COMPRESSED_REFERENCE_SIZE // (Increment r1 as a side effect.)
+ str r1, [r9, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET]
+ // Decrement the size of the free list
+ ldr r1, [r12, #(ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)]
+ sub r1, #1
+ // TODO: consider combining this store
+ // and the list head store above using
+ // strd.
+ str r1, [r12, #(ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)]
+ // Fence. This is "ish" not "ishst" so
+ // that the code after this allocation
+ // site will see the right values in
+ // the fields of the class.
+ // Alternatively we could use "ishst"
+ // if we use load-acquire for the
+ // class status load.)
+ dmb ish
+ mov r0, r3 // Set the return value and return.
+ bx lr
+
+.Lart_quick_alloc_object_rosalloc_slow_path:
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC
+ mov r2, r9 @ pass Thread::Current
+ bl artAllocObjectFromCodeRosAlloc @ (uint32_t type_idx, Method* method, Thread*)
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+END art_quick_alloc_object_rosalloc
/*
* Called by managed code when the value in rSUSPEND has been decremented to 0.
diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S
index ef5edbb..fbacdbc 100644
--- a/runtime/arch/quick_alloc_entrypoints.S
+++ b/runtime/arch/quick_alloc_entrypoints.S
@@ -113,7 +113,8 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc_instrumented, DlMallocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc_instrumented, DlMallocInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc)
+// This is to be separately defined for each architecture to allow a hand-written assembly fast path.
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_rosalloc, RosAlloc)
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 4a106e4..2f485ae 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -788,6 +788,7 @@
// Generate the allocation entrypoints for each allocator.
GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB)
ONE_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 5c413d2..95f0ccb 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -809,6 +809,7 @@
// Generate the allocation entrypoints for each allocator.
GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc)
// A handle-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB).
DEFINE_FUNCTION art_quick_alloc_object_tlab
// Fast path tlab allocation.
diff --git a/runtime/art_code.cc b/runtime/art_code.cc
index b999ec8..ad0b170 100644
--- a/runtime/art_code.cc
+++ b/runtime/art_code.cc
@@ -286,8 +286,10 @@
return;
}
- uint32_t code_size = reinterpret_cast<const OatQuickMethodHeader*>(code)[-1].code_size_;
- CHECK(PcIsWithinQuickCode(pc))
+ uint32_t code_size = reinterpret_cast<const OatQuickMethodHeader*>(
+ EntryPointToCodePointer(code))[-1].code_size_;
+ uintptr_t code_start = reinterpret_cast<uintptr_t>(code);
+ CHECK(code_start <= pc && pc <= (code_start + code_size))
<< PrettyMethod(method_)
<< " pc=" << std::hex << pc
<< " code=" << code
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index c415073..f741732 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -219,8 +219,9 @@
case kDirect:
return !IsDirect() || IsStatic();
case kVirtual: {
+ // We have an error if we are direct or a non-default, non-miranda interface method.
mirror::Class* methods_class = GetDeclaringClass();
- return IsDirect() || (methods_class->IsInterface() && !IsMiranda());
+ return IsDirect() || (methods_class->IsInterface() && !IsDefault() && !IsMiranda());
}
case kSuper:
// Constructors and static methods are called with invoke-direct.
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 3c58644..9743250 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -135,6 +135,11 @@
return (GetAccessFlags() & kAccMiranda) != 0;
}
+ // This is set by the class linker.
+ bool IsDefault() {
+ return (GetAccessFlags() & kAccDefault) != 0;
+ }
+
bool IsNative() {
return (GetAccessFlags() & kAccNative) != 0;
}
@@ -163,6 +168,11 @@
SetAccessFlags(GetAccessFlags() | kAccPreverified);
}
+ // Returns true if this method could be overridden by a default method.
+ bool IsOverridableByDefaultMethod() {
+ return IsDefault() || IsAbstract();
+ }
+
bool CheckIncompatibleClassChange(InvokeType type) SHARED_REQUIRES(Locks::mutator_lock_);
uint16_t GetMethodIndex() SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index d98fc51..69f6fe9 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -19,6 +19,7 @@
#if defined(__cplusplus)
#include "art_method.h"
+#include "gc/allocator/rosalloc.h"
#include "lock_word.h"
#include "mirror/class.h"
#include "mirror/string.h"
@@ -53,6 +54,14 @@
#define ADD_TEST_EQ(x, y)
#endif
+#if defined(__LP64__)
+#define POINTER_SIZE_SHIFT 3
+#else
+#define POINTER_SIZE_SHIFT 2
+#endif
+ADD_TEST_EQ(static_cast<size_t>(1U << POINTER_SIZE_SHIFT),
+ static_cast<size_t>(__SIZEOF_POINTER__))
+
// Size of references to the heap on the stack.
#define STACK_REFERENCE_SIZE 4
ADD_TEST_EQ(static_cast<size_t>(STACK_REFERENCE_SIZE), sizeof(art::StackReference<art::mirror::Object>))
@@ -62,6 +71,10 @@
ADD_TEST_EQ(static_cast<size_t>(COMPRESSED_REFERENCE_SIZE),
sizeof(art::mirror::CompressedReference<art::mirror::Object>))
+#define COMPRESSED_REFERENCE_SIZE_SHIFT 2
+ADD_TEST_EQ(static_cast<size_t>(1U << COMPRESSED_REFERENCE_SIZE_SHIFT),
+ static_cast<size_t>(COMPRESSED_REFERENCE_SIZE))
+
// Note: these callee save methods loads require read barriers.
// Offset of field Runtime::callee_save_methods_[kSaveAll]
#define RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET 0
@@ -120,6 +133,18 @@
#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_LOCAL_POS_OFFSET + 2 * __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET,
art::Thread::ThreadLocalObjectsOffset<__SIZEOF_POINTER__>().Int32Value())
+// Offset of field Thread::tlsPtr_.rosalloc_runs.
+#define THREAD_ROSALLOC_RUNS_OFFSET (THREAD_LOCAL_POS_OFFSET + 3 * __SIZEOF_POINTER__)
+ADD_TEST_EQ(THREAD_ROSALLOC_RUNS_OFFSET,
+ art::Thread::RosAllocRunsOffset<__SIZEOF_POINTER__>().Int32Value())
+// Offset of field Thread::tlsPtr_.thread_local_alloc_stack_top.
+#define THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET (THREAD_ROSALLOC_RUNS_OFFSET + 34 * __SIZEOF_POINTER__)
+ADD_TEST_EQ(THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET,
+ art::Thread::ThreadLocalAllocStackTopOffset<__SIZEOF_POINTER__>().Int32Value())
+// Offset of field Thread::tlsPtr_.thread_local_alloc_stack_end.
+#define THREAD_LOCAL_ALLOC_STACK_END_OFFSET (THREAD_ROSALLOC_RUNS_OFFSET + 35 * __SIZEOF_POINTER__)
+ADD_TEST_EQ(THREAD_LOCAL_ALLOC_STACK_END_OFFSET,
+ art::Thread::ThreadLocalAllocStackEndOffset<__SIZEOF_POINTER__>().Int32Value())
// Offsets within java.lang.Object.
#define MIRROR_OBJECT_CLASS_OFFSET 0
@@ -236,6 +261,44 @@
ADD_TEST_EQ(static_cast<uint32_t>(OBJECT_ALIGNMENT_MASK_TOGGLED),
~static_cast<uint32_t>(art::kObjectAlignment - 1))
+#define ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE 128
+ADD_TEST_EQ(ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE,
+ static_cast<int32_t>(art::gc::allocator::RosAlloc::kMaxThreadLocalBracketSize))
+
+#define ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT 4
+ADD_TEST_EQ(ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT,
+ static_cast<int32_t>(art::gc::allocator::RosAlloc::kBracketQuantumSizeShift))
+
+#define ROSALLOC_BRACKET_QUANTUM_SIZE_MASK 15
+ADD_TEST_EQ(ROSALLOC_BRACKET_QUANTUM_SIZE_MASK,
+ static_cast<int32_t>(art::gc::allocator::RosAlloc::kBracketQuantumSize - 1))
+
+#define ROSALLOC_BRACKET_QUANTUM_SIZE_MASK_TOGGLED32 0xfffffff0
+ADD_TEST_EQ(static_cast<uint32_t>(ROSALLOC_BRACKET_QUANTUM_SIZE_MASK_TOGGLED32),
+ ~static_cast<uint32_t>(art::gc::allocator::RosAlloc::kBracketQuantumSize - 1))
+
+#define ROSALLOC_BRACKET_QUANTUM_SIZE_MASK_TOGGLED64 0xfffffffffffffff0
+ADD_TEST_EQ(static_cast<uint64_t>(ROSALLOC_BRACKET_QUANTUM_SIZE_MASK_TOGGLED64),
+ ~static_cast<uint64_t>(art::gc::allocator::RosAlloc::kBracketQuantumSize - 1))
+
+#define ROSALLOC_RUN_FREE_LIST_OFFSET 8
+ADD_TEST_EQ(ROSALLOC_RUN_FREE_LIST_OFFSET,
+ static_cast<int32_t>(art::gc::allocator::RosAlloc::RunFreeListOffset()))
+
+#define ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET 0
+ADD_TEST_EQ(ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET,
+ static_cast<int32_t>(art::gc::allocator::RosAlloc::RunFreeListHeadOffset()))
+
+#define ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET 16
+ADD_TEST_EQ(ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET,
+ static_cast<int32_t>(art::gc::allocator::RosAlloc::RunFreeListSizeOffset()))
+
+#define ROSALLOC_SLOT_NEXT_OFFSET 0
+ADD_TEST_EQ(ROSALLOC_SLOT_NEXT_OFFSET,
+ static_cast<int32_t>(art::gc::allocator::RosAlloc::RunSlotNextOffset()))
+// Assert this so that we can avoid zeroing the next field by installing the class pointer.
+ADD_TEST_EQ(ROSALLOC_SLOT_NEXT_OFFSET, MIRROR_OBJECT_CLASS_OFFSET)
+
#if defined(__cplusplus)
} // End of CheckAsmSupportOffsets.
#endif
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 30bfb4a..70bd398 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -51,6 +51,7 @@
MutatorMutex* Locks::mutator_lock_ = nullptr;
Mutex* Locks::profiler_lock_ = nullptr;
ReaderWriterMutex* Locks::oat_file_manager_lock_ = nullptr;
+ReaderWriterMutex* Locks::oat_file_count_lock_ = nullptr;
Mutex* Locks::reference_processor_lock_ = nullptr;
Mutex* Locks::reference_queue_cleared_references_lock_ = nullptr;
Mutex* Locks::reference_queue_finalizer_references_lock_ = nullptr;
@@ -942,6 +943,7 @@
DCHECK(deoptimization_lock_ != nullptr);
DCHECK(heap_bitmap_lock_ != nullptr);
DCHECK(oat_file_manager_lock_ != nullptr);
+ DCHECK(oat_file_count_lock_ != nullptr);
DCHECK(intern_table_lock_ != nullptr);
DCHECK(jni_libraries_lock_ != nullptr);
DCHECK(logging_lock_ != nullptr);
@@ -1034,6 +1036,10 @@
DCHECK(oat_file_manager_lock_ == nullptr);
oat_file_manager_lock_ = new ReaderWriterMutex("OatFile manager lock", current_lock_level);
+ UPDATE_CURRENT_LOCK_LEVEL(kOatFileCountLock);
+ DCHECK(oat_file_count_lock_ == nullptr);
+ oat_file_count_lock_ = new ReaderWriterMutex("OatFile count lock", current_lock_level);
+
UPDATE_CURRENT_LOCK_LEVEL(kInternTableLock);
DCHECK(intern_table_lock_ == nullptr);
intern_table_lock_ = new Mutex("InternTable lock", current_lock_level);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 17f6a03..d4c9057 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -83,6 +83,7 @@
kDexFileToMethodInlinerMapLock,
kInternTableLock,
kOatFileSecondaryLookupLock,
+ kOatFileCountLock,
kOatFileManagerLock,
kTracingUniqueMethodsLock,
kTracingStreamingLock,
@@ -648,8 +649,11 @@
// Guards opened oat files in OatFileManager.
static ReaderWriterMutex* oat_file_manager_lock_ ACQUIRED_AFTER(modify_ldt_lock_);
+ // Guards opened oat files in OatFileManager.
+ static ReaderWriterMutex* oat_file_count_lock_ ACQUIRED_AFTER(oat_file_manager_lock_);
+
// Guards intern table.
- static Mutex* intern_table_lock_ ACQUIRED_AFTER(oat_file_manager_lock_);
+ static Mutex* intern_table_lock_ ACQUIRED_AFTER(oat_file_count_lock_);
// Guards reference processor.
static Mutex* reference_processor_lock_ ACQUIRED_AFTER(intern_table_lock_);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 9349fe3..b569f50 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -16,12 +16,15 @@
#include "class_linker.h"
+#include <algorithm>
#include <deque>
#include <iostream>
#include <memory>
#include <queue>
#include <string>
+#include <tuple>
#include <unistd.h>
+#include <unordered_map>
#include <utility>
#include <vector>
@@ -41,25 +44,20 @@
#include "compiler_callbacks.h"
#include "debugger.h"
#include "dex_file-inl.h"
+#include "entrypoints/entrypoint_utils.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "gc_root-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap.h"
#include "gc/heap.h"
#include "gc/space/image_space.h"
-#include "handle_scope.h"
+#include "handle_scope-inl.h"
#include "intern_table.h"
#include "interpreter/interpreter.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
#include "leb128.h"
#include "linear_alloc.h"
-#include "oat.h"
-#include "oat_file.h"
-#include "oat_file-inl.h"
-#include "oat_file_assistant.h"
-#include "oat_file_manager.h"
-#include "object_lock.h"
#include "mirror/class.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
@@ -73,12 +71,17 @@
#include "mirror/reference-inl.h"
#include "mirror/stack_trace_element.h"
#include "mirror/string-inl.h"
+#include "native/dalvik_system_DexFile.h"
+#include "oat.h"
+#include "oat_file.h"
+#include "oat_file-inl.h"
+#include "oat_file_assistant.h"
+#include "oat_file_manager.h"
+#include "object_lock.h"
#include "os.h"
#include "runtime.h"
-#include "entrypoints/entrypoint_utils.h"
#include "ScopedLocalRef.h"
#include "scoped_thread_state_change.h"
-#include "handle_scope-inl.h"
#include "thread-inl.h"
#include "trace.h"
#include "utils.h"
@@ -1429,13 +1432,18 @@
break;
}
int32_t long_array_size = long_array->GetLength();
- for (int32_t j = 0; j < long_array_size; ++j) {
+ // First element is the oat file.
+ for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) {
const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(
long_array->GetWithoutChecks(j)));
const DexFile::ClassDef* dex_class_def = cp_dex_file->FindClassDef(descriptor, hash);
if (dex_class_def != nullptr) {
- mirror::Class* klass = DefineClass(self, descriptor, hash, class_loader,
- *cp_dex_file, *dex_class_def);
+ mirror::Class* klass = DefineClass(self,
+ descriptor,
+ hash,
+ class_loader,
+ *cp_dex_file,
+ *dex_class_def);
if (klass == nullptr) {
CHECK(self->IsExceptionPending()) << descriptor;
self->ClearException();
@@ -3337,6 +3345,18 @@
return false;
}
}
+ // If we are a class we need to initialize all interfaces with default methods when we are
+ // initialized. Check all of them.
+ if (!klass->IsInterface()) {
+ size_t num_interfaces = klass->GetIfTableCount();
+ for (size_t i = 0; i < num_interfaces; i++) {
+ mirror::Class* iface = klass->GetIfTable()->GetInterface(i);
+ if (iface->HasDefaultMethods() &&
+ !CanWeInitializeClass(iface, can_init_statics, can_init_parents)) {
+ return false;
+ }
+ }
+ }
}
if (klass->IsInterface() || !klass->HasSuperClass()) {
return true;
@@ -3463,6 +3483,38 @@
}
}
+ if (!klass->IsInterface()) {
+ // Initialize interfaces with default methods for the JLS.
+ size_t num_direct_interfaces = klass->NumDirectInterfaces();
+ // Only setup the (expensive) handle scope if we actually need to.
+ if (UNLIKELY(num_direct_interfaces > 0)) {
+ StackHandleScope<1> hs_iface(self);
+ MutableHandle<mirror::Class> handle_scope_iface(hs_iface.NewHandle<mirror::Class>(nullptr));
+ for (size_t i = 0; i < num_direct_interfaces; i++) {
+ handle_scope_iface.Assign(mirror::Class::GetDirectInterface(self, klass, i));
+ CHECK(handle_scope_iface.Get() != nullptr);
+ CHECK(handle_scope_iface->IsInterface());
+ if (handle_scope_iface->HasBeenRecursivelyInitialized()) {
+ // We have already done this for this interface. Skip it.
+ continue;
+ }
+ // We cannot just call initialize class directly because we need to ensure that ALL
+ // interfaces with default methods are initialized. Non-default interface initialization
+ // will not affect other non-default super-interfaces.
+ bool iface_initialized = InitializeDefaultInterfaceRecursive(self,
+ handle_scope_iface,
+ can_init_statics,
+ can_init_parents);
+ if (!iface_initialized) {
+ ObjectLock<mirror::Class> lock(self, klass);
+ // Initialization failed because one of our interfaces with default methods is erroneous.
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ return false;
+ }
+ }
+ }
+ }
+
const size_t num_static_fields = klass->NumStaticFields();
if (num_static_fields > 0) {
const DexFile::ClassDef* dex_class_def = klass->GetClassDef();
@@ -3552,6 +3604,52 @@
return success;
}
+// We recursively run down the tree of interfaces. We need to do this in the order they are declared
+// and perform the initialization only on those interfaces that contain default methods.
+bool ClassLinker::InitializeDefaultInterfaceRecursive(Thread* self,
+ Handle<mirror::Class> iface,
+ bool can_init_statics,
+ bool can_init_parents) {
+ CHECK(iface->IsInterface());
+ size_t num_direct_ifaces = iface->NumDirectInterfaces();
+ // Only create the (expensive) handle scope if we need it.
+ if (UNLIKELY(num_direct_ifaces > 0)) {
+ StackHandleScope<1> hs(self);
+ MutableHandle<mirror::Class> handle_super_iface(hs.NewHandle<mirror::Class>(nullptr));
+ // First we initialize all of iface's super-interfaces recursively.
+ for (size_t i = 0; i < num_direct_ifaces; i++) {
+ mirror::Class* super_iface = mirror::Class::GetDirectInterface(self, iface, i);
+ if (!super_iface->HasBeenRecursivelyInitialized()) {
+ // Recursive step
+ handle_super_iface.Assign(super_iface);
+ if (!InitializeDefaultInterfaceRecursive(self,
+ handle_super_iface,
+ can_init_statics,
+ can_init_parents)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ bool result = true;
+ // Then we initialize 'iface' if it has default methods. We do not need to (and in fact must not)
+ // initialize if we don't have default methods.
+ if (iface->HasDefaultMethods()) {
+ result = EnsureInitialized(self, iface, can_init_statics, can_init_parents);
+ }
+
+ // Mark that this interface has undergone recursive default interface initialization so we know we
+ // can skip it on any later class initializations. We do this even if we are not a default
+ // interface since we can still avoid the traversal. This is purely a performance optimization.
+ if (result) {
+ // TODO This should be done in a better way
+ ObjectLock<mirror::Class> lock(self, iface);
+ iface->SetRecursivelyInitialized();
+ }
+ return result;
+}
+
bool ClassLinker::WaitForInitializeClass(Handle<mirror::Class> klass,
Thread* self,
ObjectLock<mirror::Class>& lock)
@@ -4284,20 +4382,16 @@
Handle<mirror::ObjectArray<mirror::Class>> interfaces,
ArtMethod** out_imt) {
self->AllowThreadSuspension();
- if (klass->IsInterface()) {
- // No vtable.
- size_t count = klass->NumVirtualMethods();
- if (!IsUint<16>(count)) {
- ThrowClassFormatError(klass.Get(), "Too many methods on interface: %zd", count);
- return false;
- }
- for (size_t i = 0; i < count; ++i) {
- klass->GetVirtualMethodDuringLinking(i, image_pointer_size_)->SetMethodIndex(i);
- }
- } else if (!LinkVirtualMethods(self, klass)) { // Link virtual methods first.
- return false;
- }
- return LinkInterfaceMethods(self, klass, interfaces, out_imt); // Link interface method last.
+ // A map from vtable indexes to the method they need to be updated to point to. Used because we
+ // need to have default methods be in the virtuals array of each class but we don't set that up
+ // until LinkInterfaceMethods.
+ std::unordered_map<size_t, ArtMethod*> default_translations;
+ // Link virtual methods then interface methods.
+ // We set up the interface lookup table first because we need it to determine if we need to update
+ // any vtable entries with new default method implementations.
+ return SetupInterfaceLookupTable(self, klass, interfaces)
+ && LinkVirtualMethods(self, klass, /*out*/ &default_translations)
+ && LinkInterfaceMethods(self, klass, default_translations, out_imt);
}
// Comparator for name and signature of a method, used in finding overriding methods. Implementation
@@ -4421,9 +4515,36 @@
const uint32_t LinkVirtualHashTable::invalid_index_ = std::numeric_limits<uint32_t>::max();
const uint32_t LinkVirtualHashTable::removed_index_ = std::numeric_limits<uint32_t>::max() - 1;
-bool ClassLinker::LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass) {
+bool ClassLinker::LinkVirtualMethods(
+ Thread* self,
+ Handle<mirror::Class> klass,
+ /*out*/std::unordered_map<size_t, ArtMethod*>* default_translations) {
const size_t num_virtual_methods = klass->NumVirtualMethods();
- if (klass->HasSuperClass()) {
+ if (klass->IsInterface()) {
+ // No vtable.
+ if (!IsUint<16>(num_virtual_methods)) {
+ ThrowClassFormatError(klass.Get(), "Too many methods on interface: %zu", num_virtual_methods);
+ return false;
+ }
+ bool has_defaults = false;
+ // TODO May need to replace this with real VTable for invoke_super
+ // Assign each method an IMT index and set the default flag.
+ for (size_t i = 0; i < num_virtual_methods; ++i) {
+ ArtMethod* m = klass->GetVirtualMethodDuringLinking(i, image_pointer_size_);
+ m->SetMethodIndex(i);
+ if (!m->IsAbstract()) {
+ m->SetAccessFlags(m->GetAccessFlags() | kAccDefault);
+ has_defaults = true;
+ }
+ }
+ // Mark that we have default methods so that we won't need to scan the virtual_methods_ array
+ // during initialization. This is a performance optimization. We could simply traverse the
+ // virtual_methods_ array again during initialization.
+ if (has_defaults) {
+ klass->SetHasDefaultMethods();
+ }
+ return true;
+ } else if (klass->HasSuperClass()) {
const size_t super_vtable_length = klass->GetSuperClass()->GetVTableLength();
const size_t max_count = num_virtual_methods + super_vtable_length;
StackHandleScope<2> hs(self);
@@ -4439,14 +4560,22 @@
vtable->SetElementPtrSize(
i, super_class->GetEmbeddedVTableEntry(i, image_pointer_size_), image_pointer_size_);
}
- if (num_virtual_methods == 0) {
+ // We might need to change vtable if we have new virtual methods or new interfaces (since that
+ // might give us new default methods). If no new interfaces then we can skip the rest since
+ // the class cannot override any of the super-class's methods. This is required for
+ // correctness since without it we might not update overridden default method vtable entries
+ // correctly.
+ if (num_virtual_methods == 0 && super_class->GetIfTableCount() == klass->GetIfTableCount()) {
klass->SetVTable(vtable.Get());
return true;
}
} else {
+ DCHECK(super_class->IsAbstract() && !super_class->IsArrayClass());
auto* super_vtable = super_class->GetVTable();
CHECK(super_vtable != nullptr) << PrettyClass(super_class.Get());
- if (num_virtual_methods == 0) {
+ // We might need to change vtable if we have new virtual methods or new interfaces (since that
+ // might give us new default methods). See comment above.
+ if (num_virtual_methods == 0 && super_class->GetIfTableCount() == klass->GetIfTableCount()) {
klass->SetVTable(super_vtable);
return true;
}
@@ -4467,7 +4596,9 @@
// the need for the initial vtable which we later shrink back down).
// 3. Add non overridden methods to the end of the vtable.
static constexpr size_t kMaxStackHash = 250;
- const size_t hash_table_size = num_virtual_methods * 3;
+ // + 1 so that even if we only have new default methods we will still be able to use this hash
+ // table (i.e. it will never have 0 size).
+ const size_t hash_table_size = num_virtual_methods * 3 + 1;
uint32_t* hash_table_ptr;
std::unique_ptr<uint32_t[]> hash_heap_storage;
if (hash_table_size <= kMaxStackHash) {
@@ -4484,10 +4615,10 @@
i, image_pointer_size_)->GetDeclaringClass() != nullptr);
hash_table.Add(i);
}
- // Loop through each super vtable method and see if they are overriden by a method we added to
+ // Loop through each super vtable method and see if they are overridden by a method we added to
// the hash table.
for (size_t j = 0; j < super_vtable_length; ++j) {
- // Search the hash table to see if we are overidden by any method.
+ // Search the hash table to see if we are overridden by any method.
ArtMethod* super_method = vtable->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_);
MethodNameAndSignatureComparator super_method_name_comparator(
super_method->GetInterfaceMethodIfProxy(image_pointer_size_));
@@ -4510,10 +4641,51 @@
<< " would have incorrectly overridden the package-private method in "
<< PrettyDescriptor(super_method->GetDeclaringClassDescriptor());
}
+ } else if (super_method->IsDefault()) {
+ // We didn't directly override this method but we might through default methods...
+ // Check for default method update.
+ ArtMethod* default_method = nullptr;
+ std::string icce_message;
+ if (!FindDefaultMethodImplementation(self,
+ super_method,
+ klass,
+ /*out*/&default_method,
+ /*out*/&icce_message)) {
+ // An error occurred while finding default methods.
+ // TODO This should actually be thrown when we attempt to invoke this method.
+ ThrowIncompatibleClassChangeError(klass.Get(), "%s", icce_message.c_str());
+ return false;
+ }
+ // This should always work because we inherit superclass interfaces. We should either get
+ // 1) An IncompatibleClassChangeError because of conflicting default method
+ // implementations.
+ // 2) The same default method implementation as the superclass.
+ // 3) A default method that overrides the superclass's.
+ // Therefore this check should never fail.
+ CHECK(default_method != nullptr);
+ if (UNLIKELY(default_method->GetDeclaringClass() != super_method->GetDeclaringClass())) {
+ // TODO Refactor this add default methods to virtuals here and not in
+ // LinkInterfaceMethods maybe.
+ // The problem is default methods might override previously present default-method or
+ // miranda-method vtable entries from the superclass. Unfortunately we need these to
+ // be entries in this class's virtuals. We do not give these entries there until
+ // LinkInterfaceMethods so we pass this map around to let it know which vtable
+ // entries need to be updated.
+ // Make a note that vtable entry j must be updated, store what it needs to be updated to.
+ // We will allocate a virtual method slot in LinkInterfaceMethods and fix it up then.
+ default_translations->insert({j, default_method});
+ VLOG(class_linker) << "Method " << PrettyMethod(super_method) << " overridden by default "
+ << PrettyMethod(default_method) << " in " << PrettyClass(klass.Get());
+ } else {
+ // They are the same method/no override
+ // Cannot do direct comparison because we had to copy the ArtMethod object into the
+ // superclass's vtable.
+ continue;
+ }
}
}
- // Add the non overridden methods at the end.
size_t actual_count = super_vtable_length;
+ // Add the non-overridden methods at the end.
for (size_t i = 0; i < num_virtual_methods; ++i) {
ArtMethod* local_method = klass->GetVirtualMethodDuringLinking(i, image_pointer_size_);
size_t method_idx = local_method->GetMethodIndexDuringLinking();
@@ -4561,20 +4733,223 @@
return true;
}
-bool ClassLinker::LinkInterfaceMethods(Thread* self,
- Handle<mirror::Class> klass,
- Handle<mirror::ObjectArray<mirror::Class>> interfaces,
- ArtMethod** out_imt) {
- StackHandleScope<3> hs(self);
- Runtime* const runtime = Runtime::Current();
- const bool has_superclass = klass->HasSuperClass();
- const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U;
+// Find the default method implementation for 'interface_method' in 'klass'. Stores it into
+// out_default_method and returns true on success. If no default method was found stores nullptr
+// into out_default_method and returns true. If an error occurs (such as a default_method conflict)
+// it will fill the icce_message with an appropriate message for an IncompatibleClassChangeError,
+// which should then be thrown by the caller.
+bool ClassLinker::FindDefaultMethodImplementation(Thread* self,
+ ArtMethod* target_method,
+ Handle<mirror::Class> klass,
+ /*out*/ArtMethod** out_default_method,
+ /*out*/std::string* icce_message) const {
+ DCHECK(self != nullptr);
+ DCHECK(target_method != nullptr);
+ DCHECK(out_default_method != nullptr);
+ DCHECK(icce_message != nullptr);
+
+ *out_default_method = nullptr;
+ mirror::Class* chosen_iface = nullptr;
+
+ // We organize the interface table so that, for interface I any subinterfaces J follow it in the
+ // table. This lets us walk the table backwards when searching for default methods. The first one
+ // we encounter is the best candidate since it is the most specific. Once we have found it we keep
+ // track of it and then continue checking all other interfaces, since we need to throw an error if
+ // we encounter conflicting default method implementations (one is not a subtype of the other).
+ //
+ // The order of unrelated interfaces does not matter and is not defined.
+ size_t iftable_count = klass->GetIfTableCount();
+ if (iftable_count == 0) {
+ // No interfaces. We have already reset out to null so just return true.
+ return true;
+ }
+
+ StackHandleScope<1> hs(self);
+ MutableHandle<mirror::IfTable> iftable(hs.NewHandle(klass->GetIfTable()));
+ MethodNameAndSignatureComparator target_name_comparator(
+ target_method->GetInterfaceMethodIfProxy(image_pointer_size_));
+ // Iterates over the klass's iftable in reverse
+ // We have a break at the end because size_t is unsigned.
+ for (size_t k = iftable_count - 1; /* break if k == 0 at end */; --k) {
+ DCHECK_LT(k, iftable->Count());
+ mirror::Class* iface = iftable->GetInterface(k);
+ size_t num_instance_methods = iface->NumVirtualMethods();
+ // Iterate through every method on this interface. The order does not matter so we go forwards.
+ for (size_t m = 0; m < num_instance_methods; m++) {
+ ArtMethod* current_method = iface->GetVirtualMethodUnchecked(m, image_pointer_size_);
+ // Skip abstract methods and methods with different names.
+ if (current_method->IsAbstract() ||
+ !target_name_comparator.HasSameNameAndSignature(
+ current_method->GetInterfaceMethodIfProxy(image_pointer_size_))) {
+ continue;
+ }
+ // The verifier should have caught the non-public method.
+ DCHECK(current_method->IsPublic()) << "Interface method is not public!";
+ if (UNLIKELY(chosen_iface != nullptr)) {
+ // We have multiple default impls of the same method. We need to check they do not
+ // conflict and throw an error if they do. Conflicting means that the current iface is not
+ // masked by the chosen interface.
+ if (!iface->IsAssignableFrom(chosen_iface)) {
+ *icce_message = StringPrintf("Conflicting default method implementations: '%s' and '%s'",
+ PrettyMethod(current_method).c_str(),
+ PrettyMethod(*out_default_method).c_str());
+ return false;
+ } else {
+ break; // Continue checking at the next interface.
+ }
+ } else {
+ *out_default_method = current_method;
+ chosen_iface = iface;
+ // We should now finish traversing the graph to find if we have default methods that
+ // conflict.
+ break;
+ }
+ }
+ if (k == 0) {
+ break;
+ }
+ }
+ return true;
+}
+
+// Sets imt_ref appropriately for LinkInterfaceMethods.
+// If there is no method in the imt location of imt_ref it will store the given method there.
+// Otherwise it will set the conflict method which will figure out which method to use during
+// runtime.
+static void SetIMTRef(ArtMethod* unimplemented_method,
+ ArtMethod* conflict_method,
+ size_t image_pointer_size,
+ ArtMethod* current_method,
+ /*out*/ArtMethod** imt_ref)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ // Place method in imt if entry is empty, place conflict otherwise.
+ if (*imt_ref == unimplemented_method) {
+ *imt_ref = current_method;
+ } else if (*imt_ref != conflict_method) {
+ // If we are not a conflict and we have the same signature and name as the imt
+ // entry, it must be that we overwrote a superclass vtable entry.
+ MethodNameAndSignatureComparator imt_comparator(
+ (*imt_ref)->GetInterfaceMethodIfProxy(image_pointer_size));
+ if (imt_comparator.HasSameNameAndSignature(
+ current_method->GetInterfaceMethodIfProxy(image_pointer_size))) {
+ *imt_ref = current_method;
+ } else {
+ *imt_ref = conflict_method;
+ }
+ }
+}
+
+// Simple helper function that checks that no subtypes of 'val' are contained within the 'classes'
+// set.
+static bool NotSubinterfaceOfAny(const std::unordered_set<mirror::Class*>& classes,
+ mirror::Class* val)
+ REQUIRES(Roles::uninterruptible_)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(val != nullptr);
+ for (auto c : classes) {
+ if (val->IsAssignableFrom(&*c)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// Fills in and flattens the interface inheritance hierarchy.
+//
+// By the end of this function all interfaces in the transitive closure of to_process are added to
+// the iftable and every interface precedes all of its sub-interfaces in this list.
+//
+// all I, J: Interface | I <: J implies J precedes I
+//
+// (note A <: B means that A is a subtype of B)
+//
+// This returns the total number of items in the iftable. The iftable might be resized down after
+// this call.
+//
+// We order this backwards so that we do not need to reorder superclass interfaces when new
+// interfaces are added in subclass's interface tables.
+//
+// Upon entry into this function iftable is a copy of the superclass's iftable with the first
+// super_ifcount entries filled in with the transitive closure of the interfaces of the superclass.
+// The other entries are uninitialized. We will fill in the remaining entries in this function. The
+// iftable must be large enough to hold all interfaces without changing its size.
+static size_t FillIfTable(mirror::IfTable* iftable,
+ size_t super_ifcount,
+ std::vector<mirror::Class*> to_process)
+ REQUIRES(Roles::uninterruptible_)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ // This is the set of all class's already in the iftable. Used to make checking if a class has
+ // already been added quicker.
+ std::unordered_set<mirror::Class*> classes_in_iftable;
+ // The first super_ifcount elements are from the superclass. We note that they are already added.
+ for (size_t i = 0; i < super_ifcount; i++) {
+ mirror::Class* iface = iftable->GetInterface(i);
+ DCHECK(NotSubinterfaceOfAny(classes_in_iftable, iface)) << "Bad ordering.";
+ classes_in_iftable.insert(iface);
+ }
+ size_t filled_ifcount = super_ifcount;
+ for (mirror::Class* interface : to_process) {
+ // Let us call the first filled_ifcount elements of iftable the current-iface-list.
+ // At this point in the loop current-iface-list has the invariant that:
+ // for every pair of interfaces I,J within it:
+ // if index_of(I) < index_of(J) then I is not a subtype of J
+
+ // If we have already seen this element then all of its super-interfaces must already be in the
+ // current-iface-list so we can skip adding it.
+ if (!ContainsElement(classes_in_iftable, interface)) {
+ // We haven't seen this interface so add all of its super-interfaces onto the
+ // current-iface-list, skipping those already on it.
+ int32_t ifcount = interface->GetIfTableCount();
+ for (int32_t j = 0; j < ifcount; j++) {
+ mirror::Class* super_interface = interface->GetIfTable()->GetInterface(j);
+ if (!ContainsElement(classes_in_iftable, super_interface)) {
+ DCHECK(NotSubinterfaceOfAny(classes_in_iftable, super_interface)) << "Bad ordering.";
+ classes_in_iftable.insert(super_interface);
+ iftable->SetInterface(filled_ifcount, super_interface);
+ filled_ifcount++;
+ }
+ }
+ DCHECK(NotSubinterfaceOfAny(classes_in_iftable, interface)) << "Bad ordering";
+ // Place this interface onto the current-iface-list after all of its super-interfaces.
+ classes_in_iftable.insert(interface);
+ iftable->SetInterface(filled_ifcount, interface);
+ filled_ifcount++;
+ } else if (kIsDebugBuild) {
+ // Check all super-interfaces are already in the list.
+ int32_t ifcount = interface->GetIfTableCount();
+ for (int32_t j = 0; j < ifcount; j++) {
+ mirror::Class* super_interface = interface->GetIfTable()->GetInterface(j);
+ DCHECK(ContainsElement(classes_in_iftable, super_interface))
+ << "Iftable does not contain " << PrettyClass(super_interface)
+ << ", a superinterface of " << PrettyClass(interface);
+ }
+ }
+ }
+ if (kIsDebugBuild) {
+ // Check that the iftable is ordered correctly.
+ for (size_t i = 0; i < filled_ifcount; i++) {
+ mirror::Class* if_a = iftable->GetInterface(i);
+ for (size_t j = i + 1; j < filled_ifcount; j++) {
+ mirror::Class* if_b = iftable->GetInterface(j);
+ // !(if_a <: if_b)
+ CHECK(!if_b->IsAssignableFrom(if_a))
+ << "Bad interface order: " << PrettyClass(if_a) << " (index " << i << ") extends "
+ << PrettyClass(if_b) << " (index " << j << ") and so should be after it in the "
+ << "interface list.";
+ }
+ }
+ }
+ return filled_ifcount;
+}
+
+bool ClassLinker::SetupInterfaceLookupTable(Thread* self, Handle<mirror::Class> klass,
+ Handle<mirror::ObjectArray<mirror::Class>> interfaces) {
+ StackHandleScope<1> hs(self);
+ const size_t super_ifcount =
+ klass->HasSuperClass() ? klass->GetSuperClass()->GetIfTableCount() : 0U;
const bool have_interfaces = interfaces.Get() != nullptr;
- const size_t num_interfaces = have_interfaces
- ? interfaces->GetLength()
- : klass->NumDirectInterfaces();
- const size_t method_alignment = ArtMethod::Alignment(image_pointer_size_);
- const size_t method_size = ArtMethod::Size(image_pointer_size_);
+ const size_t num_interfaces =
+ have_interfaces ? interfaces->GetLength() : klass->NumDirectInterfaces();
if (num_interfaces == 0) {
if (super_ifcount == 0) {
// Class implements no interfaces.
@@ -4598,6 +4973,7 @@
}
}
size_t ifcount = super_ifcount + num_interfaces;
+ // Check that every class being implemented is an interface.
for (size_t i = 0; i < num_interfaces; i++) {
mirror::Class* interface = have_interfaces
? interfaces->GetWithoutChecks(i)
@@ -4613,11 +4989,13 @@
}
ifcount += interface->GetIfTableCount();
}
+ // Create the interface function table.
MutableHandle<mirror::IfTable> iftable(hs.NewHandle(AllocIfTable(self, ifcount)));
if (UNLIKELY(iftable.Get() == nullptr)) {
self->AssertPendingOOMException();
return false;
}
+ // Fill in table with superclass's iftable.
if (super_ifcount != 0) {
mirror::IfTable* super_iftable = klass->GetSuperClass()->GetIfTable();
for (size_t i = 0; i < super_ifcount; i++) {
@@ -4625,56 +5003,59 @@
iftable->SetInterface(i, super_interface);
}
}
+
+ // Note that AllowThreadSuspension is to thread suspension as pthread_testcancel is to pthread
+ // cancellation. That is it will suspend if one has a pending suspend request but otherwise
+ // doesn't really do anything.
self->AllowThreadSuspension();
- // Flatten the interface inheritance hierarchy.
- size_t idx = super_ifcount;
- for (size_t i = 0; i < num_interfaces; i++) {
- mirror::Class* interface = have_interfaces ? interfaces->Get(i) :
- mirror::Class::GetDirectInterface(self, klass, i);
- // Check if interface is already in iftable
- bool duplicate = false;
- for (size_t j = 0; j < idx; j++) {
- mirror::Class* existing_interface = iftable->GetInterface(j);
- if (existing_interface == interface) {
- duplicate = true;
- break;
- }
+
+ size_t new_ifcount;
+ {
+ ScopedAssertNoThreadSuspension nts(self, "Copying mirror::Class*'s for FillIfTable");
+ std::vector<mirror::Class*> to_add;
+ for (size_t i = 0; i < num_interfaces; i++) {
+ mirror::Class* interface = have_interfaces ? interfaces->Get(i) :
+ mirror::Class::GetDirectInterface(self, klass, i);
+ to_add.push_back(interface);
}
- if (!duplicate) {
- // Add this non-duplicate interface.
- iftable->SetInterface(idx++, interface);
- // Add this interface's non-duplicate super-interfaces.
- for (int32_t j = 0; j < interface->GetIfTableCount(); j++) {
- mirror::Class* super_interface = interface->GetIfTable()->GetInterface(j);
- bool super_duplicate = false;
- for (size_t k = 0; k < idx; k++) {
- mirror::Class* existing_interface = iftable->GetInterface(k);
- if (existing_interface == super_interface) {
- super_duplicate = true;
- break;
- }
- }
- if (!super_duplicate) {
- iftable->SetInterface(idx++, super_interface);
- }
- }
- }
+
+ new_ifcount = FillIfTable(iftable.Get(), super_ifcount, std::move(to_add));
}
+
self->AllowThreadSuspension();
+
// Shrink iftable in case duplicates were found
- if (idx < ifcount) {
+ if (new_ifcount < ifcount) {
DCHECK_NE(num_interfaces, 0U);
iftable.Assign(down_cast<mirror::IfTable*>(
- iftable->CopyOf(self, idx * mirror::IfTable::kMax)));
+ iftable->CopyOf(self, new_ifcount * mirror::IfTable::kMax)));
if (UNLIKELY(iftable.Get() == nullptr)) {
self->AssertPendingOOMException();
return false;
}
- ifcount = idx;
+ ifcount = new_ifcount;
} else {
- DCHECK_EQ(idx, ifcount);
+ DCHECK_EQ(new_ifcount, ifcount);
}
klass->SetIfTable(iftable.Get());
+ return true;
+}
+
+bool ClassLinker::LinkInterfaceMethods(
+ Thread* self,
+ Handle<mirror::Class> klass,
+ const std::unordered_map<size_t, ArtMethod*>& default_translations,
+ ArtMethod** out_imt) {
+ StackHandleScope<3> hs(self);
+ Runtime* const runtime = Runtime::Current();
+ const bool has_superclass = klass->HasSuperClass();
+ const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U;
+ const size_t method_alignment = ArtMethod::Alignment(image_pointer_size_);
+ const size_t method_size = ArtMethod::Size(image_pointer_size_);
+ const size_t ifcount = klass->GetIfTableCount();
+
+ MutableHandle<mirror::IfTable> iftable(hs.NewHandle(klass->GetIfTable()));
+
// If we're an interface, we don't need the vtable pointers, so we're done.
if (klass->IsInterface()) {
return true;
@@ -4687,6 +5068,7 @@
ArenaStack stack(runtime->GetLinearAlloc()->GetArenaPool());
ScopedArenaAllocator allocator(&stack);
ScopedArenaVector<ArtMethod*> miranda_methods(allocator.Adapter());
+ ScopedArenaVector<ArtMethod*> default_methods(allocator.Adapter());
MutableHandle<mirror::PointerArray> vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
ArtMethod* const unimplemented_method = runtime->GetImtUnimplementedMethod();
@@ -4716,7 +5098,9 @@
for (size_t j = 0; j < num_virtuals; ++j) {
auto method = method_array->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_);
DCHECK(method != nullptr) << PrettyClass(super_class);
- if (method->IsMiranda()) {
+ // Miranda methods cannot be used to implement an interface method and defaults should be
+ // skipped in case we override it.
+ if (method->IsDefault() || method->IsMiranda()) {
continue;
}
ArtMethod* interface_method = interface->GetVirtualMethod(j, image_pointer_size_);
@@ -4737,6 +5121,8 @@
size_t num_methods = iftable->GetInterface(i)->NumVirtualMethods();
if (num_methods > 0) {
const bool is_super = i < super_ifcount;
+ // This is an interface implemented by a super-class. Therefore we can just copy the method
+ // array from the superclass.
const bool super_interface = is_super && extend_super_iftable;
mirror::PointerArray* method_array;
if (super_interface) {
@@ -4780,16 +5166,13 @@
input_vtable_array = vtable;
input_array_length = input_vtable_array->GetLength();
}
- if (input_array_length == 0) {
- // If the added virtual methods is empty, do nothing.
- DCHECK(super_interface);
- continue;
- }
+ // For each method in interface
for (size_t j = 0; j < num_methods; ++j) {
auto* interface_method = iftable->GetInterface(i)->GetVirtualMethod(j, image_pointer_size_);
MethodNameAndSignatureComparator interface_name_comparator(
interface_method->GetInterfaceMethodIfProxy(image_pointer_size_));
- int32_t k;
+ uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
+ ArtMethod** imt_ptr = &out_imt[imt_index];
// For each method listed in the interface's method list, find the
// matching method in our class's method list. We want to favor the
// subclass over the superclass, which just requires walking
@@ -4798,7 +5181,12 @@
// it -- otherwise it would use the same vtable slot. In .dex files
// those don't end up in the virtual method table, so it shouldn't
// matter which direction we go. We walk it backward anyway.)
- for (k = input_array_length - 1; k >= 0; --k) {
+ //
+ // To find defaults we need to do the same but also go over interfaces.
+ bool found_impl = false;
+ ArtMethod* default_impl = nullptr;
+ bool found_default_impl = false;
+ for (int32_t k = input_array_length - 1; k >= 0; --k) {
ArtMethod* vtable_method = input_virtual_methods != nullptr ?
&input_virtual_methods->At(k, method_size, method_alignment) :
input_vtable_array->GetElementPtrSize<ArtMethod*>(k, image_pointer_size_);
@@ -4814,25 +5202,69 @@
"Method '%s' implementing interface method '%s' is not public",
PrettyMethod(vtable_method).c_str(), PrettyMethod(interface_method).c_str());
return false;
+ } else if (vtable_method->IsDefault()) {
+ // We might have a newer, better, default method for this, so we just skip it. If we
+ // are still using this we will select it again when scanning for default methods. To
+ // obviate the need to copy the method again we will make a note that we already found
+ // a default here.
+ // TODO This should be much cleaner.
+ found_default_impl = true;
+ default_impl = vtable_method;
+ break;
+ } else {
+ found_impl = true;
}
method_array->SetElementPtrSize(j, vtable_method, image_pointer_size_);
// Place method in imt if entry is empty, place conflict otherwise.
- uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
- auto** imt_ref = &out_imt[imt_index];
- if (*imt_ref == unimplemented_method) {
- *imt_ref = vtable_method;
- } else if (*imt_ref != conflict_method) {
- // If we are not a conflict and we have the same signature and name as the imt entry,
- // it must be that we overwrote a superclass vtable entry.
- MethodNameAndSignatureComparator imt_comparator(
- (*imt_ref)->GetInterfaceMethodIfProxy(image_pointer_size_));
- *imt_ref = imt_comparator.HasSameNameAndSignature(vtable_method_for_name_comparison) ?
- vtable_method : conflict_method;
- }
+ SetIMTRef(unimplemented_method,
+ conflict_method,
+ image_pointer_size_,
+ vtable_method,
+ /*out*/imt_ptr);
break;
}
}
- if (k < 0 && !super_interface) {
+ // We should only search for default implementations when the class does not implement the
+ // method directly and either (1) the interface is newly implemented on this class and not
+ // on any of its superclasses, (2) the superclass's implementation is a default method, or
+ // (3) the superclass does not have an implementation.
+ if (!found_impl && (!super_interface ||
+ method_array->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_)
+ ->IsOverridableByDefaultMethod())) {
+ ArtMethod* current_method = nullptr;
+ std::string icce_message;
+ if (!FindDefaultMethodImplementation(self,
+ interface_method,
+ klass,
+ /*out*/¤t_method,
+ /*out*/&icce_message)) {
+ // There was a conflict with default method implementations.
+ self->EndAssertNoThreadSuspension(old_cause);
+ // TODO This should actually be thrown when we attempt to invoke this method.
+ ThrowIncompatibleClassChangeError(klass.Get(), "%s", icce_message.c_str());
+ return false;
+ } else if (current_method != nullptr) {
+ if (found_default_impl &&
+ current_method->GetDeclaringClass() == default_impl->GetDeclaringClass()) {
+ // We found a default method but it was the same one we already have from our
+ // superclass. Don't bother adding it to our vtable again.
+ current_method = default_impl;
+ } else {
+ // We found a default method implementation and there were no conflicts.
+ // Save the default method. We need to add it to the vtable.
+ default_methods.push_back(current_method);
+ }
+ method_array->SetElementPtrSize(j, current_method, image_pointer_size_);
+ SetIMTRef(unimplemented_method,
+ conflict_method,
+ image_pointer_size_,
+ current_method,
+ /*out*/imt_ptr);
+ found_impl = true;
+ }
+ }
+ if (!found_impl && !super_interface) {
+ // It is defined in this class or any of its subclasses.
ArtMethod* miranda_method = nullptr;
for (auto& mir_method : miranda_methods) {
if (interface_name_comparator.HasSameNameAndSignature(mir_method)) {
@@ -4852,9 +5284,10 @@
}
}
}
- if (!miranda_methods.empty()) {
+ if (!miranda_methods.empty() || !default_methods.empty()) {
const size_t old_method_count = klass->NumVirtualMethods();
- const size_t new_method_count = old_method_count + miranda_methods.size();
+ const size_t new_method_count =
+ old_method_count + miranda_methods.size() + default_methods.size();
// Attempt to realloc to save RAM if possible.
LengthPrefixedArray<ArtMethod>* old_virtuals = klass->GetVirtualMethodsPtr();
// The Realloced virtual methods aren't visiblef from the class roots, so there is no issue
@@ -4889,13 +5322,36 @@
++out;
}
}
- StrideIterator<ArtMethod> out(virtuals->Begin(method_size, method_alignment) + old_method_count);
+ StrideIterator<ArtMethod> out(virtuals->Begin(method_size, method_alignment)
+ + old_method_count);
// Copy over miranda methods before copying vtable since CopyOf may cause thread suspension and
// we want the roots of the miranda methods to get visited.
for (ArtMethod* mir_method : miranda_methods) {
- out->CopyFrom(mir_method, image_pointer_size_);
- out->SetAccessFlags(out->GetAccessFlags() | kAccMiranda);
- move_table.emplace(mir_method, &*out);
+ ArtMethod& new_method = *out;
+ new_method.CopyFrom(mir_method, image_pointer_size_);
+ new_method.SetAccessFlags(new_method.GetAccessFlags() | kAccMiranda);
+ DCHECK_NE(new_method.GetAccessFlags() & kAccAbstract, 0u)
+ << "Miranda method should be abstract!";
+ move_table.emplace(mir_method, &new_method);
+ ++out;
+ }
+ // We need to copy the default methods into our own virtual method table since the runtime
+ // requires that every method on a class's vtable be in that respective class's virtual method
+ // table.
+ // NOTE This means that two classes might have the same implementation of a method from the same
+ // interface but will have different ArtMethod*s for them. This also means we cannot compare a
+ // default method found on a class with one found on the declaring interface directly and must
+ // look at the declaring class to determine if they are the same.
+ for (ArtMethod* def_method : default_methods) {
+ ArtMethod& new_method = *out;
+ new_method.CopyFrom(def_method, image_pointer_size_);
+ new_method.SetAccessFlags(new_method.GetAccessFlags() | kAccDefault);
+ // Clear the preverified flag if it is present. Since this class hasn't been verified yet it
+ // shouldn't have methods that are preverified.
+ // TODO This is rather arbitrary. We should maybe support classes where only some of its
+ // methods are preverified.
+ new_method.SetAccessFlags(new_method.GetAccessFlags() & ~kAccPreverified);
+ move_table.emplace(def_method, &new_method);
++out;
}
virtuals->SetLength(new_method_count);
@@ -4905,7 +5361,8 @@
self->EndAssertNoThreadSuspension(old_cause);
const size_t old_vtable_count = vtable->GetLength();
- const size_t new_vtable_count = old_vtable_count + miranda_methods.size();
+ const size_t new_vtable_count =
+ old_vtable_count + miranda_methods.size() + default_methods.size();
miranda_methods.clear();
vtable.Assign(down_cast<mirror::PointerArray*>(vtable->CopyOf(self, new_vtable_count)));
if (UNLIKELY(vtable.Get() == nullptr)) {
@@ -4922,15 +5379,29 @@
++vtable_pos;
}
CHECK_EQ(vtable_pos, new_vtable_count);
- // Update old vtable methods.
+ // Update old vtable methods. We use the default_translations map to figure out what each vtable
+ // entry should be updated to, if they need to be at all.
for (size_t i = 0; i < old_vtable_count; ++i) {
- auto* m = vtable->GetElementPtrSize<ArtMethod*>(i, image_pointer_size_);
- DCHECK(m != nullptr) << PrettyClass(klass.Get());
- auto it = move_table.find(m);
+ ArtMethod* translated_method = vtable->GetElementPtrSize<ArtMethod*>(i, image_pointer_size_);
+ // Try and find what we need to change this method to.
+ auto translation_it = default_translations.find(i);
+ bool found_translation = false;
+ if (translation_it != default_translations.end()) {
+ size_t vtable_index;
+ std::tie(vtable_index, translated_method) = *translation_it;
+ DCHECK_EQ(vtable_index, i);
+ found_translation = true;
+ }
+ DCHECK(translated_method != nullptr);
+ auto it = move_table.find(translated_method);
if (it != move_table.end()) {
- auto* new_m = it->second;
- DCHECK(new_m != nullptr) << PrettyClass(klass.Get());
- vtable->SetElementPtrSize(i, new_m, image_pointer_size_);
+ auto* new_method = it->second;
+ DCHECK(new_method != nullptr);
+ vtable->SetElementPtrSize(i, new_method, image_pointer_size_);
+ } else {
+ // If it was not going to be updated we wouldn't have put it into the default_translations
+ // map.
+ CHECK(!found_translation) << "We were asked to update this vtable entry. Must not fail.";
}
}
@@ -4961,7 +5432,11 @@
auto* resolved_methods = klass->GetDexCache()->GetResolvedMethods();
for (size_t i = 0, count = klass->GetDexCache()->NumResolvedMethods(); i < count; ++i) {
auto* m = mirror::DexCache::GetElementPtrSize(resolved_methods, i, image_pointer_size_);
- CHECK(move_table.find(m) == move_table.end()) << PrettyMethod(m);
+ // We don't remove default methods from the move table since we need them to update the
+ // vtable. Therefore just skip them for this check.
+ if (!m->IsDefault()) {
+ CHECK(move_table.find(m) == move_table.end()) << PrettyMethod(m);
+ }
}
}
// Put some random garbage in old virtuals to help find stale pointers.
@@ -5794,9 +6269,13 @@
for (const DexFile* dex_file : dex_files) {
StackHandleScope<3> hs2(self);
- Handle<mirror::LongArray> h_long_array = hs2.NewHandle(mirror::LongArray::Alloc(self, 1));
+ // CreatePathClassLoader is only used by gtests. Index 0 of h_long_array is supposed to be the
+ // oat file but we can leave it null.
+ Handle<mirror::LongArray> h_long_array = hs2.NewHandle(mirror::LongArray::Alloc(
+ self,
+ kDexFileIndexStart + 1));
DCHECK(h_long_array.Get() != nullptr);
- h_long_array->Set(0, reinterpret_cast<intptr_t>(dex_file));
+ h_long_array->Set(kDexFileIndexStart, reinterpret_cast<intptr_t>(dex_file));
Handle<mirror::Object> h_dex_file = hs2.NewHandle(
cookie_field->GetDeclaringClass()->AllocObject(self));
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 76cb0a6..93161f7 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -18,6 +18,7 @@
#define ART_RUNTIME_CLASS_LINKER_H_
#include <string>
+#include <unordered_map>
#include <utility>
#include <vector>
@@ -648,6 +649,12 @@
bool can_init_parents)
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!dex_lock_);
+ bool InitializeDefaultInterfaceRecursive(Thread* self,
+ Handle<mirror::Class> klass,
+ bool can_run_clinit,
+ bool can_init_parents)
+ REQUIRES(!dex_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
bool WaitForInitializeClass(Handle<mirror::Class> klass,
Thread* self,
ObjectLock<mirror::Class>& lock);
@@ -687,12 +694,65 @@
ArtMethod** out_imt)
SHARED_REQUIRES(Locks::mutator_lock_);
- bool LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass)
+ // Links the virtual methods for the given class and records any default methods that will need to
+ // be updated later.
+ //
+ // Arguments:
+ // * self - The current thread.
+ // * klass - class, whose vtable will be filled in.
+ // * default_translations - Vtable index to new method map.
+ // Any vtable entries that need to be updated with new default methods
+ // are stored into the default_translations map. The default_translations
+ // map is keyed on the vtable index that needs to be updated. We use this
+ // map because if we override a default method with another default
+ // method we need to update the vtable to point to the new method.
+ // Unfortunately since we copy the ArtMethod* we cannot just do a simple
+ // scan, we therefore store the vtable index's that might need to be
+ // updated with the method they will turn into.
+ // TODO This whole default_translations thing is very dirty. There should be a better way.
+ bool LinkVirtualMethods(Thread* self,
+ Handle<mirror::Class> klass,
+ /*out*/std::unordered_map<size_t, ArtMethod*>* default_translations)
SHARED_REQUIRES(Locks::mutator_lock_);
+ // Sets up the interface lookup table (IFTable) in the correct order to allow searching for
+ // default methods.
+ bool SetupInterfaceLookupTable(Thread* self,
+ Handle<mirror::Class> klass,
+ Handle<mirror::ObjectArray<mirror::Class>> interfaces)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Find the default method implementation for 'interface_method' in 'klass', if one exists.
+ //
+ // Arguments:
+ // * self - The current thread.
+ // * target_method - The method we are trying to find a default implementation for.
+ // * klass - The class we are searching for a definition of target_method.
+ // * out_default_method - The pointer we will store the found default method to on success.
+ // * icce_message - A string we will store an appropriate IncompatibleClassChangeError message
+ // into in case of failure. Note we must do it this way since we do not know
+ // whether we can allocate the exception object, which could cause us to go to
+ // sleep.
+ //
+ // Return value:
+ // * True - There were no conflicting method implementations found in the class while searching
+ // for target_method. The default method implementation is stored into out_default_method
+ // if it was found. Otherwise *out_default_method will be set to nullptr.
+ // * False - Conflicting method implementations were found when searching for target_method. The
+ // value of *out_default_method is undefined and *icce_message is a string that should
+ // be used to create an IncompatibleClassChangeError as soon as possible.
+ bool FindDefaultMethodImplementation(Thread* self,
+ ArtMethod* target_method,
+ Handle<mirror::Class> klass,
+ /*out*/ArtMethod** out_default_method,
+ /*out*/std::string* icce_message) const
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Sets the imt entries and fixes up the vtable for the given class by linking all the interface
+ // methods. See LinkVirtualMethods for an explanation of what default_translations is.
bool LinkInterfaceMethods(Thread* self,
Handle<mirror::Class> klass,
- Handle<mirror::ObjectArray<mirror::Class>> interfaces,
+ const std::unordered_map<size_t, ArtMethod*>& default_translations,
ArtMethod** out_imt)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 56c5d1a..b6b5141 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -42,6 +42,7 @@
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mem_map.h"
+#include "native/dalvik_system_DexFile.h"
#include "noop_compiler_callbacks.h"
#include "os.h"
#include "primitive.h"
@@ -516,7 +517,7 @@
mirror::LongArray* long_array = cookie_field->GetObject(dex_file)->AsLongArray();
DCHECK(long_array != nullptr);
int32_t long_array_size = long_array->GetLength();
- for (int32_t j = 0; j < long_array_size; ++j) {
+ for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) {
const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(
long_array->GetWithoutChecks(j)));
if (cp_dex_file == nullptr) {
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 09416cc..a5f9d09 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -23,7 +23,9 @@
#include "base/stringprintf.h"
#include "dex_file-inl.h"
+#include "experimental_flags.h"
#include "leb128.h"
+#include "runtime.h"
#include "safe_map.h"
#include "utf-inl.h"
#include "utils.h"
@@ -2530,7 +2532,14 @@
}
// Only the static initializer may have code in an interface.
- if (((class_access_flags & kAccInterface) != 0) && !is_clinit_by_name) {
+ // TODO We should have some way determine whether to allow this experimental flag without the
+ // runtime being started.
+ // We assume experimental flags are enabled when running without a runtime to enable tools like
+ // dexdump to handle dex files with these features.
+ if (((class_access_flags & kAccInterface) != 0)
+ && !is_clinit_by_name
+ && Runtime::Current() != nullptr
+ && !Runtime::Current()->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods)) {
*error_msg = StringPrintf("Non-clinit interface method %" PRIu32 " should not have code",
method_index);
return false;
diff --git a/runtime/experimental_flags.h b/runtime/experimental_flags.h
new file mode 100644
index 0000000..2e674e9
--- /dev/null
+++ b/runtime/experimental_flags.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ART_RUNTIME_EXPERIMENTAL_FLAGS_H_
+#define ART_RUNTIME_EXPERIMENTAL_FLAGS_H_
+
+#include <ostream>
+
+namespace art {
+
+// Possible experimental features that might be enabled.
+struct ExperimentalFlags {
+ // The actual flag values.
+ enum {
+ kNone = 0x0000,
+ kLambdas = 0x0001,
+ kDefaultMethods = 0x0002,
+ };
+
+ constexpr ExperimentalFlags() : value_(0x0000) {}
+ constexpr ExperimentalFlags(decltype(kNone) t) : value_(static_cast<uint32_t>(t)) {}
+
+ constexpr operator decltype(kNone)() const {
+ return static_cast<decltype(kNone)>(value_);
+ }
+
+ constexpr explicit operator bool() const {
+ return value_ != kNone;
+ }
+
+ constexpr ExperimentalFlags operator|(const decltype(kNone)& b) const {
+ return static_cast<decltype(kNone)>(value_ | static_cast<uint32_t>(b));
+ }
+ constexpr ExperimentalFlags operator|(const ExperimentalFlags& b) const {
+ return static_cast<decltype(kNone)>(value_ | b.value_);
+ }
+
+ constexpr ExperimentalFlags operator&(const ExperimentalFlags& b) const {
+ return static_cast<decltype(kNone)>(value_ & b.value_);
+ }
+ constexpr ExperimentalFlags operator&(const decltype(kNone)& b) const {
+ return static_cast<decltype(kNone)>(value_ & static_cast<uint32_t>(b));
+ }
+
+ constexpr bool operator==(const ExperimentalFlags& b) const {
+ return value_ == b.value_;
+ }
+
+ private:
+ uint32_t value_;
+};
+
+inline std::ostream& operator<<(std::ostream& stream, const ExperimentalFlags& e) {
+ bool started = false;
+ if (e & ExperimentalFlags::kLambdas) {
+ stream << (started ? "|" : "") << "kLambdas";
+ started = true;
+ }
+ if (e & ExperimentalFlags::kDefaultMethods) {
+ stream << (started ? "|" : "") << "kDefaultMethods";
+ started = true;
+ }
+ if (!started) {
+ stream << "kNone";
+ }
+ return stream;
+}
+
+inline std::ostream& operator<<(std::ostream& stream, const decltype(ExperimentalFlags::kNone)& e) {
+ return stream << ExperimentalFlags(e);
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_EXPERIMENTAL_FLAGS_H_
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index 87f1392..3ce3d63 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -131,6 +131,7 @@
private:
Slot* next_; // Next slot in the list.
+ friend class RosAlloc;
};
// We use the tail (kUseTail == true) for the bulk or thread-local free lists to avoid the need to
@@ -302,6 +303,7 @@
// free without traversing the whole free list.
uint32_t size_;
uint32_t padding_ ATTRIBUTE_UNUSED;
+ friend class RosAlloc;
};
// Represents a run of memory slots of the same size.
@@ -482,7 +484,7 @@
static constexpr uint8_t kMagicNumFree = 43;
// The number of size brackets. Sync this with the length of Thread::rosalloc_runs_.
static constexpr size_t kNumOfSizeBrackets = kNumRosAllocThreadLocalSizeBrackets;
- // The number of smaller size brackets that are 16 bytes apart.
+ // The number of smaller size brackets that are the quantum size apart.
static constexpr size_t kNumOfQuantumSizeBrackets = 32;
// The sizes (the slot sizes, in bytes) of the size brackets.
static size_t bracketSizes[kNumOfSizeBrackets];
@@ -520,9 +522,7 @@
}
// Returns true if the given allocation size is for a thread local allocation.
static bool IsSizeForThreadLocal(size_t size) {
- DCHECK_GT(kNumThreadLocalSizeBrackets, 0U);
- size_t max_thread_local_bracket_idx = kNumThreadLocalSizeBrackets - 1;
- bool is_size_for_thread_local = size <= bracketSizes[max_thread_local_bracket_idx];
+ bool is_size_for_thread_local = size <= kMaxThreadLocalBracketSize;
DCHECK(size > kLargeSizeThreshold ||
(is_size_for_thread_local == (SizeToIndex(size) < kNumThreadLocalSizeBrackets)));
return is_size_for_thread_local;
@@ -634,6 +634,16 @@
// are less than this index. We use shared (current) runs for the rest.
static const size_t kNumThreadLocalSizeBrackets = 8;
+ // The size of the largest bracket we use thread-local runs for.
+ // This should be equal to bracketSizes[kNumThreadLocalSizeBrackets - 1].
+ static const size_t kMaxThreadLocalBracketSize = 128;
+
+ // The bracket size increment for the brackets of size <= 512 bytes.
+ static constexpr size_t kBracketQuantumSize = 16;
+
+ // Equal to Log2(kQuantumBracketSizeIncrement).
+ static constexpr size_t kBracketQuantumSizeShift = 4;
+
private:
// The base address of the memory region that's managed by this allocator.
uint8_t* base_;
@@ -770,6 +780,19 @@
size_t page_release_size_threshold = kDefaultPageReleaseSizeThreshold);
~RosAlloc();
+ static size_t RunFreeListOffset() {
+ return OFFSETOF_MEMBER(Run, free_list_);
+ }
+ static size_t RunFreeListHeadOffset() {
+ return OFFSETOF_MEMBER(SlotFreeList<false>, head_);
+ }
+ static size_t RunFreeListSizeOffset() {
+ return OFFSETOF_MEMBER(SlotFreeList<false>, size_);
+ }
+ static size_t RunSlotNextOffset() {
+ return OFFSETOF_MEMBER(Slot, next_);
+ }
+
// If kThreadUnsafe is true then the allocator may avoid acquiring some locks as an optimization.
// If used, this may cause race conditions if multiple threads are allocating at the same time.
template<bool kThreadSafe = true>
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index b010504..7c0594a 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -458,10 +458,11 @@
}
self->PushShadowFrame(shadow_frame);
+ ArtMethod* method = shadow_frame->GetMethod();
// Ensure static methods are initialized.
- const bool is_static = shadow_frame->GetMethod()->IsStatic();
+ const bool is_static = method->IsStatic();
if (is_static) {
- mirror::Class* declaring_class = shadow_frame->GetMethod()->GetDeclaringClass();
+ mirror::Class* declaring_class = method->GetDeclaringClass();
if (UNLIKELY(!declaring_class->IsInitialized())) {
StackHandleScope<1> hs(self);
HandleWrapper<Class> h_declaring_class(hs.NewHandleWrapper(&declaring_class));
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index 4265b50..9766299 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -19,6 +19,7 @@
#include "base/stl_util.h" // MakeUnique
+#include "experimental_flags.h"
#include "interpreter_common.h"
#include "safe_math.h"
@@ -83,7 +84,7 @@
#define HANDLE_EXPERIMENTAL_INSTRUCTION_START(opcode) \
HANDLE_INSTRUCTION_START(opcode); \
DCHECK(inst->IsExperimental()); \
- if (Runtime::Current()->AreExperimentalLambdasEnabled()) {
+ if (Runtime::Current()->AreExperimentalFlagsEnabled(ExperimentalFlags::kLambdas)) {
#define HANDLE_EXPERIMENTAL_INSTRUCTION_END() \
} else { \
UnexpectedOpcode(inst, shadow_frame); \
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 76d4bb0fc..bf95a0e 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -15,6 +15,7 @@
*/
#include "base/stl_util.h" // MakeUnique
+#include "experimental_flags.h"
#include "interpreter_common.h"
#include "safe_math.h"
@@ -67,7 +68,7 @@
static bool IsExperimentalInstructionEnabled(const Instruction *inst) {
DCHECK(inst->IsExperimental());
- return Runtime::Current()->AreExperimentalLambdasEnabled();
+ return Runtime::Current()->AreExperimentalFlagsEnabled(ExperimentalFlags::kLambdas);
}
template<bool do_access_check, bool transaction_active>
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 2a019c5..2d3581d 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -134,11 +134,25 @@
uintptr_t MemMap::next_mem_pos_ = GenerateNextMemPos();
#endif
-// Return true if the address range is contained in a single /proc/self/map entry.
-static bool ContainedWithinExistingMap(uint8_t* ptr, size_t size,
- std::string* error_msg) {
+// Return true if the address range is contained in a single memory map by either reading
+// the maps_ variable or the /proc/self/map entry.
+bool MemMap::ContainedWithinExistingMap(uint8_t* ptr, size_t size, std::string* error_msg) {
uintptr_t begin = reinterpret_cast<uintptr_t>(ptr);
uintptr_t end = begin + size;
+
+ // There is a suspicion that BacktraceMap::Create is occasionally missing maps. TODO: Investigate
+ // further.
+ {
+ MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_);
+ for (auto& pair : *maps_) {
+ MemMap* const map = pair.second;
+ if (begin >= reinterpret_cast<uintptr_t>(map->Begin()) &&
+ end <= reinterpret_cast<uintptr_t>(map->End())) {
+ return true;
+ }
+ }
+ }
+
std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid(), true));
if (map.get() == nullptr) {
*error_msg = StringPrintf("Failed to build process map");
diff --git a/runtime/mem_map.h b/runtime/mem_map.h
index 196a7f6..7c11ceb 100644
--- a/runtime/mem_map.h
+++ b/runtime/mem_map.h
@@ -161,6 +161,8 @@
REQUIRES(Locks::mem_maps_lock_);
static MemMap* GetLargestMemMapAt(void* address)
REQUIRES(Locks::mem_maps_lock_);
+ static bool ContainedWithinExistingMap(uint8_t* ptr, size_t size, std::string* error_msg)
+ REQUIRES(!Locks::mem_maps_lock_);
const std::string name_;
uint8_t* const begin_; // Start of data.
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 93f2aea..a528c3b 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -392,7 +392,8 @@
}
inline ArtMethod* Class::FindVirtualMethodForVirtual(ArtMethod* method, size_t pointer_size) {
- DCHECK(!method->GetDeclaringClass()->IsInterface() || method->IsMiranda());
+ // Only miranda or default methods may come from interfaces and be used as a virtual.
+ DCHECK(!method->GetDeclaringClass()->IsInterface() || method->IsDefault() || method->IsMiranda());
// The argument method may from a super class.
// Use the index to a potentially overridden one for this instance's class.
return GetVTableEntry(method->GetMethodIndex(), pointer_size);
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 2668b3d..8219d69 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -30,6 +30,7 @@
#include "primitive.h"
#include "read_barrier_option.h"
#include "stride_iterator.h"
+#include "thread.h"
#include "utils.h"
#ifndef IMT_SIZE
@@ -229,6 +230,18 @@
return (GetAccessFlags() & kAccClassIsFinalizable) != 0;
}
+ ALWAYS_INLINE void SetRecursivelyInitialized() SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK_EQ(GetLockOwnerThreadId(), Thread::Current()->GetThreadId());
+ uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_));
+ SetAccessFlags(flags | kAccRecursivelyInitialized);
+ }
+
+ ALWAYS_INLINE void SetHasDefaultMethods() SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK_EQ(GetLockOwnerThreadId(), Thread::Current()->GetThreadId());
+ uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_));
+ SetAccessFlags(flags | kAccHasDefaultMethod);
+ }
+
ALWAYS_INLINE void SetFinalizable() SHARED_REQUIRES(Locks::mutator_lock_) {
uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_));
SetAccessFlags(flags | kAccClassIsFinalizable);
@@ -860,6 +873,14 @@
ArtMethod* FindClassInitializer(size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_);
+ bool HasDefaultMethods() SHARED_REQUIRES(Locks::mutator_lock_) {
+ return (GetAccessFlags() & kAccHasDefaultMethod) != 0;
+ }
+
+ bool HasBeenRecursivelyInitialized() SHARED_REQUIRES(Locks::mutator_lock_) {
+ return (GetAccessFlags() & kAccRecursivelyInitialized) != 0;
+ }
+
ALWAYS_INLINE int32_t GetIfTableCount() SHARED_REQUIRES(Locks::mutator_lock_);
ALWAYS_INLINE IfTable* GetIfTable() SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index f7ab10b..116cbe9 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -49,8 +49,13 @@
// method (dex only)
static constexpr uint32_t kAccFastNative = 0x00080000; // method (dex only)
static constexpr uint32_t kAccMiranda = 0x00200000; // method (dex only)
+static constexpr uint32_t kAccDefault = 0x00400000; // method (runtime)
// Special runtime-only flags.
+// Interface and all its super-interfaces with default methods have been recursively initialized.
+static constexpr uint32_t kAccRecursivelyInitialized = 0x20000000;
+// Interface declares some default method.
+static constexpr uint32_t kAccHasDefaultMethod = 0x40000000;
// class/ancestor overrides finalize()
static constexpr uint32_t kAccClassIsFinalizable = 0x80000000;
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 4850b6f..e9ce02b 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -40,13 +40,16 @@
namespace art {
-static std::unique_ptr<std::vector<const DexFile*>>
-ConvertJavaArrayToNative(JNIEnv* env, jobject arrayObject) {
+static bool ConvertJavaArrayToDexFiles(
+ JNIEnv* env,
+ jobject arrayObject,
+ /*out*/ std::vector<const DexFile*>& dex_files,
+ /*out*/ const OatFile*& oat_file) {
jarray array = reinterpret_cast<jarray>(arrayObject);
jsize array_size = env->GetArrayLength(array);
if (env->ExceptionCheck() == JNI_TRUE) {
- return std::unique_ptr<std::vector<const DexFile*>>();
+ return false;
}
// TODO: Optimize. On 32bit we can use an int array.
@@ -54,27 +57,24 @@
jlong* long_data = env->GetLongArrayElements(reinterpret_cast<jlongArray>(array),
&is_long_data_copied);
if (env->ExceptionCheck() == JNI_TRUE) {
- return std::unique_ptr<std::vector<const DexFile*>>();
+ return false;
}
- std::unique_ptr<std::vector<const DexFile*>> ret(new std::vector<const DexFile*>());
- ret->reserve(array_size);
- for (jsize i = 0; i < array_size; ++i) {
- ret->push_back(reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(*(long_data + i))));
+ oat_file = reinterpret_cast<const OatFile*>(static_cast<uintptr_t>(long_data[kOatFileIndex]));
+ dex_files.reserve(array_size - 1);
+ for (jsize i = kDexFileIndexStart; i < array_size; ++i) {
+ dex_files.push_back(reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(long_data[i])));
}
env->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array), long_data, JNI_ABORT);
- if (env->ExceptionCheck() == JNI_TRUE) {
- return std::unique_ptr<std::vector<const DexFile*>>();
- }
-
- return ret;
+ return env->ExceptionCheck() != JNI_TRUE;
}
-static jlongArray ConvertNativeToJavaArray(JNIEnv* env,
- std::vector<std::unique_ptr<const DexFile>>& vec) {
- size_t vec_size = vec.size();
- jlongArray long_array = env->NewLongArray(static_cast<jsize>(vec_size));
+static jlongArray ConvertDexFilesToJavaArray(JNIEnv* env,
+ const OatFile* oat_file,
+ std::vector<std::unique_ptr<const DexFile>>& vec) {
+ // Add one for the oat file.
+ jlongArray long_array = env->NewLongArray(static_cast<jsize>(1u + vec.size()));
if (env->ExceptionCheck() == JNI_TRUE) {
return nullptr;
}
@@ -85,10 +85,9 @@
return nullptr;
}
- jlong* tmp = long_data;
- for (auto& dex_file : vec) {
- *tmp = reinterpret_cast<uintptr_t>(dex_file.get());
- tmp++;
+ long_data[kOatFileIndex] = reinterpret_cast<uintptr_t>(oat_file);
+ for (size_t i = 0; i < vec.size(); ++i) {
+ long_data[kDexFileIndexStart + i] = reinterpret_cast<uintptr_t>(vec[i].get());
}
env->ReleaseLongArrayElements(long_array, long_data, 0);
@@ -165,13 +164,15 @@
ClassLinker* linker = runtime->GetClassLinker();
std::vector<std::unique_ptr<const DexFile>> dex_files;
std::vector<std::string> error_msgs;
+ const OatFile* oat_file = nullptr;
dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),
outputName.c_str(),
- &error_msgs);
+ /*out*/ &oat_file,
+ /*out*/ &error_msgs);
if (!dex_files.empty()) {
- jlongArray array = ConvertNativeToJavaArray(env, dex_files);
+ jlongArray array = ConvertDexFilesToJavaArray(env, oat_file, dex_files);
if (array == nullptr) {
ScopedObjectAccess soa(env);
for (auto& dex_file : dex_files) {
@@ -197,43 +198,55 @@
}
static jboolean DexFile_closeDexFile(JNIEnv* env, jclass, jobject cookie) {
- ScopedObjectAccess soa(env);
- mirror::Object* dex_files_object = soa.Decode<mirror::Object*>(cookie);
- if (dex_files_object == nullptr) {
- ThrowNullPointerException("cookie == null");
+ std::vector<const DexFile*> dex_files;
+ const OatFile* oat_file;
+ if (!ConvertJavaArrayToDexFiles(env, cookie, dex_files, oat_file)) {
+ Thread::Current()->AssertPendingException();
return JNI_FALSE;
}
- mirror::LongArray* dex_files = dex_files_object->AsLongArray();
-
- // Delete dex files associated with this dalvik.system.DexFile since there should not be running
- // code using it. dex_files is a vector due to multidex.
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ Runtime* const runtime = Runtime::Current();
bool all_deleted = true;
- for (int32_t i = 0, count = dex_files->GetLength(); i < count; ++i) {
- auto* dex_file = reinterpret_cast<DexFile*>(dex_files->Get(i));
- if (dex_file == nullptr) {
- continue;
- }
- // Only delete the dex file if the dex cache is not found to prevent runtime crashes if there
- // are calls to DexFile.close while the ART DexFile is still in use.
- if (class_linker->FindDexCache(soa.Self(), *dex_file, true) == nullptr) {
- // Clear the element in the array so that we can call close again.
- dex_files->Set(i, 0);
- delete dex_file;
- } else {
- all_deleted = false;
+ {
+ ScopedObjectAccess soa(env);
+ mirror::Object* dex_files_object = soa.Decode<mirror::Object*>(cookie);
+ mirror::LongArray* long_dex_files = dex_files_object->AsLongArray();
+ // Delete dex files associated with this dalvik.system.DexFile since there should not be running
+ // code using it. dex_files is a vector due to multidex.
+ ClassLinker* const class_linker = runtime->GetClassLinker();
+ int32_t i = kDexFileIndexStart; // Oat file is at index 0.
+ for (const DexFile* dex_file : dex_files) {
+ if (dex_file != nullptr) {
+ // Only delete the dex file if the dex cache is not found to prevent runtime crashes if there
+ // are calls to DexFile.close while the ART DexFile is still in use.
+ if (class_linker->FindDexCache(soa.Self(), *dex_file, true) == nullptr) {
+ // Clear the element in the array so that we can call close again.
+ long_dex_files->Set(i, 0);
+ delete dex_file;
+ } else {
+ all_deleted = false;
+ }
+ }
+ ++i;
}
}
- // TODO: Also unmap the OatFile for this dalvik.system.DexFile.
-
+ // oat_file can be null if we are running without dex2oat.
+ if (all_deleted && oat_file != nullptr) {
+ // If all of the dex files are no longer in use we can unmap the corresponding oat file.
+ VLOG(class_linker) << "Unregistering " << oat_file;
+ runtime->GetOatFileManager().UnRegisterAndDeleteOatFile(oat_file);
+ }
return all_deleted ? JNI_TRUE : JNI_FALSE;
}
-static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader,
+static jclass DexFile_defineClassNative(JNIEnv* env,
+ jclass,
+ jstring javaName,
+ jobject javaLoader,
jobject cookie) {
- std::unique_ptr<std::vector<const DexFile*>> dex_files = ConvertJavaArrayToNative(env, cookie);
- if (dex_files.get() == nullptr) {
+ std::vector<const DexFile*> dex_files;
+ const OatFile* oat_file;
+ if (!ConvertJavaArrayToDexFiles(env, cookie, /*out*/ dex_files, /*out*/ oat_file)) {
VLOG(class_linker) << "Failed to find dex_file";
DCHECK(env->ExceptionCheck());
return nullptr;
@@ -246,7 +259,7 @@
}
const std::string descriptor(DotToDescriptor(class_name.c_str()));
const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str()));
- for (auto& dex_file : *dex_files) {
+ for (auto& dex_file : dex_files) {
const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str(), hash);
if (dex_class_def != nullptr) {
ScopedObjectAccess soa(env);
@@ -255,8 +268,12 @@
StackHandleScope<1> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader)));
- mirror::Class* result = class_linker->DefineClass(soa.Self(), descriptor.c_str(), hash,
- class_loader, *dex_file, *dex_class_def);
+ mirror::Class* result = class_linker->DefineClass(soa.Self(),
+ descriptor.c_str(),
+ hash,
+ class_loader,
+ *dex_file,
+ *dex_class_def);
if (result != nullptr) {
VLOG(class_linker) << "DexFile_defineClassNative returning " << result
<< " for " << class_name.c_str();
@@ -277,8 +294,9 @@
// Note: this can be an expensive call, as we sort out duplicates in MultiDex files.
static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jobject cookie) {
- std::unique_ptr<std::vector<const DexFile*>> dex_files = ConvertJavaArrayToNative(env, cookie);
- if (dex_files.get() == nullptr) {
+ const OatFile* oat_file = nullptr;
+ std::vector<const DexFile*> dex_files;
+ if (!ConvertJavaArrayToDexFiles(env, cookie, /*out */ dex_files, /* out */ oat_file)) {
DCHECK(env->ExceptionCheck());
return nullptr;
}
@@ -286,7 +304,7 @@
// Push all class descriptors into a set. Use set instead of unordered_set as we want to
// retrieve all in the end.
std::set<const char*, CharPointerComparator> descriptors;
- for (auto& dex_file : *dex_files) {
+ for (auto& dex_file : dex_files) {
for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
const char* descriptor = dex_file->GetClassDescriptor(class_def);
@@ -295,7 +313,8 @@
}
// Now create output array and copy the set into it.
- jobjectArray result = env->NewObjectArray(descriptors.size(), WellKnownClasses::java_lang_String,
+ jobjectArray result = env->NewObjectArray(descriptors.size(),
+ WellKnownClasses::java_lang_String,
nullptr);
if (result != nullptr) {
auto it = descriptors.begin();
@@ -313,9 +332,11 @@
return result;
}
-static jint GetDexOptNeeded(JNIEnv* env, const char* filename,
- const char* pkgname, const char* instruction_set, const jboolean defer) {
-
+static jint GetDexOptNeeded(JNIEnv* env,
+ const char* filename,
+ const char* pkgname,
+ const char* instruction_set,
+ const jboolean defer) {
if ((filename == nullptr) || !OS::FileExists(filename)) {
LOG(ERROR) << "DexFile_getDexOptNeeded file '" << filename << "' does not exist";
ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException"));
@@ -365,8 +386,12 @@
return oat_file_assistant.GetDexOptNeeded();
}
-static jint DexFile_getDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename,
- jstring javaPkgname, jstring javaInstructionSet, jboolean defer) {
+static jint DexFile_getDexOptNeeded(JNIEnv* env,
+ jclass,
+ jstring javaFilename,
+ jstring javaPkgname,
+ jstring javaInstructionSet,
+ jboolean defer) {
ScopedUtfChars filename(env, javaFilename);
if (env->ExceptionCheck()) {
return 0;
@@ -379,8 +404,11 @@
return 0;
}
- return GetDexOptNeeded(env, filename.c_str(), pkgname.c_str(),
- instruction_set.c_str(), defer);
+ return GetDexOptNeeded(env,
+ filename.c_str(),
+ pkgname.c_str(),
+ instruction_set.c_str(),
+ defer);
}
// public API, null pkgname
diff --git a/runtime/native/dalvik_system_DexFile.h b/runtime/native/dalvik_system_DexFile.h
index 7585ab9..77d219d 100644
--- a/runtime/native/dalvik_system_DexFile.h
+++ b/runtime/native/dalvik_system_DexFile.h
@@ -18,9 +18,13 @@
#define ART_RUNTIME_NATIVE_DALVIK_SYSTEM_DEXFILE_H_
#include <jni.h>
+#include <unistd.h>
namespace art {
+constexpr size_t kOatFileIndex = 0;
+constexpr size_t kDexFileIndexStart = 1;
+
class DexFile;
void register_dalvik_system_DexFile(JNIEnv* env);
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 80f017d..6cbbce9 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -122,21 +122,29 @@
// and for having dex caches arrays in the .bss section.
Runtime* const runtime = Runtime::Current();
OatFileManager* const manager = (runtime != nullptr) ? &runtime->GetOatFileManager() : nullptr;
- if (kUseDlopen &&
- (kIsTargetBuild ||
- (kUseDlopenOnHost &&
- // Manager may be null if we are running without a runtime.
- manager != nullptr &&
- manager->FindOpenedOatFileFromOatLocation(location) == nullptr)) &&
- executable) {
- // Try to use dlopen. This may fail for various reasons, outlined below. We try dlopen, as
- // this will register the oat file with the linker and allows libunwind to find our info.
- ret.reset(OpenDlopen(filename, location, requested_base, abs_dex_location, error_msg));
- if (ret.get() != nullptr) {
- return ret.release();
+ if (kUseDlopen && executable) {
+ bool success = kIsTargetBuild;
+ bool reserved_location = false;
+ // Manager may be null if we are running without a runtime.
+ if (!success && kUseDlopenOnHost && manager != nullptr) {
+ // RegisterOatFileLocation returns false if we are not the first caller to register that
+ // location.
+ reserved_location = manager->RegisterOatFileLocation(location);
+ success = reserved_location;
}
- if (kPrintDlOpenErrorMessage) {
- LOG(ERROR) << "Failed to dlopen: " << *error_msg;
+ if (success) {
+ // Try to use dlopen. This may fail for various reasons, outlined below. We try dlopen, as
+ // this will register the oat file with the linker and allows libunwind to find our info.
+ ret.reset(OpenDlopen(filename, location, requested_base, abs_dex_location, error_msg));
+ if (reserved_location) {
+ manager->UnRegisterOatFileLocation(location);
+ }
+ if (ret != nullptr) {
+ return ret.release();
+ }
+ if (kPrintDlOpenErrorMessage) {
+ LOG(ERROR) << "Failed to dlopen: " << *error_msg;
+ }
}
}
@@ -217,6 +225,10 @@
is_executable_(is_executable), dlopen_handle_(nullptr),
secondary_lookup_lock_("OatFile secondary lookup lock", kOatFileSecondaryLookupLock) {
CHECK(!location_.empty());
+ Runtime* const runtime = Runtime::Current();
+ if (runtime != nullptr && !runtime->IsAotCompiler()) {
+ runtime->GetOatFileManager().RegisterOatFileLocation(location);
+ }
}
OatFile::~OatFile() {
@@ -224,6 +236,10 @@
if (dlopen_handle_ != nullptr) {
dlclose(dlopen_handle_);
}
+ Runtime* const runtime = Runtime::Current();
+ if (runtime != nullptr && !runtime->IsAotCompiler()) {
+ runtime->GetOatFileManager().UnRegisterOatFileLocation(location_);
+ }
}
bool OatFile::Dlopen(const std::string& elf_filename, uint8_t* requested_base,
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index de4e8ec..cef8702 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -961,13 +961,16 @@
// we can verify only one oat file was loaded for the dex location.
std::vector<std::unique_ptr<const DexFile>> dex_files;
std::vector<std::string> error_msgs;
+ const OatFile* oat_file = nullptr;
dex_files = Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(
dex_location_.c_str(),
oat_location_.c_str(),
+ &oat_file,
&error_msgs);
CHECK(!dex_files.empty()) << Join(error_msgs, '\n');
CHECK(dex_files[0]->GetOatDexFile() != nullptr) << dex_files[0]->GetLocation();
loaded_oat_file_ = dex_files[0]->GetOatDexFile()->GetOatFile();
+ CHECK_EQ(loaded_oat_file_, oat_file);
}
const OatFile* GetLoadedOatFile() const {
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 73b065f..3371a39 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -33,9 +33,10 @@
static constexpr bool kDuplicateClassesCheck = false;
const OatFile* OatFileManager::RegisterOatFile(std::unique_ptr<const OatFile> oat_file) {
- ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
+ WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
DCHECK(oat_file != nullptr);
if (kIsDebugBuild) {
+ CHECK(oat_files_.find(oat_file) == oat_files_.end());
for (const std::unique_ptr<const OatFile>& existing : oat_files_) {
CHECK_NE(oat_file.get(), existing.get()) << oat_file->GetLocation();
// Check that we don't have an oat file with the same address. Copies of the same oat file
@@ -44,13 +45,29 @@
}
}
have_non_pic_oat_file_ = have_non_pic_oat_file_ || !oat_file->IsPic();
- oat_files_.push_back(std::move(oat_file));
- return oat_files_.back().get();
+ const OatFile* ret = oat_file.get();
+ oat_files_.insert(std::move(oat_file));
+ return ret;
+}
+
+void OatFileManager::UnRegisterAndDeleteOatFile(const OatFile* oat_file) {
+ WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
+ DCHECK(oat_file != nullptr);
+ std::unique_ptr<const OatFile> compare(oat_file);
+ auto it = oat_files_.find(compare);
+ CHECK(it != oat_files_.end());
+ oat_files_.erase(it);
+ compare.release();
}
const OatFile* OatFileManager::FindOpenedOatFileFromOatLocation(const std::string& oat_location)
const {
ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
+ return FindOpenedOatFileFromOatLocationLocked(oat_location);
+}
+
+const OatFile* OatFileManager::FindOpenedOatFileFromOatLocationLocked(
+ const std::string& oat_location) const {
for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) {
if (oat_file->GetLocation() == oat_location) {
return oat_file.get();
@@ -81,6 +98,9 @@
}
OatFileManager::~OatFileManager() {
+ // Explicitly clear oat_files_ since the OatFile destructor calls back into OatFileManager for
+ // UnRegisterOatFileLocation.
+ oat_files_.clear();
}
const OatFile* OatFileManager::RegisterImageOatFile(gc::space::ImageSpace* space) {
@@ -95,17 +115,9 @@
current_class_index_(current_class_index),
from_loaded_oat_(from_loaded_oat) {}
- DexFileAndClassPair(DexFileAndClassPair&& rhs) {
- *this = std::move(rhs);
- }
+ DexFileAndClassPair(DexFileAndClassPair&& rhs) = default;
- DexFileAndClassPair& operator=(DexFileAndClassPair&& rhs) {
- cached_descriptor_ = rhs.cached_descriptor_;
- dex_file_ = std::move(rhs.dex_file_);
- current_class_index_ = rhs.current_class_index_;
- from_loaded_oat_ = rhs.from_loaded_oat_;
- return *this;
- }
+ DexFileAndClassPair& operator=(DexFileAndClassPair&& rhs) = default;
const char* GetCachedDescriptor() const {
return cached_descriptor_;
@@ -127,6 +139,7 @@
void Next() {
++current_class_index_;
+ cached_descriptor_ = nullptr;
}
size_t GetCurrentClassIndex() const {
@@ -253,6 +266,7 @@
std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
const char* dex_location,
const char* oat_location,
+ const OatFile** out_oat_file,
std::vector<std::string>* error_msgs) {
CHECK(dex_location != nullptr);
CHECK(error_msgs != nullptr);
@@ -311,6 +325,7 @@
if (accept_oat_file) {
VLOG(class_linker) << "Registering " << oat_file->GetLocation();
source_oat_file = RegisterOatFile(std::move(oat_file));
+ *out_oat_file = source_oat_file;
}
}
@@ -344,4 +359,26 @@
return dex_files;
}
+bool OatFileManager::RegisterOatFileLocation(const std::string& oat_location) {
+ WriterMutexLock mu(Thread::Current(), *Locks::oat_file_count_lock_);
+ auto it = oat_file_count_.find(oat_location);
+ if (it != oat_file_count_.end()) {
+ ++it->second;
+ return false;
+ }
+ oat_file_count_.insert(std::pair<std::string, size_t>(oat_location, 1u));
+ return true;
+}
+
+void OatFileManager::UnRegisterOatFileLocation(const std::string& oat_location) {
+ WriterMutexLock mu(Thread::Current(), *Locks::oat_file_count_lock_);
+ auto it = oat_file_count_.find(oat_location);
+ if (it != oat_file_count_.end()) {
+ --it->second;
+ if (it->second == 0) {
+ oat_file_count_.erase(it);
+ }
+ }
+}
+
} // namespace art
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index 3059cb5..af7efb4 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -18,7 +18,9 @@
#define ART_RUNTIME_OAT_FILE_MANAGER_H_
#include <memory>
+#include <set>
#include <string>
+#include <unordered_map>
#include <vector>
#include "base/macros.h"
@@ -49,10 +51,23 @@
const OatFile* RegisterOatFile(std::unique_ptr<const OatFile> oat_file)
REQUIRES(!Locks::oat_file_manager_lock_);
+ void UnRegisterAndDeleteOatFile(const OatFile* oat_file)
+ REQUIRES(!Locks::oat_file_manager_lock_);
+
// Find the first opened oat file with the same location, returns null if there are none.
const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location) const
REQUIRES(!Locks::oat_file_manager_lock_);
+ // Attempt to reserve a location, returns false if it is already reserved or already in used by
+ // an oat file.
+ bool RegisterOatFileLocation(const std::string& oat_location)
+ REQUIRES(!Locks::oat_file_count_lock_);
+
+ // Unreserve oat file location, should only be used for error cases since RegisterOatFile will
+ // remove the reserved location.
+ void UnRegisterOatFileLocation(const std::string& oat_location)
+ REQUIRES(!Locks::oat_file_count_lock_);
+
// Returns true if we have a non pic oat file.
bool HaveNonPicOatFile() const {
return have_non_pic_oat_file_;
@@ -86,7 +101,8 @@
std::vector<std::unique_ptr<const DexFile>> OpenDexFilesFromOat(
const char* dex_location,
const char* oat_location,
- /*out*/std::vector<std::string>* error_msgs)
+ /*out*/ const OatFile** out_oat_file,
+ /*out*/ std::vector<std::string>* error_msgs)
REQUIRES(!Locks::oat_file_manager_lock_, !Locks::mutator_lock_);
private:
@@ -95,7 +111,11 @@
bool HasCollisions(const OatFile* oat_file, /*out*/std::string* error_msg) const
REQUIRES(!Locks::oat_file_manager_lock_);
- std::vector<std::unique_ptr<const OatFile>> oat_files_ GUARDED_BY(Locks::oat_file_manager_lock_);
+ const OatFile* FindOpenedOatFileFromOatLocationLocked(const std::string& oat_location) const
+ REQUIRES(Locks::oat_file_manager_lock_);
+
+ std::set<std::unique_ptr<const OatFile>> oat_files_ GUARDED_BY(Locks::oat_file_manager_lock_);
+ std::unordered_map<std::string, size_t> oat_file_count_ GUARDED_BY(Locks::oat_file_count_lock_);
bool have_non_pic_oat_file_;
DISALLOW_COPY_AND_ASSIGN(OatFileManager);
};
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 50e2053..ae16c7f 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -269,10 +269,10 @@
.Define("-Xfingerprint:_")
.WithType<std::string>()
.IntoKey(M::Fingerprint)
- .Define({"-Xexperimental-lambdas", "-Xnoexperimental-lambdas"})
- .WithType<bool>()
- .WithValues({true, false})
- .IntoKey(M::ExperimentalLambdas)
+ .Define("-Xexperimental:_")
+ .WithType<ExperimentalFlags>()
+ .AppendValues()
+ .IntoKey(M::Experimental)
.Ignore({
"-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
"-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
@@ -557,7 +557,14 @@
args.Set(M::HeapGrowthLimit, args.GetOrDefault(M::MemoryMaximumSize));
}
- if (args.GetOrDefault(M::ExperimentalLambdas)) {
+ if (args.GetOrDefault(M::Experimental) & ExperimentalFlags::kDefaultMethods) {
+ LOG(WARNING) << "Default method support has been enabled. The verifier will be less strict "
+ << "in some cases. All existing invoke opcodes have an unstable updated "
+ << "specification and are nearly guaranteed to change over time. Do not attempt "
+ << "to write shipping code against the invoke opcodes with this flag.";
+ }
+
+ if (args.GetOrDefault(M::Experimental) & ExperimentalFlags::kLambdas) {
LOG(WARNING) << "Experimental lambdas have been enabled. All lambda opcodes have "
<< "an unstable specification and are nearly guaranteed to change over time. "
<< "Do not attempt to write shipping code against these opcodes.";
@@ -682,8 +689,8 @@
UsageMessage(stream, " -X[no]image-dex2oat (Whether to create and use a boot image)\n");
UsageMessage(stream, " -Xno-dex-file-fallback "
"(Don't fall back to dex files without oat files)\n");
- UsageMessage(stream, " -X[no]experimental-lambdas\n"
- " (Enable new experimental dalvik opcodes, off by default)\n");
+ UsageMessage(stream, " -Xexperimental:{lambdas,default-methods} "
+ "(Enable new experimental dalvik opcodes and semantics, off by default)\n");
UsageMessage(stream, "\n");
UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n");
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 7a1f0af..cd09bee 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -64,6 +64,7 @@
#include "debugger.h"
#include "elf_file.h"
#include "entrypoints/runtime_asm_entrypoints.h"
+#include "experimental_flags.h"
#include "fault_handler.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/heap.h"
@@ -199,7 +200,7 @@
no_sig_chain_(false),
is_native_bridge_loaded_(false),
zygote_max_failed_boots_(0),
- experimental_lambdas_(false) {
+ experimental_flags_(ExperimentalFlags::kNone) {
CheckAsmSupportOffsetsAndSizes();
std::fill(callee_save_methods_, callee_save_methods_ + arraysize(callee_save_methods_), 0u);
}
@@ -282,10 +283,10 @@
delete monitor_list_;
delete monitor_pool_;
delete class_linker_;
- oat_file_manager_.reset();
delete heap_;
delete intern_table_;
delete java_vm_;
+ delete oat_file_manager_;
Thread::Shutdown();
QuasiAtomic::Shutdown();
verifier::MethodVerifier::Shutdown();
@@ -833,7 +834,7 @@
QuasiAtomic::Startup();
- oat_file_manager_.reset(new OatFileManager);
+ oat_file_manager_ = new OatFileManager;
Monitor::Init(runtime_options.GetOrDefault(Opt::LockProfThreshold),
runtime_options.GetOrDefault(Opt::HookIsSensitiveThread));
@@ -884,7 +885,7 @@
}
zygote_max_failed_boots_ = runtime_options.GetOrDefault(Opt::ZygoteMaxFailedBoots);
- experimental_lambdas_ = runtime_options.GetOrDefault(Opt::ExperimentalLambdas);
+ experimental_flags_ = runtime_options.GetOrDefault(Opt::Experimental);
XGcOption xgc_option = runtime_options.GetOrDefault(Opt::GcOption);
ATRACE_BEGIN("CreateHeap");
diff --git a/runtime/runtime.h b/runtime/runtime.h
index abccb44..458f08a 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -28,6 +28,7 @@
#include "arch/instruction_set.h"
#include "base/macros.h"
+#include "experimental_flags.h"
#include "gc_root.h"
#include "instrumentation.h"
#include "jobject_comparator.h"
@@ -532,8 +533,8 @@
return zygote_max_failed_boots_;
}
- bool AreExperimentalLambdasEnabled() const {
- return experimental_lambdas_;
+ bool AreExperimentalFlagsEnabled(ExperimentalFlags flags) {
+ return (experimental_flags_ & flags) != ExperimentalFlags::kNone;
}
lambda::BoxTable* GetLambdaBoxTable() const {
@@ -576,7 +577,7 @@
OatFileManager& GetOatFileManager() const {
DCHECK(oat_file_manager_ != nullptr);
- return *oat_file_manager_.get();
+ return *oat_file_manager_;
}
private:
@@ -769,7 +770,7 @@
// eventually publish them as public-usable opcodes, but they aren't ready yet.
//
// Experimental opcodes should not be used by other production code.
- bool experimental_lambdas_;
+ ExperimentalFlags experimental_flags_;
MethodRefToStringInitRegMap method_ref_string_init_reg_map_;
@@ -777,7 +778,7 @@
std::string fingerprint_;
// Oat file manager, keeps track of what oat files are open.
- std::unique_ptr<OatFileManager> oat_file_manager_;
+ OatFileManager* oat_file_manager_;
DISALLOW_COPY_AND_ASSIGN(Runtime);
};
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index d88e84b..7b5bc1a 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -114,7 +114,7 @@
RUNTIME_OPTIONS_KEY (Unit, NoDexFileFallback)
RUNTIME_OPTIONS_KEY (std::string, CpuAbiList)
RUNTIME_OPTIONS_KEY (std::string, Fingerprint)
-RUNTIME_OPTIONS_KEY (bool, ExperimentalLambdas, false) // -X[no]experimental-lambdas
+RUNTIME_OPTIONS_KEY (ExperimentalFlags, Experimental, ExperimentalFlags::kNone) // -Xexperimental:{, lambdas, default-methods}
// Not parse-able from command line, but can be provided explicitly.
// (Do not add anything here that is defined in ParsedOptions::MakeParser)
diff --git a/runtime/thread.h b/runtime/thread.h
index 8cea10c..8f3461a 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -626,6 +626,24 @@
return ThreadOffsetFromTlsPtr<pointer_size>(OFFSETOF_MEMBER(tls_ptr_sized_values, thread_local_objects));
}
+ template<size_t pointer_size>
+ static ThreadOffset<pointer_size> RosAllocRunsOffset() {
+ return ThreadOffsetFromTlsPtr<pointer_size>(OFFSETOF_MEMBER(tls_ptr_sized_values,
+ rosalloc_runs));
+ }
+
+ template<size_t pointer_size>
+ static ThreadOffset<pointer_size> ThreadLocalAllocStackTopOffset() {
+ return ThreadOffsetFromTlsPtr<pointer_size>(OFFSETOF_MEMBER(tls_ptr_sized_values,
+ thread_local_alloc_stack_top));
+ }
+
+ template<size_t pointer_size>
+ static ThreadOffset<pointer_size> ThreadLocalAllocStackEndOffset() {
+ return ThreadOffsetFromTlsPtr<pointer_size>(OFFSETOF_MEMBER(tls_ptr_sized_values,
+ thread_local_alloc_stack_end));
+ }
+
// Size of stack less any space reserved for stack overflow
size_t GetStackSize() const {
return tlsPtr_.stack_size - (tlsPtr_.stack_end - tlsPtr_.stack_begin);
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index eed3e22..4051a1c 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -30,6 +30,7 @@
#include "dex_instruction-inl.h"
#include "dex_instruction_utils.h"
#include "dex_instruction_visitor.h"
+#include "experimental_flags.h"
#include "gc/accounting/card_table-inl.h"
#include "indenter.h"
#include "intern_table.h"
@@ -560,6 +561,7 @@
bool MethodVerifier::Verify() {
// Some older code doesn't correctly mark constructors as such. Test for this case by looking at
// the name.
+ Runtime* runtime = Runtime::Current();
const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
const char* method_name = dex_file_->StringDataByIdx(method_id.name_idx_);
bool instance_constructor_by_name = strcmp("<init>", method_name) == 0;
@@ -628,9 +630,13 @@
}
}
if ((class_def_->GetJavaAccessFlags() & kAccInterface) != 0) {
- // Interface methods must be public and abstract.
- if ((method_access_flags_ & (kAccPublic | kAccAbstract)) != (kAccPublic | kAccAbstract)) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface methods must be public and abstract";
+ // Interface methods must be public and abstract (if default methods are disabled).
+ bool default_methods_supported =
+ runtime->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods);
+ uint32_t kRequired = kAccPublic | (default_methods_supported ? 0 : kAccAbstract);
+ if ((method_access_flags_ & kRequired) != kRequired) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface methods must be public"
+ << (default_methods_supported ? "" : " and abstract");
return false;
}
// In addition to the above, interface methods must not be protected.
@@ -657,10 +663,22 @@
return false;
}
- // Only the static initializer may have code in an interface.
if ((class_def_->GetJavaAccessFlags() & kAccInterface) != 0) {
- // Interfaces may have static initializers for their fields.
- if (!IsConstructor() || !IsStatic()) {
+ // Interfaces may always have static initializers for their fields. If we are running with
+ // default methods enabled we also allow other public, static, non-final methods to have code.
+ // Otherwise that is the only type of method allowed.
+ if (runtime->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods)) {
+ if (IsInstanceConstructor()) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have non-static constructor";
+ return false;
+ } else if (method_access_flags_ & kAccFinal) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have final methods";
+ return false;
+ } else if (!(method_access_flags_ & kAccPublic)) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have non-public members";
+ return false;
+ }
+ } else if (!IsConstructor() || !IsStatic()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface methods must be abstract";
return false;
}
@@ -682,6 +700,7 @@
<< " regs=" << code_item_->registers_size_;
return false;
}
+
// Allocate and initialize an array to hold instruction data.
insn_flags_.reset(new InstructionFlags[code_item_->insns_size_in_code_units_]());
// Run through the instructions and see if the width checks out.
@@ -693,8 +712,8 @@
// Perform code-flow analysis and return.
result = result && VerifyCodeFlow();
// Compute information for compiler.
- if (result && Runtime::Current()->IsCompiler()) {
- result = Runtime::Current()->GetCompilerCallbacks()->MethodVerified(this);
+ if (result && runtime->IsCompiler()) {
+ result = runtime->GetCompilerCallbacks()->MethodVerified(this);
}
return result;
}
diff --git a/test/141-class-unload/expected.txt b/test/141-class-unload/expected.txt
index 53d7abe..11de660 100644
--- a/test/141-class-unload/expected.txt
+++ b/test/141-class-unload/expected.txt
@@ -21,3 +21,4 @@
JNI_OnLoad called
class null false test
JNI_OnUnload called
+Number of loaded unload-ex maps 0
diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java
index 3cc43ac..0640b36 100644
--- a/test/141-class-unload/src/Main.java
+++ b/test/141-class-unload/src/Main.java
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
@@ -43,11 +46,28 @@
testStackTrace(constructor);
// Stress test to make sure we dont leak memory.
stressTest(constructor);
+ // Test that the oat files are unloaded.
+ testOatFilesUnloaded(getPid());
} catch (Exception e) {
System.out.println(e);
}
}
+ private static void testOatFilesUnloaded(int pid) throws Exception {
+ BufferedReader reader = new BufferedReader(new FileReader ("/proc/" + pid + "/maps"));
+ String line;
+ int count = 0;
+ Runtime.getRuntime().gc();
+ System.runFinalization();
+ while ((line = reader.readLine()) != null) {
+ if (line.contains("@141-class-unload-ex.jar")) {
+ System.out.println(line);
+ ++count;
+ }
+ }
+ System.out.println("Number of loaded unload-ex maps " + count);
+ }
+
private static void stressTest(Constructor constructor) throws Exception {
for (int i = 0; i <= 100; ++i) {
setUpUnloadLoader(constructor, false);
@@ -163,4 +183,8 @@
loadLibrary.invoke(intHolder, nativeLibraryName);
return new WeakReference(loader);
}
+
+ private static int getPid() throws Exception {
+ return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());
+ }
}
diff --git a/test/538-checker-embed-constants/expected.txt b/test/538-checker-embed-constants/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/538-checker-embed-constants/expected.txt
diff --git a/test/538-checker-embed-constants/info.txt b/test/538-checker-embed-constants/info.txt
new file mode 100644
index 0000000..5a722ec
--- /dev/null
+++ b/test/538-checker-embed-constants/info.txt
@@ -0,0 +1 @@
+Test embedding of constants in assembler instructions.
diff --git a/test/538-checker-embed-constants/src/Main.java b/test/538-checker-embed-constants/src/Main.java
new file mode 100644
index 0000000..d8618e3
--- /dev/null
+++ b/test/538-checker-embed-constants/src/Main.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+public class Main {
+
+ public static void assertIntEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void assertLongEquals(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ /// CHECK-START-ARM: int Main.and255(int) disassembly (after)
+ /// CHECK-NOT: movs {{r\d+}}, #255
+ /// CHECK: and {{r\d+}}, {{r\d+}}, #255
+
+ public static int and255(int arg) {
+ return arg & 255;
+ }
+
+ /// CHECK-START-ARM: int Main.and511(int) disassembly (after)
+ /// CHECK: movw {{r\d+}}, #511
+ /// CHECK: and{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+
+ public static int and511(int arg) {
+ return arg & 511;
+ }
+
+ /// CHECK-START-ARM: int Main.andNot15(int) disassembly (after)
+ /// CHECK-NOT: mvn {{r\d+}}, #15
+ /// CHECK: bic {{r\d+}}, {{r\d+}}, #15
+
+ public static int andNot15(int arg) {
+ return arg & ~15;
+ }
+
+ /// CHECK-START-ARM: int Main.or255(int) disassembly (after)
+ /// CHECK-NOT: movs {{r\d+}}, #255
+ /// CHECK: orr {{r\d+}}, {{r\d+}}, #255
+
+ public static int or255(int arg) {
+ return arg | 255;
+ }
+
+ /// CHECK-START-ARM: int Main.or511(int) disassembly (after)
+ /// CHECK: movw {{r\d+}}, #511
+ /// CHECK: orr{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+
+ public static int or511(int arg) {
+ return arg | 511;
+ }
+
+ /// CHECK-START-ARM: int Main.orNot15(int) disassembly (after)
+ /// CHECK-NOT: mvn {{r\d+}}, #15
+ /// CHECK: orn {{r\d+}}, {{r\d+}}, #15
+
+ public static int orNot15(int arg) {
+ return arg | ~15;
+ }
+
+ /// CHECK-START-ARM: int Main.xor255(int) disassembly (after)
+ /// CHECK-NOT: movs {{r\d+}}, #255
+ /// CHECK: eor {{r\d+}}, {{r\d+}}, #255
+
+ public static int xor255(int arg) {
+ return arg ^ 255;
+ }
+
+ /// CHECK-START-ARM: int Main.xor511(int) disassembly (after)
+ /// CHECK: movw {{r\d+}}, #511
+ /// CHECK: eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+
+ public static int xor511(int arg) {
+ return arg ^ 511;
+ }
+
+ /// CHECK-START-ARM: int Main.xorNot15(int) disassembly (after)
+ /// CHECK: mvn {{r\d+}}, #15
+ /// CHECK: eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+
+ public static int xorNot15(int arg) {
+ return arg ^ ~15;
+ }
+
+ /// CHECK-START-ARM: long Main.and255(long) disassembly (after)
+ /// CHECK-NOT: movs {{r\d+}}, #255
+ /// CHECK-NOT: and
+ /// CHECK-NOT: bic
+ /// CHECK-DAG: and {{r\d+}}, {{r\d+}}, #255
+ /// CHECK-DAG: movs {{r\d+}}, #0
+ /// CHECK-NOT: and
+ /// CHECK-NOT: bic
+
+ public static long and255(long arg) {
+ return arg & 255L;
+ }
+
+ /// CHECK-START-ARM: long Main.and511(long) disassembly (after)
+ /// CHECK: movw {{r\d+}}, #511
+ /// CHECK-NOT: and
+ /// CHECK-NOT: bic
+ /// CHECK-DAG: and{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+ /// CHECK-DAG: movs {{r\d+}}, #0
+ /// CHECK-NOT: and
+ /// CHECK-NOT: bic
+
+ public static long and511(long arg) {
+ return arg & 511L;
+ }
+
+ /// CHECK-START-ARM: long Main.andNot15(long) disassembly (after)
+ /// CHECK-NOT: mvn {{r\d+}}, #15
+ /// CHECK-NOT: and
+ /// CHECK-NOT: bic
+ /// CHECK: bic {{r\d+}}, {{r\d+}}, #15
+ /// CHECK-NOT: and
+ /// CHECK-NOT: bic
+
+ public static long andNot15(long arg) {
+ return arg & ~15L;
+ }
+
+ /// CHECK-START-ARM: long Main.and0xfffffff00000000f(long) disassembly (after)
+ /// CHECK-NOT: movs {{r\d+}}, #15
+ /// CHECK-NOT: mvn {{r\d+}}, #15
+ /// CHECK-NOT: and
+ /// CHECK-NOT: bic
+ /// CHECK-DAG: and {{r\d+}}, {{r\d+}}, #15
+ /// CHECK-DAG: bic {{r\d+}}, {{r\d+}}, #15
+ /// CHECK-NOT: and
+ /// CHECK-NOT: bic
+
+ public static long and0xfffffff00000000f(long arg) {
+ return arg & 0xfffffff00000000fL;
+ }
+
+ /// CHECK-START-ARM: long Main.or255(long) disassembly (after)
+ /// CHECK-NOT: movs {{r\d+}}, #255
+ /// CHECK-NOT: orr
+ /// CHECK-NOT: orn
+ /// CHECK: orr {{r\d+}}, {{r\d+}}, #255
+ /// CHECK-NOT: orr
+ /// CHECK-NOT: orn
+
+ public static long or255(long arg) {
+ return arg | 255L;
+ }
+
+ /// CHECK-START-ARM: long Main.or511(long) disassembly (after)
+ /// CHECK: movw {{r\d+}}, #511
+ /// CHECK-NOT: orr
+ /// CHECK-NOT: orn
+ /// CHECK: orr{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+ /// CHECK-NOT: orr
+ /// CHECK-NOT: orn
+
+ public static long or511(long arg) {
+ return arg | 511L;
+ }
+
+ /// CHECK-START-ARM: long Main.orNot15(long) disassembly (after)
+ /// CHECK-NOT: mvn {{r\d+}}, #15
+ /// CHECK-NOT: orr
+ /// CHECK-NOT: orn
+ /// CHECK-DAG: orn {{r\d+}}, {{r\d+}}, #15
+ /// CHECK-DAG: mvn {{r\d+}}, #0
+ /// CHECK-NOT: orr
+ /// CHECK-NOT: orn
+
+ public static long orNot15(long arg) {
+ return arg | ~15L;
+ }
+
+ /// CHECK-START-ARM: long Main.or0xfffffff00000000f(long) disassembly (after)
+ /// CHECK-NOT: movs {{r\d+}}, #15
+ /// CHECK-NOT: mvn {{r\d+}}, #15
+ /// CHECK-NOT: orr
+ /// CHECK-NOT: orn
+ /// CHECK-DAG: orr {{r\d+}}, {{r\d+}}, #15
+ /// CHECK-DAG: orn {{r\d+}}, {{r\d+}}, #15
+ /// CHECK-NOT: orr
+ /// CHECK-NOT: orn
+
+ public static long or0xfffffff00000000f(long arg) {
+ return arg | 0xfffffff00000000fL;
+ }
+
+ /// CHECK-START-ARM: long Main.xor255(long) disassembly (after)
+ /// CHECK-NOT: movs {{r\d+}}, #255
+ /// CHECK-NOT: eor
+ /// CHECK: eor {{r\d+}}, {{r\d+}}, #255
+ /// CHECK-NOT: eor
+
+ public static long xor255(long arg) {
+ return arg ^ 255L;
+ }
+
+ /// CHECK-START-ARM: long Main.xor511(long) disassembly (after)
+ /// CHECK: movw {{r\d+}}, #511
+ /// CHECK-NOT: eor
+ /// CHECK-DAG: eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+ /// CHECK-NOT: eor
+
+ public static long xor511(long arg) {
+ return arg ^ 511L;
+ }
+
+ /// CHECK-START-ARM: long Main.xorNot15(long) disassembly (after)
+ /// CHECK-DAG: mvn {{r\d+}}, #15
+ /// CHECK-DAG: mov.w {{r\d+}}, #-1
+ /// CHECK-NOT: eor
+ /// CHECK-DAG: eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+ /// CHECK-DAG: eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+ /// CHECK-NOT: eor
+
+ public static long xorNot15(long arg) {
+ return arg ^ ~15L;
+ }
+
+ // Note: No support for partial long constant embedding.
+ /// CHECK-START-ARM: long Main.xor0xfffffff00000000f(long) disassembly (after)
+ /// CHECK-DAG: movs {{r\d+}}, #15
+ /// CHECK-DAG: mvn {{r\d+}}, #15
+ /// CHECK-NOT: eor
+ /// CHECK-DAG: eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+ /// CHECK-DAG: eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+ /// CHECK-NOT: eor
+
+ public static long xor0xfffffff00000000f(long arg) {
+ return arg ^ 0xfffffff00000000fL;
+ }
+
+ /// CHECK-START-ARM: long Main.xor0xf00000000000000f(long) disassembly (after)
+ /// CHECK-NOT: movs {{r\d+}}, #15
+ /// CHECK-NOT: mov.w {{r\d+}}, #-268435456
+ /// CHECK-NOT: eor
+ /// CHECK-DAG: eor {{r\d+}}, {{r\d+}}, #15
+ /// CHECK-DAG: eor {{r\d+}}, {{r\d+}}, #-268435456
+ /// CHECK-NOT: eor
+
+ public static long xor0xf00000000000000f(long arg) {
+ return arg ^ 0xf00000000000000fL;
+ }
+
+ public static void main(String[] args) {
+ int arg = 0x87654321;
+ assertIntEquals(and255(arg), 0x21);
+ assertIntEquals(and511(arg), 0x121);
+ assertIntEquals(andNot15(arg), 0x87654320);
+ assertIntEquals(or255(arg), 0x876543ff);
+ assertIntEquals(or511(arg), 0x876543ff);
+ assertIntEquals(orNot15(arg), 0xfffffff1);
+ assertIntEquals(xor255(arg), 0x876543de);
+ assertIntEquals(xor511(arg), 0x876542de);
+ assertIntEquals(xorNot15(arg), 0x789abcd1);
+
+ long longArg = 0x1234567887654321L;
+ assertLongEquals(and255(longArg), 0x21L);
+ assertLongEquals(and511(longArg), 0x121L);
+ assertLongEquals(andNot15(longArg), 0x1234567887654320L);
+ assertLongEquals(and0xfffffff00000000f(longArg), 0x1234567000000001L);
+ assertLongEquals(or255(longArg), 0x12345678876543ffL);
+ assertLongEquals(or511(longArg), 0x12345678876543ffL);
+ assertLongEquals(orNot15(longArg), 0xfffffffffffffff1L);
+ assertLongEquals(or0xfffffff00000000f(longArg), 0xfffffff88765432fL);
+ assertLongEquals(xor255(longArg), 0x12345678876543deL);
+ assertLongEquals(xor511(longArg), 0x12345678876542deL);
+ assertLongEquals(xorNot15(longArg), 0xedcba987789abcd1L);
+ assertLongEquals(xor0xfffffff00000000f(longArg), 0xedcba9888765432eL);
+ assertLongEquals(xor0xf00000000000000f(longArg), 0xe23456788765432eL);
+ }
+}
diff --git a/test/955-lambda-smali/run b/test/955-lambda-smali/run
index 2aeca8c..b754680 100755
--- a/test/955-lambda-smali/run
+++ b/test/955-lambda-smali/run
@@ -15,4 +15,4 @@
# limitations under the License.
# Ensure that the lambda experimental opcodes are turned on for dalvikvm and dex2oat
-${RUN} "$@" --runtime-option -Xexperimental-lambdas -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental-lambdas
+${RUN} "$@" --runtime-option -Xexperimental:lambdas -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:lambdas
diff --git a/test/960-default-smali/build b/test/960-default-smali/build
new file mode 100755
index 0000000..c786687
--- /dev/null
+++ b/test/960-default-smali/build
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+# make us exit on a failure
+set -e
+
+# Generate the smali Main.smali file or fail
+./util-src/generate_smali.py ./smali
+
+if [[ $@ == *"--jvm"* ]]; then
+ # Build the Java files if we are running a --jvm test
+ mkdir -p src
+ mkdir -p classes
+ ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src
+ ${JAVAC} -implicit:none -d classes $(find src -name '*.java')
+fi
+
+# Build the smali files and make a dex
+${SMALI} -JXmx256m --experimental --api-level 23 --output classes.dex $(find smali -name '*.smali')
+zip "$TEST_NAME.jar" classes.dex
diff --git a/test/960-default-smali/expected.txt b/test/960-default-smali/expected.txt
new file mode 100644
index 0000000..7671eed
--- /dev/null
+++ b/test/960-default-smali/expected.txt
@@ -0,0 +1,84 @@
+Testing for type A
+A-virtual A.SayHi()='Hi '
+A-interface Greeter.SayHi()='Hi '
+A-virtual A.SayHiTwice()='Hi Hi '
+A-interface Greeter.SayHiTwice()='Hi Hi '
+End testing for type A
+Testing for type B
+B-virtual B.SayHi()='Hello '
+B-interface Greeter.SayHi()='Hello '
+B-interface Greeter2.SayHi()='Hello '
+B-virtual B.SayHiTwice()='I say Hello Hello '
+B-interface Greeter.SayHiTwice()='I say Hello Hello '
+B-interface Greeter2.SayHiTwice()='I say Hello Hello '
+End testing for type B
+Testing for type C
+C-virtual A.SayHi()='Hi '
+C-virtual C.SayHi()='Hi '
+C-interface Greeter.SayHi()='Hi '
+C-virtual A.SayHiTwice()='You don't control me'
+C-virtual C.SayHiTwice()='You don't control me'
+C-interface Greeter.SayHiTwice()='You don't control me'
+End testing for type C
+Testing for type D
+D-virtual D.GetName()='Alex '
+D-interface Greeter3.GetName()='Alex '
+D-virtual D.SayHi()='Hello Alex '
+D-interface Greeter.SayHi()='Hello Alex '
+D-interface Greeter3.SayHi()='Hello Alex '
+D-virtual D.SayHiTwice()='Hello Alex Hello Alex '
+D-interface Greeter.SayHiTwice()='Hello Alex Hello Alex '
+D-interface Greeter3.SayHiTwice()='Hello Alex Hello Alex '
+End testing for type D
+Testing for type E
+E-virtual A.SayHi()='Hi2 '
+E-virtual E.SayHi()='Hi2 '
+E-interface Greeter.SayHi()='Hi2 '
+E-interface Greeter2.SayHi()='Hi2 '
+E-virtual A.SayHiTwice()='I say Hi2 Hi2 '
+E-virtual E.SayHiTwice()='I say Hi2 Hi2 '
+E-interface Greeter.SayHiTwice()='I say Hi2 Hi2 '
+E-interface Greeter2.SayHiTwice()='I say Hi2 Hi2 '
+End testing for type E
+Testing for type F
+F-interface Attendant.GetPlace()='android'
+F-virtual F.GetPlace()='android'
+F-virtual A.SayHi()='Hi '
+F-interface Attendant.SayHi()='Hi '
+F-virtual F.SayHi()='Hi '
+F-interface Greeter.SayHi()='Hi '
+F-virtual A.SayHiTwice()='We can override both interfaces'
+F-interface Attendant.SayHiTwice()='We can override both interfaces'
+F-virtual F.SayHiTwice()='We can override both interfaces'
+F-interface Greeter.SayHiTwice()='We can override both interfaces'
+End testing for type F
+Testing for type G
+G-interface Attendant.GetPlace()='android'
+G-virtual G.GetPlace()='android'
+G-interface Attendant.SayHi()='welcome to android'
+G-virtual G.SayHi()='welcome to android'
+G-interface Attendant.SayHiTwice()='welcome to androidwelcome to android'
+G-virtual G.SayHiTwice()='welcome to androidwelcome to android'
+End testing for type G
+Testing for type H
+H-interface Extension.SayHi()='welcome '
+H-virtual H.SayHi()='welcome '
+End testing for type H
+Testing for type I
+I-virtual A.SayHi()='Hi '
+I-interface Greeter.SayHi()='Hi '
+I-interface Greeter2.SayHi()='Hi '
+I-virtual I.SayHi()='Hi '
+I-virtual A.SayHiTwice()='I say Hi Hi '
+I-interface Greeter.SayHiTwice()='I say Hi Hi '
+I-interface Greeter2.SayHiTwice()='I say Hi Hi '
+I-virtual I.SayHiTwice()='I say Hi Hi '
+End testing for type I
+Testing for type J
+J-virtual A.SayHi()='Hi '
+J-interface Greeter.SayHi()='Hi '
+J-virtual J.SayHi()='Hi '
+J-virtual A.SayHiTwice()='Hi Hi '
+J-interface Greeter.SayHiTwice()='Hi Hi '
+J-virtual J.SayHiTwice()='Hi Hi '
+End testing for type J
diff --git a/test/960-default-smali/info.txt b/test/960-default-smali/info.txt
new file mode 100644
index 0000000..eb596e2
--- /dev/null
+++ b/test/960-default-smali/info.txt
@@ -0,0 +1,19 @@
+Smali-based tests for experimental interface default methods.
+
+Obviously needs to run under ART or a Java 8 Language runtime and compiler.
+
+When run a Main.smali file will be generated by the util-src/generate_smali.py
+script. If we run with --jvm we will use the tools/extract-embedded-java script to
+turn the smali into equivalent Java using the embedded Java code.
+
+When updating be sure to write the equivalent Java code in comments of the smali
+files.
+
+Care should be taken when updating the generate_smali.py script. It must always
+return equivalent output when run multiple times.
+
+To update the test files do the following steps:
+ <Add new classes/interfaces>
+ <Add these classes/interfaces to ./smali/classes.xml>
+ JAVA_HOME="/path/to/java-8-jdk" ../run-test --use-java-home --update --jvm --host 956-default-smali
+ git add ./smali/classes.xml ./expected.txt
diff --git a/test/960-default-smali/run b/test/960-default-smali/run
new file mode 100755
index 0000000..e378b06
--- /dev/null
+++ b/test/960-default-smali/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+if echo $@ | grep -q -- "--jvm"; then
+ ${RUN} "$@"
+else
+ ${RUN} "$@" --runtime-option -Xexperimental:default-methods -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:default-methods
+fi
diff --git a/test/960-default-smali/smali/A.smali b/test/960-default-smali/smali/A.smali
new file mode 100644
index 0000000..e755612
--- /dev/null
+++ b/test/960-default-smali/smali/A.smali
@@ -0,0 +1,38 @@
+# /*
+# * Copyright 2015 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LA;
+.super Ljava/lang/Object;
+.implements LGreeter;
+
+# class A implements Greeter {
+# public String SayHi() {
+# return "Hi ";
+# }
+# }
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public SayHi()Ljava/lang/String;
+ .registers 1
+
+ const-string v0, "Hi "
+ return-object v0
+.end method
diff --git a/test/960-default-smali/smali/Attendant.smali b/test/960-default-smali/smali/Attendant.smali
new file mode 100644
index 0000000..ab63aee
--- /dev/null
+++ b/test/960-default-smali/smali/Attendant.smali
@@ -0,0 +1,53 @@
+# /*
+# * Copyright 2015 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public abstract interface LAttendant;
+.super Ljava/lang/Object;
+
+# public interface Attendant {
+# public default String SayHi() {
+# return "welcome to " + GetPlace();
+# }
+# public default String SayHiTwice() {
+# return SayHi() + SayHi();
+# }
+#
+# public String GetPlace();
+# }
+
+.method public SayHi()Ljava/lang/String;
+ .locals 2
+ const-string v0, "welcome to "
+ invoke-interface {p0}, LAttendant;->GetPlace()Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String;
+ move-result-object v0
+ return-object v0
+.end method
+
+.method public SayHiTwice()Ljava/lang/String;
+ .locals 2
+ invoke-interface {p0}, LAttendant;->SayHi()Ljava/lang/String;
+ move-result-object v0
+ invoke-interface {p0}, LAttendant;->SayHi()Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String;
+ move-result-object v0
+ return-object v0
+.end method
+
+.method public abstract GetPlace()Ljava/lang/String;
+.end method
diff --git a/test/960-default-smali/smali/B.smali b/test/960-default-smali/smali/B.smali
new file mode 100644
index 0000000..d847dd1
--- /dev/null
+++ b/test/960-default-smali/smali/B.smali
@@ -0,0 +1,38 @@
+# /*
+# * Copyright 2015 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LB;
+.super Ljava/lang/Object;
+.implements LGreeter2;
+
+# class B implements Greeter2 {
+# public String SayHi() {
+# return "Hello ";
+# }
+# }
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public SayHi()Ljava/lang/String;
+ .registers 1
+
+ const-string v0, "Hello "
+ return-object v0
+.end method
diff --git a/test/960-default-smali/smali/C.smali b/test/960-default-smali/smali/C.smali
new file mode 100644
index 0000000..08a8508
--- /dev/null
+++ b/test/960-default-smali/smali/C.smali
@@ -0,0 +1,37 @@
+# /*
+# * Copyright 2015 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LC;
+.super LA;
+
+# class C extends A {
+# public String SayHiTwice() {
+# return "You don't control me";
+# }
+# }
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, LA;-><init>()V
+ return-void
+.end method
+
+.method public SayHiTwice()Ljava/lang/String;
+ .registers 1
+
+ const-string v0, "You don't control me"
+ return-object v0
+.end method
diff --git a/test/960-default-smali/smali/D.smali b/test/960-default-smali/smali/D.smali
new file mode 100644
index 0000000..32f3b7e
--- /dev/null
+++ b/test/960-default-smali/smali/D.smali
@@ -0,0 +1,38 @@
+# /*
+# * Copyright 2015 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LD;
+.super Ljava/lang/Object;
+.implements LGreeter3;
+
+# class D implements Greeter3 {
+# public String GetName() {
+# return "Alex ";
+# }
+# }
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public GetName()Ljava/lang/String;
+ .registers 1
+
+ const-string v0, "Alex "
+ return-object v0
+.end method
diff --git a/test/960-default-smali/smali/E.smali b/test/960-default-smali/smali/E.smali
new file mode 100644
index 0000000..bae6250
--- /dev/null
+++ b/test/960-default-smali/smali/E.smali
@@ -0,0 +1,38 @@
+# /*
+# * Copyright 2015 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LE;
+.super LA;
+.implements LGreeter2;
+
+# class E extends A implements Greeter2 {
+# public String SayHi() {
+# return "Hi2 ";
+# }
+# }
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, LA;-><init>()V
+ return-void
+.end method
+
+.method public SayHi()Ljava/lang/String;
+ .registers 1
+
+ const-string v0, "Hi2 "
+ return-object v0
+.end method
diff --git a/test/960-default-smali/smali/Extension.smali b/test/960-default-smali/smali/Extension.smali
new file mode 100644
index 0000000..60ffa26
--- /dev/null
+++ b/test/960-default-smali/smali/Extension.smali
@@ -0,0 +1,30 @@
+# /*
+# * Copyright 2015 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public abstract interface LExtension;
+.super Ljava/lang/Object;
+
+# public interface Extension {
+# public default String SayHi() {
+# return "welcome ";
+# }
+# }
+
+.method public SayHi()Ljava/lang/String;
+ .locals 1
+ const-string v0, "welcome "
+ return-object v0
+.end method
diff --git a/test/960-default-smali/smali/F.smali b/test/960-default-smali/smali/F.smali
new file mode 100644
index 0000000..3eaa089
--- /dev/null
+++ b/test/960-default-smali/smali/F.smali
@@ -0,0 +1,47 @@
+# /*
+# * Copyright 2015 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LF;
+.super LA;
+.implements LAttendant;
+
+# class F extends A implements Attendant {
+# public String GetPlace() {
+# return "android";
+# }
+# public String SayHiTwice() {
+# return "We can override both interfaces";
+# }
+# }
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public SayHiTwice()Ljava/lang/String;
+ .registers 1
+
+ const-string v0, "We can override both interfaces"
+ return-object v0
+.end method
+
+.method public GetPlace()Ljava/lang/String;
+ .registers 1
+ const-string v0, "android"
+ return-object v0
+.end method
diff --git a/test/960-default-smali/smali/G.smali b/test/960-default-smali/smali/G.smali
new file mode 100644
index 0000000..446f2a4
--- /dev/null
+++ b/test/960-default-smali/smali/G.smali
@@ -0,0 +1,37 @@
+# /*
+# * Copyright 2015 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LG;
+.super Ljava/lang/Object;
+.implements LAttendant;
+
+# class G implements Attendant {
+# public String GetPlace() {
+# return "android";
+# }
+# }
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public GetPlace()Ljava/lang/String;
+ .registers 1
+ const-string v0, "android"
+ return-object v0
+.end method
diff --git a/test/960-default-smali/smali/Greeter.smali b/test/960-default-smali/smali/Greeter.smali
new file mode 100644
index 0000000..28530ff
--- /dev/null
+++ b/test/960-default-smali/smali/Greeter.smali
@@ -0,0 +1,40 @@
+# /*
+# * Copyright 2015 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public abstract interface LGreeter;
+.super Ljava/lang/Object;
+
+# public interface Greeter {
+# public String SayHi();
+#
+# public default String SayHiTwice() {
+# return SayHi() + SayHi();
+# }
+# }
+
+.method public abstract SayHi()Ljava/lang/String;
+.end method
+
+.method public SayHiTwice()Ljava/lang/String;
+ .locals 2
+ invoke-interface {p0}, LGreeter;->SayHi()Ljava/lang/String;
+ move-result-object v0
+ invoke-interface {p0}, LGreeter;->SayHi()Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String;
+ move-result-object v0
+ return-object v0
+.end method
diff --git a/test/960-default-smali/smali/Greeter2.smali b/test/960-default-smali/smali/Greeter2.smali
new file mode 100644
index 0000000..ace1798
--- /dev/null
+++ b/test/960-default-smali/smali/Greeter2.smali
@@ -0,0 +1,39 @@
+# /*
+# * Copyright 2015 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public abstract interface LGreeter2;
+.super Ljava/lang/Object;
+.implements LGreeter;
+
+# public interface Greeter2 extends Greeter {
+# public default String SayHiTwice() {
+# return "I say " + SayHi() + SayHi();
+# }
+# }
+
+.method public SayHiTwice()Ljava/lang/String;
+ .locals 3
+ const-string v0, "I say "
+ invoke-interface {p0}, LGreeter;->SayHi()Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String;
+ move-result-object v0
+ invoke-interface {p0}, LGreeter;->SayHi()Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String;
+ move-result-object v0
+ return-object v0
+.end method
diff --git a/test/960-default-smali/smali/Greeter3.smali b/test/960-default-smali/smali/Greeter3.smali
new file mode 100644
index 0000000..31fc2e7
--- /dev/null
+++ b/test/960-default-smali/smali/Greeter3.smali
@@ -0,0 +1,40 @@
+# /*
+# * Copyright 2015 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public abstract interface LGreeter3;
+.super Ljava/lang/Object;
+.implements LGreeter;
+
+# public interface Greeter3 extends Greeter {
+# public String GetName();
+#
+# public default String SayHi() {
+# return "Hello " + GetName();
+# }
+# }
+
+.method public abstract GetName()Ljava/lang/String;
+.end method
+
+.method public SayHi()Ljava/lang/String;
+ .locals 2
+ const-string v0, "Hello "
+ invoke-interface {p0}, LGreeter3;->GetName()Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String;
+ move-result-object v0
+ return-object v0
+.end method
diff --git a/test/960-default-smali/smali/H.smali b/test/960-default-smali/smali/H.smali
new file mode 100644
index 0000000..82065ea
--- /dev/null
+++ b/test/960-default-smali/smali/H.smali
@@ -0,0 +1,28 @@
+# /*
+# * Copyright 2015 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LH;
+.super Ljava/lang/Object;
+.implements LExtension;
+
+# class H implements Extension {
+# }
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
diff --git a/test/960-default-smali/smali/I.smali b/test/960-default-smali/smali/I.smali
new file mode 100644
index 0000000..72fb58a
--- /dev/null
+++ b/test/960-default-smali/smali/I.smali
@@ -0,0 +1,28 @@
+# /*
+# * Copyright 2015 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LI;
+.super LA;
+.implements LGreeter2;
+
+# class I extends A implements Greeter2 {
+# }
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
diff --git a/test/960-default-smali/smali/J.smali b/test/960-default-smali/smali/J.smali
new file mode 100644
index 0000000..93f3d62
--- /dev/null
+++ b/test/960-default-smali/smali/J.smali
@@ -0,0 +1,29 @@
+# /*
+# * Copyright 2015 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LJ;
+.super LA;
+
+# class J extends A {
+# }
+
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, LA;-><init>()V
+ return-void
+.end method
+
diff --git a/test/960-default-smali/smali/classes.xml b/test/960-default-smali/smali/classes.xml
new file mode 100644
index 0000000..0aa41f7
--- /dev/null
+++ b/test/960-default-smali/smali/classes.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2015 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.
+-->
+
+<data>
+ <classes>
+ <class name="A" super="java/lang/Object">
+ <implements>
+ <item>Greeter</item>
+ </implements>
+ <methods> </methods>
+ </class>
+
+ <class name="B" super="java/lang/Object">
+ <implements>
+ <item>Greeter2</item>
+ </implements>
+ <methods> </methods>
+ </class>
+
+ <class name="C" super="A">
+ <implements> </implements>
+ <methods> </methods>
+ </class>
+
+ <class name="D" super="java/lang/Object">
+ <implements>
+ <item>Greeter3</item>
+ </implements>
+ <methods> </methods>
+ </class>
+
+ <class name="E" super="A">
+ <implements>
+ <item>Greeter2</item>
+ </implements>
+ <methods> </methods>
+ </class>
+
+ <class name="F" super="A">
+ <implements>
+ <item>Attendant</item>
+ </implements>
+ <methods> </methods>
+ </class>
+
+ <class name="G" super="java/lang/Object">
+ <implements>
+ <item>Attendant</item>
+ </implements>
+ <methods> </methods>
+ </class>
+
+ <class name="H" super="java/lang/Object">
+ <implements>
+ <item>Extension</item>
+ </implements>
+ <methods> </methods>
+ </class>
+
+ <class name="I" super="A">
+ <implements>
+ <item>Greeter2</item>
+ </implements>
+ <methods> </methods>
+ </class>
+
+ <class name="J" super="A">
+ <implements> </implements>
+ <methods> </methods>
+ </class>
+ </classes>
+
+ <interfaces>
+ <interface name="Extension" super="java/lang/Object">
+ <implements> </implements>
+ <methods>
+ <method type="default">SayHi</method>
+ </methods>
+ </interface>
+
+ <interface name="Greeter" super="java/lang/Object">
+ <implements> </implements>
+ <methods>
+ <method type="abstract">SayHi</method>
+ <method type="default">SayHiTwice</method>
+ </methods>
+ </interface>
+
+ <interface name="Greeter2" super="java/lang/Object">
+ <implements>
+ <item>Greeter</item>
+ </implements>
+ <methods> </methods>
+ </interface>
+
+ <interface name="Greeter3" super="java/lang/Object">
+ <implements>
+ <item>Greeter</item>
+ </implements>
+ <methods>
+ <method type="abstract">GetName</method>
+ </methods>
+ </interface>
+
+ <interface name="Attendant" super="java/lang/Object">
+ <implements> </implements>
+ <methods>
+ <method type="default">SayHi</method>
+ <method type="default">SayHiTwice</method>
+ <method type="abstract">GetPlace</method>
+ </methods>
+ </interface>
+ </interfaces>
+</data>
diff --git a/test/960-default-smali/util-src/generate_smali.py b/test/960-default-smali/util-src/generate_smali.py
new file mode 100755
index 0000000..b2bf1f0
--- /dev/null
+++ b/test/960-default-smali/util-src/generate_smali.py
@@ -0,0 +1,376 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2015 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.
+
+"""
+Generate Smali Main file for test 960
+"""
+
+import os
+import sys
+from pathlib import Path
+
+BUILD_TOP = os.getenv("ANDROID_BUILD_TOP")
+if BUILD_TOP is None:
+ print("ANDROID_BUILD_TOP not set. Please run build/envsetup.sh", file=sys.stderr)
+ sys.exit(1)
+
+# Allow us to import utils and mixins.
+sys.path.append(str(Path(BUILD_TOP)/"art"/"test"/"utils"/"python"))
+
+from testgen.utils import get_copyright
+import testgen.mixins as mixins
+
+from collections import namedtuple
+import itertools
+import functools
+import xml.etree.ElementTree as ET
+
+class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin):
+ """
+ A mainclass and main method for this test.
+ """
+
+ MAIN_CLASS_TEMPLATE = """{copyright}
+.class public LMain;
+.super Ljava/lang/Object;
+
+# class Main {{
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {{p0}}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+{test_groups}
+
+{test_funcs}
+
+{main_func}
+
+# }}
+"""
+
+ MAIN_FUNCTION_TEMPLATE = """
+# public static void main(String[] args) {{
+.method public static main([Ljava/lang/String;)V
+ .locals 2
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+ {test_group_invoke}
+
+ return-void
+.end method
+# }}
+"""
+
+ TEST_GROUP_INVOKE_TEMPLATE = """
+# {test_name}();
+ invoke-static {{}}, {test_name}()V
+"""
+
+ def __init__(self):
+ """
+ Initialize this MainClass
+ """
+ self.tests = set()
+ self.global_funcs = set()
+
+ def add_instance(self, it):
+ """
+ Add an instance test for the given class
+ """
+ self.tests.add(it)
+
+ def add_func(self, f):
+ """
+ Add a function to the class
+ """
+ self.global_funcs.add(f)
+
+ def get_name(self):
+ """
+ Get the name of this class
+ """
+ return "Main"
+
+ def __str__(self):
+ """
+ Print this class
+ """
+ all_tests = sorted(self.tests)
+ test_invoke = ""
+ test_groups = ""
+ for t in all_tests:
+ test_groups += str(t)
+ for t in sorted(all_tests):
+ test_invoke += self.TEST_GROUP_INVOKE_TEMPLATE.format(test_name=t.get_name())
+ main_func = self.MAIN_FUNCTION_TEMPLATE.format(test_group_invoke=test_invoke)
+
+ funcs = ""
+ for f in self.global_funcs:
+ funcs += str(f)
+ return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright('smali'),
+ test_groups=test_groups,
+ main_func=main_func, test_funcs=funcs)
+
+
+class InstanceTest(mixins.Named, mixins.NameComparableMixin):
+ """
+ A method that runs tests for a particular concrete type, It calls the test
+ cases for running it in all possible ways.
+ """
+
+ INSTANCE_TEST_TEMPLATE = """
+# public static void {test_name}() {{
+# System.out.println("Testing for type {ty}");
+# String s = "{ty}";
+# {ty} v = new {ty}();
+.method public static {test_name}()V
+ .locals 3
+ sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v0, "Testing for type {ty}"
+ invoke-virtual {{v2,v0}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+ const-string v0, "{ty}"
+ new-instance v1, L{ty};
+ invoke-direct {{v1}}, L{ty};-><init>()V
+
+ {invokes}
+
+ const-string v0, "End testing for type {ty}"
+ invoke-virtual {{v2,v0}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+# System.out.println("End testing for type {ty}");
+# }}
+"""
+
+ TEST_INVOKE_TEMPLATE = """
+# {fname}(s, v);
+ invoke-static {{v0, v1}}, {fname}(Ljava/lang/String;L{farg};)V
+"""
+
+ def __init__(self, main, ty):
+ """
+ Initialize this test group for the given type
+ """
+ self.ty = ty
+ self.main = main
+ self.funcs = set()
+ self.main.add_instance(self)
+
+ def get_name(self):
+ """
+ Get the name of this test group
+ """
+ return "TEST_NAME_"+self.ty
+
+ def add_func(self, f):
+ """
+ Add a test function to this test group
+ """
+ self.main.add_func(f)
+ self.funcs.add(f)
+
+ def __str__(self):
+ """
+ Returns the smali code for this function
+ """
+ func_invokes = ""
+ for f in sorted(self.funcs, key=lambda a: (a.func, a.farg)):
+ func_invokes += self.TEST_INVOKE_TEMPLATE.format(fname=f.get_name(),
+ farg=f.farg)
+
+ return self.INSTANCE_TEST_TEMPLATE.format(test_name=self.get_name(), ty=self.ty,
+ invokes=func_invokes)
+
+class Func(mixins.Named, mixins.NameComparableMixin):
+ """
+ A single test case that attempts to invoke a function on receiver of a given type.
+ """
+
+ TEST_FUNCTION_TEMPLATE = """
+# public static void {fname}(String s, {farg} v) {{
+# try {{
+# System.out.printf("%s-{invoke_type:<9} {farg:>9}.{callfunc}()='%s'\\n", s, v.{callfunc}());
+# return;
+# }} catch (Error e) {{
+# System.out.printf("%s-{invoke_type} on {farg}: {callfunc}() threw exception!\\n", s);
+# e.printStackTrace(System.out);
+# }}
+# }}
+.method public static {fname}(Ljava/lang/String;L{farg};)V
+ .locals 7
+ :call_{fname}_try_start
+ const/4 v0, 2
+ new-array v1,v0, [Ljava/lang/Object;
+ const/4 v0, 0
+ aput-object p0,v1,v0
+
+ sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v3, "%s-{invoke_type:<9} {farg:>9}.{callfunc}()='%s'\\n"
+
+ invoke-{invoke_type} {{p1}}, L{farg};->{callfunc}()Ljava/lang/String;
+ move-result-object v4
+ const/4 v0, 1
+ aput-object v4, v1, v0
+
+ invoke-virtual {{v2,v3,v1}}, Ljava/io/PrintStream;->printf(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
+ return-void
+ :call_{fname}_try_end
+ .catch Ljava/lang/Error; {{:call_{fname}_try_start .. :call_{fname}_try_end}} :error_{fname}_start
+ :error_{fname}_start
+ move-exception v3
+ const/4 v0, 1
+ new-array v1,v0, [Ljava/lang/Object;
+ const/4 v0, 0
+ aput-object p0, v1, v0
+ sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v4, "%s-{invoke_type} on {farg}: {callfunc}() threw exception!\\n"
+ invoke-virtual {{v2,v4,v1}}, Ljava/io/PrintStream;->printf(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
+ invoke-virtual {{v3,v2}}, Ljava/lang/Error;->printStackTrace(Ljava/io/PrintStream;)V
+ return-void
+.end method
+"""
+
+ def __init__(self, func, farg, invoke):
+ """
+ Initialize this test function for the given invoke type and argument
+ """
+ self.func = func
+ self.farg = farg
+ self.invoke = invoke
+
+ def get_name(self):
+ """
+ Get the name of this test
+ """
+ return "Test_Func_{}_{}_{}".format(self.func, self.farg, self.invoke)
+
+ def __str__(self):
+ """
+ Get the smali code for this test function
+ """
+ return self.TEST_FUNCTION_TEMPLATE.format(fname=self.get_name(),
+ farg=self.farg,
+ invoke_type=self.invoke,
+ callfunc=self.func)
+
+def flatten_classes(classes, c):
+ """
+ Iterate over all the classes 'c' can be used as
+ """
+ while c:
+ yield c
+ c = classes.get(c.super_class)
+
+def flatten_class_methods(classes, c):
+ """
+ Iterate over all the methods 'c' can call
+ """
+ for c1 in flatten_classes(classes, c):
+ yield from c1.methods
+
+def flatten_interfaces(dat, c):
+ """
+ Iterate over all the interfaces 'c' transitively implements
+ """
+ def get_ifaces(cl):
+ for i2 in cl.implements:
+ yield dat.interfaces[i2]
+ yield from get_ifaces(dat.interfaces[i2])
+
+ for cl in flatten_classes(dat.classes, c):
+ yield from get_ifaces(cl)
+
+def flatten_interface_methods(dat, i):
+ """
+ Iterate over all the interface methods 'c' can call
+ """
+ yield from i.methods
+ for i2 in flatten_interfaces(dat, i):
+ yield from i2.methods
+
+def make_main_class(dat):
+ """
+ Creates a Main.smali file that runs all the tests
+ """
+ m = MainClass()
+ for c in dat.classes.values():
+ i = InstanceTest(m, c.name)
+ for clazz in flatten_classes(dat.classes, c):
+ for meth in flatten_class_methods(dat.classes, clazz):
+ i.add_func(Func(meth, clazz.name, 'virtual'))
+ for iface in flatten_interfaces(dat, clazz):
+ for meth in flatten_interface_methods(dat, iface):
+ i.add_func(Func(meth, clazz.name, 'virtual'))
+ i.add_func(Func(meth, iface.name, 'interface'))
+ return m
+
+class TestData(namedtuple("TestData", ['classes', 'interfaces'])):
+ """
+ A class representing the classes.xml document.
+ """
+ pass
+
+class Clazz(namedtuple("Clazz", ["name", "methods", "super_class", "implements"])):
+ """
+ A class representing a class element in the classes.xml document.
+ """
+ pass
+
+class IFace(namedtuple("IFace", ["name", "methods", "super_class", "implements"])):
+ """
+ A class representing an interface element in the classes.xml document.
+ """
+ pass
+
+def parse_xml(xml):
+ """
+ Parse the xml description of this test.
+ """
+ classes = dict()
+ ifaces = dict()
+ root = ET.fromstring(xml)
+ for iface in root.find("interfaces"):
+ name = iface.attrib['name']
+ implements = [a.text for a in iface.find("implements")]
+ methods = [a.text for a in iface.find("methods")]
+ ifaces[name] = IFace(name = name,
+ super_class = iface.attrib['super'],
+ methods = methods,
+ implements = implements)
+ for clazz in root.find('classes'):
+ name = clazz.attrib['name']
+ implements = [a.text for a in clazz.find("implements")]
+ methods = [a.text for a in clazz.find("methods")]
+ classes[name] = Clazz(name = name,
+ super_class = clazz.attrib['super'],
+ methods = methods,
+ implements = implements)
+ return TestData(classes, ifaces)
+
+def main(argv):
+ smali_dir = Path(argv[1])
+ if not smali_dir.exists() or not smali_dir.is_dir():
+ print("{} is not a valid smali dir".format(smali_dir), file=sys.stderr)
+ sys.exit(1)
+ class_data = parse_xml((smali_dir / "classes.xml").open().read())
+ make_main_class(class_data).dump(smali_dir)
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/test/961-default-iface-resolution-generated/build b/test/961-default-iface-resolution-generated/build
new file mode 100755
index 0000000..707c17e
--- /dev/null
+++ b/test/961-default-iface-resolution-generated/build
@@ -0,0 +1,47 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+# make us exit on a failure
+set -e
+
+mkdir -p ./smali
+
+# We will be making more files than the ulimit is set to allow. Remove it temporarily.
+OLD_ULIMIT=`ulimit -S`
+ulimit -S unlimited
+
+restore_ulimit() {
+ ulimit -S "$OLD_ULIMIT"
+}
+trap 'restore_ulimit' ERR
+
+# Generate the smali files and expected.txt or fail
+./util-src/generate_smali.py ./smali ./expected.txt
+
+if [[ $@ == *"--jvm"* ]]; then
+ # Build the Java files if we are running a --jvm test
+ mkdir -p src
+ mkdir -p classes
+ ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src
+ ${JAVAC} -implicit:none -d classes $(find src -name '*.java')
+fi
+
+# Build the smali files and make a dex
+${SMALI} -JXmx512m --experimental --api-level 23 --output classes.dex $(find smali -name '*.smali')
+zip $TEST_NAME.jar classes.dex
+
+# Reset the ulimit back to its initial value
+restore_ulimit
diff --git a/test/961-default-iface-resolution-generated/expected.txt b/test/961-default-iface-resolution-generated/expected.txt
new file mode 100644
index 0000000..1ddd65d
--- /dev/null
+++ b/test/961-default-iface-resolution-generated/expected.txt
@@ -0,0 +1 @@
+This file is generated by util-src/generate_smali.py do not directly modify!
diff --git a/test/961-default-iface-resolution-generated/info.txt b/test/961-default-iface-resolution-generated/info.txt
new file mode 100644
index 0000000..2cd2cc7
--- /dev/null
+++ b/test/961-default-iface-resolution-generated/info.txt
@@ -0,0 +1,17 @@
+Smali-based tests for experimental interface default methods.
+
+This tests that interface method resolution order is correct.
+
+Obviously needs to run under ART or a Java 8 Language runtime and compiler.
+
+When run smali test files are generated by the util-src/generate_smali.py
+script. If we run with --jvm we will use the
+$(ANDROID_BUILD_TOP)/art/tools/extract-embedded-java script to turn the smali
+into equivalent Java using the embedded Java code.
+
+Care should be taken when updating the generate_smali.py script. It should always
+return equivalent output when run multiple times and the expected output should
+be valid.
+
+Do not modify the expected.txt file. It is generated on each run by
+util-src/generate_smali.py.
diff --git a/test/961-default-iface-resolution-generated/run b/test/961-default-iface-resolution-generated/run
new file mode 100755
index 0000000..e378b06
--- /dev/null
+++ b/test/961-default-iface-resolution-generated/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+if echo $@ | grep -q -- "--jvm"; then
+ ${RUN} "$@"
+else
+ ${RUN} "$@" --runtime-option -Xexperimental:default-methods -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:default-methods
+fi
diff --git a/test/961-default-iface-resolution-generated/util-src/generate_smali.py b/test/961-default-iface-resolution-generated/util-src/generate_smali.py
new file mode 100755
index 0000000..921a096
--- /dev/null
+++ b/test/961-default-iface-resolution-generated/util-src/generate_smali.py
@@ -0,0 +1,466 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2015 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.
+
+"""
+Generate Smali test files for test 961.
+"""
+
+import os
+import sys
+from pathlib import Path
+
+BUILD_TOP = os.getenv("ANDROID_BUILD_TOP")
+if BUILD_TOP is None:
+ print("ANDROID_BUILD_TOP not set. Please run build/envsetup.sh", file=sys.stderr)
+ sys.exit(1)
+
+# Allow us to import utils and mixins.
+sys.path.append(str(Path(BUILD_TOP)/"art"/"test"/"utils"/"python"))
+
+from testgen.utils import get_copyright, subtree_sizes, gensym, filter_blanks
+import testgen.mixins as mixins
+
+from functools import total_ordering
+import itertools
+import string
+
+# The max depth the type tree can have. Includes the class object in the tree.
+# Increasing this increases the number of generated files significantly. This
+# value was chosen as it is fairly quick to run and very comprehensive, checking
+# every possible interface tree up to 5 layers deep.
+MAX_IFACE_DEPTH = 5
+
+class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin):
+ """
+ A Main.smali file containing the Main class and the main function. It will run
+ all the test functions we have.
+ """
+
+ MAIN_CLASS_TEMPLATE = """{copyright}
+
+.class public LMain;
+.super Ljava/lang/Object;
+
+# class Main {{
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {{p0}}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+{test_groups}
+
+{main_func}
+
+# }}
+"""
+
+ MAIN_FUNCTION_TEMPLATE = """
+# public static void main(String[] args) {{
+.method public static main([Ljava/lang/String;)V
+ .locals 2
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+ {test_group_invoke}
+
+ return-void
+.end method
+# }}
+"""
+
+ TEST_GROUP_INVOKE_TEMPLATE = """
+# {test_name}();
+ invoke-static {{}}, {test_name}()V
+"""
+
+ def __init__(self):
+ """
+ Initialize this MainClass. We start out with no tests.
+ """
+ self.tests = set()
+
+ def get_expected(self):
+ """
+ Get the expected output of this test.
+ """
+ all_tests = sorted(self.tests)
+ return filter_blanks("\n".join(a.get_expected() for a in all_tests))
+
+ def add_test(self, ty):
+ """
+ Add a test for the concrete type 'ty'
+ """
+ self.tests.add(Func(ty))
+
+ def get_name(self):
+ """
+ Get the name of this class
+ """
+ return "Main"
+
+ def __str__(self):
+ """
+ Print the MainClass smali code.
+ """
+ all_tests = sorted(self.tests)
+ test_invoke = ""
+ test_groups = ""
+ for t in all_tests:
+ test_groups += str(t)
+ for t in all_tests:
+ test_invoke += self.TEST_GROUP_INVOKE_TEMPLATE.format(test_name=t.get_name())
+ main_func = self.MAIN_FUNCTION_TEMPLATE.format(test_group_invoke=test_invoke)
+
+ return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright("smali"),
+ test_groups = test_groups,
+ main_func = main_func)
+
+class Func(mixins.Named, mixins.NameComparableMixin):
+ """
+ A function that tests the functionality of a concrete type. Should only be
+ constructed by MainClass.add_test.
+ """
+
+ TEST_FUNCTION_TEMPLATE = """
+# public static void {fname}() {{
+# try {{
+# {farg} v = new {farg}();
+# System.out.printf("%s calls default method on %s\\n",
+# v.CalledClassName(),
+# v.CalledInterfaceName());
+# return;
+# }} catch (Error e) {{
+# e.printStackTrace(System.out);
+# return;
+# }}
+# }}
+.method public static {fname}()V
+ .locals 7
+ :call_{fname}_try_start
+ new-instance v6, L{farg};
+ invoke-direct {{v6}}, L{farg};-><init>()V
+
+ const/4 v0, 2
+ new-array v1,v0, [Ljava/lang/Object;
+ const/4 v0, 0
+ invoke-virtual {{v6}}, L{farg};->CalledClassName()Ljava/lang/String;
+ move-result-object v4
+ aput-object v4,v1,v0
+
+ sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v3, "%s calls default method on %s\\n"
+
+ invoke-virtual {{v6}}, L{farg};->CalledInterfaceName()Ljava/lang/String;
+ move-result-object v4
+ const/4 v0, 1
+ aput-object v4, v1, v0
+
+ invoke-virtual {{v2,v3,v1}}, Ljava/io/PrintStream;->printf(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
+ return-void
+ :call_{fname}_try_end
+ .catch Ljava/lang/Error; {{:call_{fname}_try_start .. :call_{fname}_try_end}} :error_{fname}_start
+ :error_{fname}_start
+ move-exception v3
+ sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {{v3,v2}}, Ljava/lang/Error;->printStackTrace(Ljava/io/PrintStream;)V
+ return-void
+.end method
+"""
+
+ def __init__(self, farg):
+ """
+ Initialize a test function for the given argument
+ """
+ self.farg = farg
+
+ def get_expected(self):
+ """
+ Get the expected output calling this function.
+ """
+ return "{tree} calls default method on {iface_tree}".format(
+ tree = self.farg.get_tree(), iface_tree = self.farg.get_called().get_tree())
+
+ def get_name(self):
+ """
+ Get the name of this function
+ """
+ return "TEST_FUNC_{}".format(self.farg.get_name())
+
+ def __str__(self):
+ """
+ Print the smali code of this function.
+ """
+ return self.TEST_FUNCTION_TEMPLATE.format(fname=self.get_name(), farg=self.farg.get_name())
+
+class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.SmaliFileMixin):
+ """
+ A class that will be instantiated to test default method resolution order.
+ """
+
+ TEST_CLASS_TEMPLATE = """{copyright}
+
+.class public L{class_name};
+.super Ljava/lang/Object;
+.implements L{iface_name};
+
+# public class {class_name} implements {iface_name} {{
+# public String CalledClassName() {{
+# return "{tree}";
+# }}
+# }}
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {{p0}}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public CalledClassName()Ljava/lang/String;
+ .locals 1
+ const-string v0, "{tree}"
+ return-object v0
+.end method
+"""
+
+ def __init__(self, iface):
+ """
+ Initialize this test class which implements the given interface
+ """
+ self.iface = iface
+ self.class_name = "CLASS_"+gensym()
+
+ def get_name(self):
+ """
+ Get the name of this class
+ """
+ return self.class_name
+
+ def get_tree(self):
+ """
+ Print out a representation of the type tree of this class
+ """
+ return "[{class_name} {iface_tree}]".format(class_name = self.class_name,
+ iface_tree = self.iface.get_tree())
+
+ def __iter__(self):
+ """
+ Step through all interfaces implemented transitively by this class
+ """
+ yield self.iface
+ yield from self.iface
+
+ def get_called(self):
+ """
+ Get the interface whose default method would be called when calling the
+ CalledInterfaceName function.
+ """
+ all_ifaces = set(iface for iface in self if iface.default)
+ for i in all_ifaces:
+ if all(map(lambda j: i not in j.get_super_types(), all_ifaces)):
+ return i
+ raise Exception("UNREACHABLE! Unable to find default method!")
+
+ def __str__(self):
+ """
+ Print the smali code of this class.
+ """
+ return self.TEST_CLASS_TEMPLATE.format(copyright = get_copyright('smali'),
+ iface_name = self.iface.get_name(),
+ tree = self.get_tree(),
+ class_name = self.class_name)
+
+class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.SmaliFileMixin):
+ """
+ An interface that will be used to test default method resolution order.
+ """
+
+ TEST_INTERFACE_TEMPLATE = """{copyright}
+.class public abstract interface L{class_name};
+.super Ljava/lang/Object;
+{implements_spec}
+
+# public interface {class_name} {extends} {ifaces} {{
+# public String CalledClassName();
+.method public abstract CalledClassName()Ljava/lang/String;
+.end method
+
+{funcs}
+
+# }}
+"""
+
+ DEFAULT_FUNC_TEMPLATE = """
+# public default String CalledInterfaceName() {{
+# return "{tree}";
+# }}
+.method public CalledInterfaceName()Ljava/lang/String;
+ .locals 1
+ const-string v0, "{tree}"
+ return-object v0
+.end method
+"""
+
+ IMPLEMENTS_TEMPLATE = """
+.implements L{iface_name};
+"""
+
+ def __init__(self, ifaces, default):
+ """
+ Initialize interface with the given super-interfaces
+ """
+ self.ifaces = sorted(ifaces)
+ self.default = default
+ end = "_DEFAULT" if default else ""
+ self.class_name = "INTERFACE_"+gensym()+end
+
+ def get_super_types(self):
+ """
+ Returns a set of all the supertypes of this interface
+ """
+ return set(i2 for i2 in self)
+
+ def get_name(self):
+ """
+ Get the name of this class
+ """
+ return self.class_name
+
+ def get_tree(self):
+ """
+ Print out a representation of the type tree of this class
+ """
+ return "[{class_name} {iftree}]".format(class_name = self.get_name(),
+ iftree = print_tree(self.ifaces))
+
+ def __iter__(self):
+ """
+ Performs depth-first traversal of the interface tree this interface is the
+ root of. Does not filter out repeats.
+ """
+ for i in self.ifaces:
+ yield i
+ yield from i
+
+ def __str__(self):
+ """
+ Print the smali code of this interface.
+ """
+ s_ifaces = " "
+ j_ifaces = " "
+ for i in self.ifaces:
+ s_ifaces += self.IMPLEMENTS_TEMPLATE.format(iface_name = i.get_name())
+ j_ifaces += " {},".format(i.get_name())
+ j_ifaces = j_ifaces[0:-1]
+ if self.default:
+ funcs = self.DEFAULT_FUNC_TEMPLATE.format(ifaces = j_ifaces,
+ tree = self.get_tree(),
+ class_name = self.class_name)
+ else:
+ funcs = ""
+ return self.TEST_INTERFACE_TEMPLATE.format(copyright = get_copyright('smali'),
+ implements_spec = s_ifaces,
+ extends = "extends" if len(self.ifaces) else "",
+ ifaces = j_ifaces,
+ funcs = funcs,
+ tree = self.get_tree(),
+ class_name = self.class_name)
+
+def print_tree(ifaces):
+ """
+ Prints a list of iface trees
+ """
+ return " ".join(i.get_tree() for i in ifaces)
+
+# The deduplicated output of subtree_sizes for each size up to
+# MAX_LEAF_IFACE_PER_OBJECT.
+SUBTREES = [set(tuple(sorted(l)) for l in subtree_sizes(i))
+ for i in range(MAX_IFACE_DEPTH + 1)]
+
+def create_interface_trees():
+ """
+ Return all legal interface trees
+ """
+ def dump_supers(s):
+ """
+ Does depth first traversal of all the interfaces in the list.
+ """
+ for i in s:
+ yield i
+ yield from i
+
+ def create_interface_trees_inner(num, allow_default):
+ for split in SUBTREES[num]:
+ ifaces = []
+ for sub in split:
+ if sub == 1:
+ ifaces.append([TestInterface([], allow_default)])
+ if allow_default:
+ ifaces[-1].append(TestInterface([], False))
+ else:
+ ifaces.append(list(create_interface_trees_inner(sub, allow_default)))
+ for supers in itertools.product(*ifaces):
+ all_supers = sorted(set(dump_supers(supers)) - set(supers))
+ for i in range(len(all_supers) + 1):
+ for combo in itertools.combinations(all_supers, i):
+ yield TestInterface(list(combo) + list(supers), allow_default)
+ if allow_default:
+ for i in range(len(split)):
+ ifaces = []
+ for sub, cs in zip(split, itertools.count()):
+ if sub == 1:
+ ifaces.append([TestInterface([], i == cs)])
+ else:
+ ifaces.append(list(create_interface_trees_inner(sub, i == cs)))
+ for supers in itertools.product(*ifaces):
+ all_supers = sorted(set(dump_supers(supers)) - set(supers))
+ for i in range(len(all_supers) + 1):
+ for combo in itertools.combinations(all_supers, i):
+ yield TestInterface(list(combo) + list(supers), False)
+
+ for num in range(1, MAX_IFACE_DEPTH):
+ yield from create_interface_trees_inner(num, True)
+
+def create_all_test_files():
+ """
+ Creates all the objects representing the files in this test. They just need to
+ be dumped.
+ """
+ mc = MainClass()
+ classes = {mc}
+ for tree in create_interface_trees():
+ classes.add(tree)
+ for i in tree:
+ classes.add(i)
+ test_class = TestClass(tree)
+ mc.add_test(test_class)
+ classes.add(test_class)
+ return mc, classes
+
+def main(argv):
+ smali_dir = Path(argv[1])
+ if not smali_dir.exists() or not smali_dir.is_dir():
+ print("{} is not a valid smali dir".format(smali_dir), file=sys.stderr)
+ sys.exit(1)
+ expected_txt = Path(argv[2])
+ mainclass, all_files = create_all_test_files()
+ with expected_txt.open('w') as out:
+ print(mainclass.get_expected(), file=out)
+ for f in all_files:
+ f.dump(smali_dir)
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/test/962-iface-static/build b/test/962-iface-static/build
new file mode 100755
index 0000000..5ad82f7
--- /dev/null
+++ b/test/962-iface-static/build
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ == *"--jvm"* ]]; then
+ # Build the Java files if we are running a --jvm test
+ mkdir -p src
+ mkdir -p classes
+ ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src
+ ${JAVAC} -implicit:none -d classes $(find src -name '*.java')
+fi
+
+# Build the smali files and make a dex
+${SMALI} -JXmx512m --experimental --api-level 23 --output classes.dex $(find smali -name '*.smali')
+zip $TEST_NAME.jar classes.dex
diff --git a/test/962-iface-static/expected.txt b/test/962-iface-static/expected.txt
new file mode 100644
index 0000000..6d98ea1
--- /dev/null
+++ b/test/962-iface-static/expected.txt
@@ -0,0 +1,3 @@
+init
+constructor
+Hello
diff --git a/test/962-iface-static/info.txt b/test/962-iface-static/info.txt
new file mode 100644
index 0000000..d4732e5
--- /dev/null
+++ b/test/962-iface-static/info.txt
@@ -0,0 +1,4 @@
+Smali-based tests for experimental interface static methods.
+
+To run with --jvm you must export JAVA_HOME to a Java 8 Language installation
+and pass the --use-java-home to run-test
diff --git a/test/962-iface-static/run b/test/962-iface-static/run
new file mode 100755
index 0000000..e713708
--- /dev/null
+++ b/test/962-iface-static/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 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.
+
+if echo $@ | grep -q -- "--jvm"; then
+ ${RUN} "$@"
+else
+ ${RUN} "$@" --runtime-option -Xexperimental:default-methods -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:default-methods
+fi
diff --git a/test/962-iface-static/smali/Displayer.smali b/test/962-iface-static/smali/Displayer.smali
new file mode 100644
index 0000000..06bec16
--- /dev/null
+++ b/test/962-iface-static/smali/Displayer.smali
@@ -0,0 +1,45 @@
+# /*
+# * Copyright (C) 2015 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.
+# */
+#
+# public class Displayer {
+# static {
+# System.out.println("init");
+# }
+#
+# public Displayer() {
+# System.out.println("constructor");
+# }
+# }
+
+.class public LDisplayer;
+.super Ljava/lang/Object;
+
+.method public static <clinit>()V
+ .locals 3
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v0, "init"
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
+.method public constructor <init>()V
+ .locals 2
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v0, "constructor"
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
diff --git a/test/962-iface-static/smali/Main.smali b/test/962-iface-static/smali/Main.smali
new file mode 100644
index 0000000..72fa5e0
--- /dev/null
+++ b/test/962-iface-static/smali/Main.smali
@@ -0,0 +1,40 @@
+# /*
+# * Copyright (C) 2015 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+#
+# class Main {
+# public static void main(String[] args) {
+# System.out.println(iface.SayHi());
+# }
+# }
+.class public LMain;
+.super Ljava/lang/Object;
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .locals 2
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+ invoke-static {}, Liface;->SayHi()Ljava/lang/String;
+ move-result-object v0
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+ return-void
+.end method
diff --git a/test/962-iface-static/smali/iface.smali b/test/962-iface-static/smali/iface.smali
new file mode 100644
index 0000000..441aae6
--- /dev/null
+++ b/test/962-iface-static/smali/iface.smali
@@ -0,0 +1,43 @@
+# /*
+# * Copyright (C) 2015 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.
+# */
+#
+# public interface iface {
+# public static final Displayer f = new Displayer();
+#
+# public static String SayHi() {
+# return "Hello";
+# }
+# }
+
+.class public abstract interface Liface;
+.super Ljava/lang/Object;
+
+.field public final static f:LDisplayer;
+
+.method public static <clinit>()V
+ .locals 3
+ new-instance v1, LDisplayer;
+ invoke-direct {v1}, LDisplayer;-><init>()V
+ sput-object v1, Liface;->f:LDisplayer;
+ return-void
+.end method
+
+.method public static SayHi()Ljava/lang/String;
+ .locals 1
+ const-string v0, "Hello"
+ return-object v0
+.end method
+
diff --git a/test/963-default-range-smali/build b/test/963-default-range-smali/build
new file mode 100755
index 0000000..5ad82f7
--- /dev/null
+++ b/test/963-default-range-smali/build
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ == *"--jvm"* ]]; then
+ # Build the Java files if we are running a --jvm test
+ mkdir -p src
+ mkdir -p classes
+ ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src
+ ${JAVAC} -implicit:none -d classes $(find src -name '*.java')
+fi
+
+# Build the smali files and make a dex
+${SMALI} -JXmx512m --experimental --api-level 23 --output classes.dex $(find smali -name '*.smali')
+zip $TEST_NAME.jar classes.dex
diff --git a/test/963-default-range-smali/expected.txt b/test/963-default-range-smali/expected.txt
new file mode 100644
index 0000000..af17d2f
--- /dev/null
+++ b/test/963-default-range-smali/expected.txt
@@ -0,0 +1,2 @@
+Hello
+Hello
diff --git a/test/963-default-range-smali/info.txt b/test/963-default-range-smali/info.txt
new file mode 100644
index 0000000..d4732e5
--- /dev/null
+++ b/test/963-default-range-smali/info.txt
@@ -0,0 +1,4 @@
+Smali-based tests for experimental interface static methods.
+
+To run with --jvm you must export JAVA_HOME to a Java 8 Language installation
+and pass the --use-java-home to run-test
diff --git a/test/963-default-range-smali/run b/test/963-default-range-smali/run
new file mode 100755
index 0000000..e713708
--- /dev/null
+++ b/test/963-default-range-smali/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 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.
+
+if echo $@ | grep -q -- "--jvm"; then
+ ${RUN} "$@"
+else
+ ${RUN} "$@" --runtime-option -Xexperimental:default-methods -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:default-methods
+fi
diff --git a/test/963-default-range-smali/smali/A.smali b/test/963-default-range-smali/smali/A.smali
new file mode 100644
index 0000000..b3d91dd
--- /dev/null
+++ b/test/963-default-range-smali/smali/A.smali
@@ -0,0 +1,29 @@
+# /*
+# * Copyright 2015 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LA;
+.super Ljava/lang/Object;
+.implements Liface;
+
+# class A implements iface {
+# }
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
diff --git a/test/963-default-range-smali/smali/Main.smali b/test/963-default-range-smali/smali/Main.smali
new file mode 100644
index 0000000..400fba7
--- /dev/null
+++ b/test/963-default-range-smali/smali/Main.smali
@@ -0,0 +1,77 @@
+# /*
+# * Copyright (C) 2015 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+#
+# class Main {
+# public static void main(String[] args) {
+# A a = new A();
+# System.out.println(a.SayHi("a string 0",
+# "a string 1",
+# "a string 2",
+# "a string 3",
+# "a string 4",
+# "a string 5",
+# "a string 6",
+# "a string 7",
+# "a string 8",
+# "a string 9"));
+# iface b = (iface)a;
+# System.out.println(b.SayHi("a string 0",
+# "a string 1",
+# "a string 2",
+# "a string 3",
+# "a string 4",
+# "a string 5",
+# "a string 6",
+# "a string 7",
+# "a string 8",
+# "a string 9"));
+# }
+# }
+.class public LMain;
+.super Ljava/lang/Object;
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .locals 15
+ sget-object v12, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+ new-instance v1, LA;
+ invoke-direct {v1}, LA;-><init>()V
+ const-string v2, "a string 0"
+ const-string v3, "a string 1"
+ const-string v4, "a string 2"
+ const-string v5, "a string 3"
+ const-string v6, "a string 4"
+ const-string v7, "a string 5"
+ const-string v8, "a string 6"
+ const-string v9, "a string 7"
+ const-string v10, "a string 8"
+ const-string v11, "a string 9"
+ invoke-virtual/range {v1 .. v11}, LA;->SayHi(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+ move-result-object v0
+ invoke-virtual {v12,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+ invoke-interface/range {v1 .. v11}, Liface;->SayHi(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+ move-result-object v0
+ invoke-virtual {v12,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+ return-void
+.end method
diff --git a/test/963-default-range-smali/smali/iface.smali b/test/963-default-range-smali/smali/iface.smali
new file mode 100644
index 0000000..c2c3ce6
--- /dev/null
+++ b/test/963-default-range-smali/smali/iface.smali
@@ -0,0 +1,40 @@
+# /*
+# * Copyright (C) 2015 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.
+# */
+#
+# public interface iface {
+# public default String SayHi(String n1,
+# String n2,
+# String n3,
+# String n4,
+# String n5,
+# String n6,
+# String n7,
+# String n8,
+# String n9,
+# String n0) {
+# return "Hello";
+# }
+# }
+
+.class public abstract interface Liface;
+.super Ljava/lang/Object;
+
+.method public SayHi(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+ .locals 1
+ const-string v0, "Hello"
+ return-object v0
+.end method
+
diff --git a/test/964-default-iface-init-generated/build b/test/964-default-iface-init-generated/build
new file mode 100755
index 0000000..deef803
--- /dev/null
+++ b/test/964-default-iface-init-generated/build
@@ -0,0 +1,45 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+# make us exit on a failure
+set -e
+
+# We will be making more files than the ulimit is set to allow. Remove it temporarily.
+OLD_ULIMIT=`ulimit -S`
+ulimit -S unlimited
+
+restore_ulimit() {
+ ulimit -S "$OLD_ULIMIT"
+}
+trap 'restore_ulimit' ERR
+
+# Generate the smali files and expected.txt or fail
+./util-src/generate_smali.py ./smali ./expected.txt
+
+if [[ $@ == *"--jvm"* ]]; then
+ # Build the Java files if we are running a --jvm test
+ mkdir -p src
+ mkdir -p classes
+ ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src
+ ${JAVAC} -implicit:none -d classes $(find src -name '*.java')
+fi
+
+# Build the smali files and make a dex
+${SMALI} -JXmx512m --experimental --api-level 23 --output classes.dex $(find smali -name '*.smali')
+zip $TEST_NAME.jar classes.dex
+
+# Reset the ulimit back to its initial value
+restore_ulimit
diff --git a/test/964-default-iface-init-generated/expected.txt b/test/964-default-iface-init-generated/expected.txt
new file mode 100644
index 0000000..1ddd65d
--- /dev/null
+++ b/test/964-default-iface-init-generated/expected.txt
@@ -0,0 +1 @@
+This file is generated by util-src/generate_smali.py do not directly modify!
diff --git a/test/964-default-iface-init-generated/info.txt b/test/964-default-iface-init-generated/info.txt
new file mode 100644
index 0000000..5805a86
--- /dev/null
+++ b/test/964-default-iface-init-generated/info.txt
@@ -0,0 +1,17 @@
+Smali-based tests for interface initialization.
+
+This tests that interface initialization order is correct.
+
+Obviously needs to run under ART or a Java 8 Language runtime and compiler.
+
+When run smali test files are generated by the util-src/generate_smali.py
+script. If we run with --jvm we will use the
+$(ANDROID_BUILD_TOP)/art/tools/extract-embedded-java script to turn the smali
+into equivalent Java using the embedded Java code.
+
+Care should be taken when updating the generate_smali.py script. It should always
+return equivalent output when run multiple times and the expected output should
+be valid.
+
+Do not modify the expected.txt file. It is generated on each run by
+util-src/generate_smali.py.
diff --git a/test/964-default-iface-init-generated/run b/test/964-default-iface-init-generated/run
new file mode 100755
index 0000000..e378b06
--- /dev/null
+++ b/test/964-default-iface-init-generated/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+if echo $@ | grep -q -- "--jvm"; then
+ ${RUN} "$@"
+else
+ ${RUN} "$@" --runtime-option -Xexperimental:default-methods -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:default-methods
+fi
diff --git a/test/964-default-iface-init-generated/smali/Displayer.smali b/test/964-default-iface-init-generated/smali/Displayer.smali
new file mode 100644
index 0000000..91280a8
--- /dev/null
+++ b/test/964-default-iface-init-generated/smali/Displayer.smali
@@ -0,0 +1,45 @@
+# /*
+# * Copyright (C) 2015 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.
+# */
+#
+# // This class is b/c java does not allow static {} blocks in interfaces.
+# public class Displayer {
+# public Displayer(String type) {
+# System.out.println("initialization of " + type);
+# }
+# public void touch() {
+# return;
+# }
+# }
+
+.class public LDisplayer;
+.super Ljava/lang/Object;
+
+.method public constructor <init>(Ljava/lang/String;)V
+ .locals 2
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ const-string v0, "initialization of "
+ invoke-virtual {v0, p1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String;
+ move-result-object v0
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
+.method public touch()V
+ .locals 0
+ return-void
+.end method
+
diff --git a/test/964-default-iface-init-generated/util-src/generate_smali.py b/test/964-default-iface-init-generated/util-src/generate_smali.py
new file mode 100755
index 0000000..be2d3ba
--- /dev/null
+++ b/test/964-default-iface-init-generated/util-src/generate_smali.py
@@ -0,0 +1,531 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2015 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.
+
+"""
+Generate Smali test files for test 964.
+"""
+
+import os
+import sys
+from pathlib import Path
+
+BUILD_TOP = os.getenv("ANDROID_BUILD_TOP")
+if BUILD_TOP is None:
+ print("ANDROID_BUILD_TOP not set. Please run build/envsetup.sh", file=sys.stderr)
+ sys.exit(1)
+
+# Allow us to import utils and mixins.
+sys.path.append(str(Path(BUILD_TOP)/"art"/"test"/"utils"/"python"))
+
+from testgen.utils import get_copyright, subtree_sizes, gensym, filter_blanks
+import testgen.mixins as mixins
+
+from functools import total_ordering
+import itertools
+import string
+
+# The max depth the tree can have.
+MAX_IFACE_DEPTH = 3
+
+class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin):
+ """
+ A Main.smali file containing the Main class and the main function. It will run
+ all the test functions we have.
+ """
+
+ MAIN_CLASS_TEMPLATE = """{copyright}
+
+.class public LMain;
+.super Ljava/lang/Object;
+
+# class Main {{
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {{p0}}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+{test_groups}
+
+{main_func}
+
+# }}
+"""
+
+ MAIN_FUNCTION_TEMPLATE = """
+# public static void main(String[] args) {{
+.method public static main([Ljava/lang/String;)V
+ .locals 2
+
+ {test_group_invoke}
+
+ return-void
+.end method
+# }}
+"""
+
+ TEST_GROUP_INVOKE_TEMPLATE = """
+# {test_name}();
+ invoke-static {{}}, {test_name}()V
+"""
+
+ def __init__(self):
+ """
+ Initialize this MainClass. We start out with no tests.
+ """
+ self.tests = set()
+
+ def add_test(self, ty):
+ """
+ Add a test for the concrete type 'ty'
+ """
+ self.tests.add(Func(ty))
+
+ def get_expected(self):
+ """
+ Get the expected output of this test.
+ """
+ all_tests = sorted(self.tests)
+ return filter_blanks("\n".join(a.get_expected() for a in all_tests))
+
+ def get_name(self):
+ """
+ Gets the name of this class
+ """
+ return "Main"
+
+ def __str__(self):
+ """
+ Print the smali code for this test.
+ """
+ all_tests = sorted(self.tests)
+ test_invoke = ""
+ test_groups = ""
+ for t in all_tests:
+ test_groups += str(t)
+ for t in all_tests:
+ test_invoke += self.TEST_GROUP_INVOKE_TEMPLATE.format(test_name=t.get_name())
+ main_func = self.MAIN_FUNCTION_TEMPLATE.format(test_group_invoke=test_invoke)
+
+ return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright('smali'),
+ test_groups = test_groups,
+ main_func = main_func)
+
+class Func(mixins.Named, mixins.NameComparableMixin):
+ """
+ A function that tests the functionality of a concrete type. Should only be
+ constructed by MainClass.add_test.
+ """
+
+ TEST_FUNCTION_TEMPLATE = """
+# public static void {fname}() {{
+# try {{
+# System.out.println("About to initialize {tree}");
+# {farg} v = new {farg}();
+# System.out.println("Initialized {tree}");
+# v.touchAll();
+# System.out.println("All of {tree} hierarchy initialized");
+# return;
+# }} catch (Error e) {{
+# e.printStackTrace(System.out);
+# return;
+# }}
+# }}
+.method public static {fname}()V
+ .locals 7
+ :call_{fname}_try_start
+ sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v3, "About to initialize {tree}"
+ invoke-virtual {{v2, v3}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+ new-instance v6, L{farg};
+ invoke-direct {{v6}}, L{farg};-><init>()V
+
+ const-string v3, "Initialized {tree}"
+ invoke-virtual {{v2, v3}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+ invoke-virtual {{v6}}, L{farg};->touchAll()V
+
+ const-string v3, "All of {tree} hierarchy initialized"
+ invoke-virtual {{v2, v3}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+ return-void
+ :call_{fname}_try_end
+ .catch Ljava/lang/Error; {{:call_{fname}_try_start .. :call_{fname}_try_end}} :error_{fname}_start
+ :error_{fname}_start
+ move-exception v3
+ sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {{v3,v2}}, Ljava/lang/Error;->printStackTrace(Ljava/io/PrintStream;)V
+ return-void
+.end method
+"""
+
+ OUTPUT_FORMAT = """
+About to initialize {tree}
+{initialize_output}
+Initialized {tree}
+{touch_output}
+All of {tree} hierarchy initialized
+""".strip()
+
+ def __init__(self, farg):
+ """
+ Initialize a test function for the given argument
+ """
+ self.farg = farg
+
+ def __str__(self):
+ """
+ Print the smali code for this test function.
+ """
+ return self.TEST_FUNCTION_TEMPLATE.format(fname=self.get_name(),
+ farg=self.farg.get_name(),
+ tree = self.farg.get_tree())
+
+ def get_name(self):
+ """
+ Gets the name of this test function
+ """
+ return "TEST_FUNC_{}".format(self.farg.get_name())
+
+ def get_expected(self):
+ """
+ Get the expected output of this function.
+ """
+ return self.OUTPUT_FORMAT.format(
+ tree = self.farg.get_tree(),
+ initialize_output = self.farg.get_initialize_output().strip(),
+ touch_output = self.farg.get_touch_output().strip())
+
+class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.SmaliFileMixin):
+ """
+ A class that will be instantiated to test interface initialization order.
+ """
+
+ TEST_CLASS_TEMPLATE = """{copyright}
+
+.class public L{class_name};
+.super Ljava/lang/Object;
+{implements_spec}
+
+# public class {class_name} implements {ifaces} {{
+#
+# public {class_name}() {{
+# }}
+.method public constructor <init>()V
+ .locals 2
+ invoke-direct {{p0}}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+# public void marker() {{
+# return;
+# }}
+.method public marker()V
+ .locals 0
+ return-void
+.end method
+
+# public void touchAll() {{
+.method public touchAll()V
+ .locals 2
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ {touch_calls}
+ return-void
+.end method
+# }}
+# }}
+"""
+
+ IMPLEMENTS_TEMPLATE = """
+.implements L{iface_name};
+"""
+
+ TOUCH_CALL_TEMPLATE = """
+# System.out.println("{class_name} touching {iface_name}");
+# {iface_name}.field.touch();
+ const-string v1, "{class_name} touching {iface_name}"
+ invoke-virtual {{v0, v1}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ sget-object v1, L{iface_name};->field:LDisplayer;
+ invoke-virtual {{v1}}, LDisplayer;->touch()V
+"""
+
+ TOUCH_OUTPUT_TEMPLATE = """
+{class_name} touching {iface_name}
+{touch_output}
+""".strip()
+
+ def __init__(self, ifaces):
+ """
+ Initialize this test class which implements the given interfaces
+ """
+ self.ifaces = ifaces
+ self.class_name = "CLASS_"+gensym()
+
+ def get_name(self):
+ """
+ Gets the name of this interface
+ """
+ return self.class_name
+
+ def get_tree(self):
+ """
+ Print out a representation of the type tree of this class
+ """
+ return "[{fname} {iftree}]".format(fname = self.get_name(), iftree = print_tree(self.ifaces))
+
+ def get_initialize_output(self):
+ return "\n".join(map(lambda i: i.get_initialize_output().strip(), dump_tree(self.ifaces)))
+
+ def get_touch_output(self):
+ return "\n".join(map(lambda a: self.TOUCH_OUTPUT_TEMPLATE.format(
+ class_name = self.class_name,
+ iface_name = a.get_name(),
+ touch_output = a.get_touch_output()).strip(),
+ self.get_all_interfaces()))
+
+ def get_all_interfaces(self):
+ """
+ Returns a set of all interfaces this class transitively implements
+ """
+ return sorted(set(dump_tree(self.ifaces)))
+
+ def __str__(self):
+ """
+ Print the smali code for this class.
+ """
+ s_ifaces = '\n'.join(map(lambda a: self.IMPLEMENTS_TEMPLATE.format(iface_name = a.get_name()),
+ self.ifaces))
+ j_ifaces = ', '.join(map(lambda a: a.get_name(), self.ifaces))
+ touches = '\n'.join(map(lambda a: self.TOUCH_CALL_TEMPLATE.format(class_name = self.class_name,
+ iface_name = a.get_name()),
+ self.get_all_interfaces()))
+ return self.TEST_CLASS_TEMPLATE.format(copyright = get_copyright('smali'),
+ implements_spec = s_ifaces,
+ ifaces = j_ifaces,
+ class_name = self.class_name,
+ touch_calls = touches)
+
+class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.SmaliFileMixin):
+ """
+ An interface that will be used to test default method resolution order.
+ """
+
+ TEST_INTERFACE_TEMPLATE = """{copyright}
+.class public abstract interface L{class_name};
+.super Ljava/lang/Object;
+{implements_spec}
+
+# public interface {class_name} {extends} {ifaces} {{
+# public static final Displayer field = new Displayer("{tree}");
+.field public final static field:LDisplayer;
+
+.method public static constructor <clinit>()V
+ .locals 3
+ const-string v2, "{tree}"
+ new-instance v1, LDisplayer;
+ invoke-direct {{v1, v2}}, LDisplayer;-><init>(Ljava/lang/String;)V
+ sput-object v1, L{class_name};->field:LDisplayer;
+ return-void
+.end method
+
+# public void marker();
+.method public abstract marker()V
+.end method
+
+{funcs}
+
+# }}
+"""
+
+ DEFAULT_FUNC_TEMPLATE = """
+# public default void {class_name}_DEFAULT_FUNC() {{
+# return;
+# }}
+.method public {class_name}_DEFAULT_FUNC()V
+ .locals 0
+ return-void
+.end method
+"""
+ IMPLEMENTS_TEMPLATE = """
+.implements L{iface_name};
+"""
+
+ OUTPUT_TEMPLATE = "initialization of {tree}"
+
+ def __init__(self, ifaces, default):
+ """
+ Initialize interface with the given super-interfaces
+ """
+ self.ifaces = ifaces
+ self.default = default
+ end = "_DEFAULT" if default else ""
+ self.class_name = "INTERFACE_"+gensym()+end
+ self.cloned = False
+ self.initialized = False
+
+ def clone(self):
+ """
+ Clones this interface, returning a new one with the same structure but
+ different name.
+ """
+ return TestInterface(tuple(map(lambda a: a.clone(), self.ifaces)), self.default)
+
+ def get_name(self):
+ """
+ Gets the name of this interface
+ """
+ return self.class_name
+
+ def __iter__(self):
+ """
+ Performs depth-first traversal of the interface tree this interface is the
+ root of. Does not filter out repeats.
+ """
+ for i in self.ifaces:
+ yield i
+ yield from i
+
+ def get_tree(self):
+ """
+ Print out a representation of the type tree of this class
+ """
+ return "[{class_name} {iftree}]".format(class_name = self.get_name(),
+ iftree = print_tree(self.ifaces))
+
+ def get_initialize_output(self):
+ """
+ Returns the expected output upon the class that implements this interface being initialized.
+ """
+ if self.default and not self.initialized:
+ self.initialized = True
+ return self.OUTPUT_TEMPLATE.format(tree = self.get_tree())
+ else:
+ return ""
+
+ def get_touch_output(self):
+ """
+ Returns the expected output upon this interface being touched.
+ """
+ if not self.default and not self.initialized:
+ self.initialized = True
+ return self.OUTPUT_TEMPLATE.format(tree = self.get_tree())
+ else:
+ return ""
+
+ def __str__(self):
+ """
+ Print the smali code for this interface.
+ """
+ s_ifaces = '\n'.join(map(lambda a: self.IMPLEMENTS_TEMPLATE.format(iface_name = a.get_name()),
+ self.ifaces))
+ j_ifaces = ', '.join(map(lambda a: a.get_name(), self.ifaces))
+ if self.default:
+ funcs = self.DEFAULT_FUNC_TEMPLATE.format(class_name = self.class_name)
+ else:
+ funcs = ""
+ return self.TEST_INTERFACE_TEMPLATE.format(copyright = get_copyright('smali'),
+ implements_spec = s_ifaces,
+ extends = "extends" if len(self.ifaces) else "",
+ ifaces = j_ifaces,
+ funcs = funcs,
+ tree = self.get_tree(),
+ class_name = self.class_name)
+
+def dump_tree(ifaces):
+ """
+ Yields all the interfaces transitively implemented by the set in
+ reverse-depth-first order
+ """
+ for i in ifaces:
+ yield from dump_tree(i.ifaces)
+ yield i
+
+def print_tree(ifaces):
+ """
+ Prints the tree for the given ifaces.
+ """
+ return " ".join(i.get_tree() for i in ifaces)
+
+def clone_all(l):
+ return tuple(a.clone() for a in l)
+
+# Cached output of subtree_sizes for speed of access.
+SUBTREES = [set(tuple(l) for l in subtree_sizes(i))
+ for i in range(MAX_IFACE_DEPTH + 1)]
+
+def create_test_classes():
+ """
+ Yield all the test classes with the different interface trees
+ """
+ for num in range(1, MAX_IFACE_DEPTH + 1):
+ for split in SUBTREES[num]:
+ ifaces = []
+ for sub in split:
+ ifaces.append(list(create_interface_trees(sub)))
+ for supers in itertools.product(*ifaces):
+ yield TestClass(clone_all(supers))
+ for i in range(len(set(dump_tree(supers)) - set(supers))):
+ ns = clone_all(supers)
+ selected = sorted(set(dump_tree(ns)) - set(ns))[i]
+ yield TestClass(tuple([selected] + list(ns)))
+
+def create_interface_trees(num):
+ """
+ Yield all the interface trees up to 'num' depth.
+ """
+ if num == 0:
+ yield TestInterface(tuple(), False)
+ yield TestInterface(tuple(), True)
+ return
+ for split in SUBTREES[num]:
+ ifaces = []
+ for sub in split:
+ ifaces.append(list(create_interface_trees(sub)))
+ for supers in itertools.product(*ifaces):
+ yield TestInterface(clone_all(supers), False)
+ yield TestInterface(clone_all(supers), True)
+ # TODO Should add on some from higher up the tree.
+
+def create_all_test_files():
+ """
+ Creates all the objects representing the files in this test. They just need to
+ be dumped.
+ """
+ mc = MainClass()
+ classes = {mc}
+ for clazz in create_test_classes():
+ classes.add(clazz)
+ for i in dump_tree(clazz.ifaces):
+ classes.add(i)
+ mc.add_test(clazz)
+ return mc, classes
+
+def main(argv):
+ smali_dir = Path(argv[1])
+ if not smali_dir.exists() or not smali_dir.is_dir():
+ print("{} is not a valid smali dir".format(smali_dir), file=sys.stderr)
+ sys.exit(1)
+ expected_txt = Path(argv[2])
+ mainclass, all_files = create_all_test_files()
+ with expected_txt.open('w') as out:
+ print(mainclass.get_expected(), file=out)
+ for f in all_files:
+ f.dump(smali_dir)
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 537873f..ad64b68 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -214,11 +214,22 @@
055-enum-performance \
133-static-invoke-super
- # disable timing sensitive tests on "dist" builds.
+# Tests that require python3.
+TEST_ART_PYTHON3_DEPENDENCY_RUN_TESTS := \
+ 960-default-smali \
+ 961-default-iface-resolution-generated \
+ 964-default-iface-init-generated \
+
+# disable timing sensitive tests on "dist" builds.
ifdef dist_goal
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
$(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
$(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(ALL_ADDRESS_SIZES))
+
+ # Currently disable tsts requiring python3.
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(TEST_ART_PYTHON3_DEPENDENCY_RUN_TESTS), $(ALL_ADDRESS_SIZES))
endif
TEST_ART_TIMING_SENSITIVE_RUN_TESTS :=
diff --git a/test/etc/default-build b/test/etc/default-build
index c281bca..c92402b 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -26,6 +26,8 @@
option="$1"
DX_FLAGS="${DX_FLAGS} $option"
shift
+ elif [ "x$1" = "x--jvm" ]; then
+ shift
elif expr "x$1" : "x--" >/dev/null 2>&1; then
echo "unknown $0 option: $1" 1>&2
exit 1
diff --git a/test/run-all-tests b/test/run-all-tests
index 13490c4..76283b7 100755
--- a/test/run-all-tests
+++ b/test/run-all-tests
@@ -41,6 +41,9 @@
if [ "x$1" = "x--host" ]; then
run_args="${run_args} --host"
shift
+ elif [ "x$1" = "x--use-java-home" ]; then
+ run_args="${run_args} --use-java-home"
+ shift
elif [ "x$1" = "x--jvm" ]; then
run_args="${run_args} --jvm"
shift
@@ -133,7 +136,7 @@
echo " --debug --dev --host --interpreter --jit --jvm --no-optimize"
echo " --no-verify -O --update --valgrind --zygote --64 --relocate"
echo " --prebuild --always-clean --gcstress --gcverify --trace"
- echo " --no-patchoat --no-dex2oat"
+ echo " --no-patchoat --no-dex2oat --use-java-home"
echo " Specific Runtime Options:"
echo " --seq Run tests one-by-one, avoiding failures caused by busy CPU"
) 1>&2
diff --git a/test/run-test b/test/run-test
index 2892ce9..1b71f33 100755
--- a/test/run-test
+++ b/test/run-test
@@ -40,7 +40,6 @@
tmp_dir="${TMPDIR}/$USER/${test_dir}"
fi
checker="${progdir}/../tools/checker/checker.py"
-
export JAVA="java"
export JAVAC="javac -g"
export RUN="${progdir}/etc/run-test-jar"
@@ -155,6 +154,15 @@
DEX_LOCATION=$tmp_dir
run_args="${run_args} --host"
shift
+ elif [ "x$1" = "x--use-java-home" ]; then
+ if [ -n "${JAVA_HOME}" ]; then
+ export JAVA="${JAVA_HOME}/bin/java"
+ export JAVAC="${JAVA_HOME}/bin/javac -g"
+ else
+ echo "Passed --use-java-home without JAVA_HOME variable set!"
+ usage="yes"
+ fi
+ shift
elif [ "x$1" = "x--jvm" ]; then
target_mode="no"
runtime="jvm"
@@ -162,6 +170,7 @@
NEED_DEX="false"
USE_JACK="false"
run_args="${run_args} --jvm"
+ build_args="${build_args} --jvm"
shift
elif [ "x$1" = "x-O" ]; then
lib="libart.so"
@@ -560,6 +569,9 @@
echo " --invoke-with Pass --invoke-with option to runtime."
echo " --dalvik Use Dalvik (off by default)."
echo " --jvm Use a host-local RI virtual machine."
+ echo " --use-java-home Use the JAVA_HOME environment variable"
+ echo " to find the java compiler and runtime"
+ echo " (if applicable) to run the test with."
echo " --output-path [path] Location where to store the build" \
"files."
echo " --64 Run the test in 64-bit mode"
diff --git a/test/utils/python/testgen/mixins.py b/test/utils/python/testgen/mixins.py
new file mode 100644
index 0000000..085e51d
--- /dev/null
+++ b/test/utils/python/testgen/mixins.py
@@ -0,0 +1,135 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2015 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.
+
+"""
+Common mixins and abstract base classes (ABCs) useful for writing test generators in python
+"""
+
+import abc
+import collections.abc
+import functools
+
+class Named(metaclass=abc.ABCMeta):
+ """
+ An abc that defines a get_name method.
+ """
+
+ @abc.abstractmethod
+ def get_name(self):
+ """
+ Returns a unique name to use as the identity for implementing comparisons.
+ """
+ pass
+
+class FileLike(metaclass=abc.ABCMeta):
+ """
+ An abc that defines get_file_name and get_file_extension methods.
+ """
+
+ @abc.abstractmethod
+ def get_file_name(self):
+ """Returns the filename this object represents"""
+ pass
+
+ @abc.abstractmethod
+ def get_file_extension(self):
+ """Returns the file extension of the file this object represents"""
+ pass
+
+@functools.lru_cache(maxsize=None)
+def get_file_extension_mixin(ext):
+ """
+ Gets a mixin that defines get_file_name(self) in terms of get_name(self) with the
+ given file extension.
+ """
+
+ class FExt(object):
+ """
+ A mixin defining get_file_name(self) in terms of get_name(self)
+ """
+
+ def get_file_name(self):
+ return self.get_name() + ext
+
+ def get_file_extension(self):
+ return ext
+
+ # Register the ABCs
+ Named.register(FExt)
+ FileLike.register(FExt)
+
+ return FExt
+
+class SmaliFileMixin(get_file_extension_mixin(".smali")):
+ """
+ A mixin that defines that the file this class belongs to is get_name() + ".smali".
+ """
+ pass
+
+class NameComparableMixin(object):
+ """
+ A mixin that defines the object comparison and related functionality in terms
+ of a get_name(self) function.
+ """
+
+ def __lt__(self, other):
+ return self.get_name() < other.get_name()
+
+ def __gt__(self, other):
+ return self.get_name() > other.get_name()
+
+ def __eq__(self, other):
+ return self.get_name() == other.get_name()
+
+ def __le__(self, other):
+ return self.get_name() <= other.get_name()
+
+ def __ge__(self, other):
+ return self.get_name() >= other.get_name()
+
+ def __ne__(self, other):
+ return self.get_name() != other.get_name()
+
+ def __hash__(self):
+ return hash(self.get_name())
+
+Named.register(NameComparableMixin)
+collections.abc.Hashable.register(NameComparableMixin)
+
+class DumpMixin(metaclass=abc.ABCMeta):
+ """
+ A mixin to add support for dumping the string representation of an object to a
+ file. Requires the get_file_name(self) method be defined.
+ """
+
+ @abc.abstractmethod
+ def __str__(self):
+ """
+ Returns the data to be printed to a file by dump.
+ """
+ pass
+
+ def dump(self, directory):
+ """
+ Dump this object to a file in the given directory
+ """
+ out_file = directory / self.get_file_name()
+ if out_file.exists():
+ out_file.unlink()
+ with out_file.open('w') as out:
+ print(str(self), file=out)
+
+FileLike.register(DumpMixin)
diff --git a/test/utils/python/testgen/utils.py b/test/utils/python/testgen/utils.py
new file mode 100644
index 0000000..769ad16
--- /dev/null
+++ b/test/utils/python/testgen/utils.py
@@ -0,0 +1,80 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2015 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.
+
+"""
+Common functions useful for writing test generators in python
+"""
+
+import itertools
+import os
+import string
+from pathlib import Path
+
+BUILD_TOP = os.getenv("ANDROID_BUILD_TOP")
+if BUILD_TOP is None:
+ print("ANDROID_BUILD_TOP not set. Please run build/envsetup.sh", file=sys.stderr)
+ sys.exit(1)
+
+# An iterator which yields strings made from lowercase letters. First yields
+# all 1 length strings, then all 2 and so on. It does this alphabetically.
+NAME_GEN = itertools.chain.from_iterable(
+ map(lambda n: itertools.product(string.ascii_lowercase, repeat=n),
+ itertools.count(1)))
+
+def gensym():
+ """
+ Returns a new, globally unique, identifier name that is a valid Java symbol
+ on each call.
+ """
+ return ''.join(next(NAME_GEN))
+
+def filter_blanks(s):
+ """
+ Takes a string returns the same string sans empty lines
+ """
+ return "\n".join(a for a in s.split("\n") if a.strip() != "")
+
+def get_copyright(filetype = "java"):
+ """
+ Returns the standard copyright header for the given filetype
+ """
+ if filetype == "smali":
+ return "\n".join(map(lambda a: "# " + a, get_copyright("java").split("\n")))
+ else:
+ fname = filetype + ".txt"
+ with (Path(BUILD_TOP)/"development"/"docs"/"copyright-templates"/fname).open() as template:
+ return "".join(template.readlines())
+
+def subtree_sizes(n):
+ """
+ A generator that yields a tuple containing a possible arrangement of subtree
+ nodes for a tree with a total of 'n' leaf nodes.
+ """
+ if n == 0:
+ return
+ elif n == 1:
+ yield (0,)
+ elif n == 2:
+ yield (1, 1)
+ else:
+ for prevt in subtree_sizes(n - 1):
+ prev = list(prevt)
+ yield tuple([1] + prev)
+ for i in range(len(prev)):
+ prev[i] += 1
+ yield tuple(prev)
+ prev[i] -= 1
+
diff --git a/tools/extract-embedded-java b/tools/extract-embedded-java
new file mode 100755
index 0000000..e966552
--- /dev/null
+++ b/tools/extract-embedded-java
@@ -0,0 +1,35 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+if [ "$#" -ne "2" ]; then
+ echo "Usage: ./extract_embedded_java.sh smali_dir java_dir"
+ exit 1
+fi
+
+# Check the input and output are directories
+[[ -d "$1" ]] || exit 1
+[[ -d "$2" ]] || exit 1
+
+# For every file which has the file extension smali, set $f to be the name without
+# .smali and then:
+for f in `find "$1" -type f -name "*.smali" | xargs -n 1 -P 0 -i basename -s .smali \{\}`; do
+ # remove all lines except those starting with '# ', remove the '#' then print
+ # it to a file ${name}.java. Do this concurrently.
+ grep "^# " "$1/${f}.smali" | sed "s:# ::" > "${2}/${f}.java" &
+done
+
+# wait for all the files to be written
+wait