summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolas Geoffray <ngeoffray@google.com>2014-07-23 16:04:16 +0100
committerNicolas Geoffray <ngeoffray@google.com>2014-07-28 15:44:28 +0100
commit3c7bb98698f77af10372cf31824d3bb115d9bf0f (patch)
tree1cd4cc18babfbb16ab908f23929fa88d7678f06b
parent98cc1e552c2ccbe5d51bc81d49e79119280f5416 (diff)
downloadart-3c7bb98698f77af10372cf31824d3bb115d9bf0f.zip
art-3c7bb98698f77af10372cf31824d3bb115d9bf0f.tar.gz
art-3c7bb98698f77af10372cf31824d3bb115d9bf0f.tar.bz2
Implement array get and array put in optimizing.
Also fix a couple of assembler/disassembler issues. Change-Id: I705c8572988c1a9c4df3172b304678529636d5f6
-rw-r--r--compiler/optimizing/builder.cc53
-rw-r--r--compiler/optimizing/builder.h4
-rw-r--r--compiler/optimizing/code_generator_arm.cc329
-rw-r--r--compiler/optimizing/code_generator_arm.h5
-rw-r--r--compiler/optimizing/code_generator_x86.cc335
-rw-r--r--compiler/optimizing/code_generator_x86.h5
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc325
-rw-r--r--compiler/optimizing/code_generator_x86_64.h5
-rw-r--r--compiler/optimizing/nodes.h78
-rw-r--r--compiler/utils/arm/assembler_thumb2.cc4
-rw-r--r--compiler/utils/assembler_thumb_test.cc6
-rw-r--r--compiler/utils/assembler_thumb_test_expected.cc.inc4
-rw-r--r--disassembler/disassembler.h4
-rw-r--r--disassembler/disassembler_arm.cc48
-rw-r--r--test/407-arrays/expected.txt0
-rw-r--r--test/407-arrays/info.txt1
-rw-r--r--test/407-arrays/src/Main.java127
-rw-r--r--test/407-arrays/src/TestCase.java199
18 files changed, 1418 insertions, 114 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 1f0b361..43e6b83 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -425,6 +425,41 @@ bool HGraphBuilder::BuildFieldAccess(const Instruction& instruction,
return true;
}
+void HGraphBuilder::BuildArrayAccess(const Instruction& instruction,
+ uint32_t dex_offset,
+ bool is_put,
+ Primitive::Type anticipated_type) {
+ uint8_t source_or_dest_reg = instruction.VRegA_23x();
+ uint8_t array_reg = instruction.VRegB_23x();
+ uint8_t index_reg = instruction.VRegC_23x();
+
+ DCHECK(IsTypeSupported(anticipated_type));
+
+ // We need one temporary for the null check, one for the index, and one for the length.
+ Temporaries temps(graph_, 3);
+
+ HInstruction* object = LoadLocal(array_reg, Primitive::kPrimNot);
+ object = new (arena_) HNullCheck(object, dex_offset);
+ current_block_->AddInstruction(object);
+ temps.Add(object);
+
+ HInstruction* length = new (arena_) HArrayLength(object);
+ current_block_->AddInstruction(length);
+ temps.Add(length);
+ HInstruction* index = LoadLocal(index_reg, Primitive::kPrimInt);
+ index = new (arena_) HBoundsCheck(index, length, dex_offset);
+ current_block_->AddInstruction(index);
+ temps.Add(index);
+ if (is_put) {
+ HInstruction* value = LoadLocal(source_or_dest_reg, anticipated_type);
+ // TODO: Insert a type check node if the type is Object.
+ current_block_->AddInstruction(new (arena_) HArraySet(object, index, value, dex_offset));
+ } else {
+ current_block_->AddInstruction(new (arena_) HArrayGet(object, index, anticipated_type));
+ UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
+ }
+}
+
bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_t dex_offset) {
if (current_block_ == nullptr) {
return true; // Dead code
@@ -697,6 +732,24 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_
break;
}
+#define ARRAY_XX(kind, anticipated_type) \
+ case Instruction::AGET##kind: { \
+ BuildArrayAccess(instruction, dex_offset, false, anticipated_type); \
+ break; \
+ } \
+ case Instruction::APUT##kind: { \
+ BuildArrayAccess(instruction, dex_offset, true, anticipated_type); \
+ break; \
+ }
+
+ ARRAY_XX(, Primitive::kPrimInt);
+ ARRAY_XX(_WIDE, Primitive::kPrimLong);
+ ARRAY_XX(_OBJECT, Primitive::kPrimNot);
+ ARRAY_XX(_BOOLEAN, Primitive::kPrimBoolean);
+ ARRAY_XX(_BYTE, Primitive::kPrimByte);
+ ARRAY_XX(_CHAR, Primitive::kPrimChar);
+ ARRAY_XX(_SHORT, Primitive::kPrimShort);
+
default:
return false;
}
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index f94b8e8..170c427 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -93,6 +93,10 @@ class HGraphBuilder : public ValueObject {
void BuildReturn(const Instruction& instruction, Primitive::Type type);
bool BuildFieldAccess(const Instruction& instruction, uint32_t dex_offset, bool is_get);
+ void BuildArrayAccess(const Instruction& instruction,
+ uint32_t dex_offset,
+ bool is_get,
+ Primitive::Type anticipated_type);
// Builds an invocation node and returns whether the instruction is supported.
bool BuildInvoke(const Instruction& instruction,
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 90ec6cf..eccc970 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -39,6 +39,24 @@ static constexpr bool kExplicitStackOverflowCheck = false;
static constexpr int kNumberOfPushedRegistersAtEntry = 1 + 2; // LR, R6, R7
static constexpr int kCurrentMethodStackOffset = 0;
+static Location ArmCoreLocation(Register reg) {
+ return Location::RegisterLocation(ArmManagedRegister::FromCoreRegister(reg));
+}
+
+static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1, R2 };
+static constexpr size_t kRuntimeParameterCoreRegistersLength =
+ arraysize(kRuntimeParameterCoreRegisters);
+
+class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
+ public:
+ InvokeRuntimeCallingConvention()
+ : CallingConvention(kRuntimeParameterCoreRegisters,
+ kRuntimeParameterCoreRegistersLength) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
+};
+
#define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())->
class NullCheckSlowPathARM : public SlowPathCode {
@@ -72,6 +90,33 @@ class StackOverflowCheckSlowPathARM : public SlowPathCode {
DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathARM);
};
+class BoundsCheckSlowPathARM : public SlowPathCode {
+ public:
+ explicit BoundsCheckSlowPathARM(uint32_t dex_pc,
+ Location index_location,
+ Location length_location)
+ : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {}
+
+ virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ CodeGeneratorARM* arm_codegen = reinterpret_cast<CodeGeneratorARM*>(codegen);
+ __ Bind(GetEntryLabel());
+ InvokeRuntimeCallingConvention calling_convention;
+ arm_codegen->Move32(ArmCoreLocation(calling_convention.GetRegisterAt(0)), index_location_);
+ arm_codegen->Move32(ArmCoreLocation(calling_convention.GetRegisterAt(1)), length_location_);
+ int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowArrayBounds).Int32Value();
+ __ ldr(LR, Address(TR, offset));
+ __ blx(LR);
+ codegen->RecordPcInfo(dex_pc_);
+ }
+
+ private:
+ const uint32_t dex_pc_;
+ const Location index_location_;
+ const Location length_location_;
+
+ DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM);
+};
+
#undef __
#define __ reinterpret_cast<ArmAssembler*>(GetAssembler())->
@@ -214,10 +259,6 @@ size_t CodeGeneratorARM::GetNumberOfRegisters() const {
return kNumberOfRegIds;
}
-static Location ArmCoreLocation(Register reg) {
- return Location::RegisterLocation(ArmManagedRegister::FromCoreRegister(reg));
-}
-
InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen)
: HGraphVisitor(graph),
assembler_(codegen->GetAssembler()),
@@ -943,20 +984,6 @@ void InstructionCodeGeneratorARM::VisitSub(HSub* sub) {
}
}
-static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1 };
-static constexpr size_t kRuntimeParameterCoreRegistersLength =
- arraysize(kRuntimeParameterCoreRegisters);
-
-class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
- public:
- InvokeRuntimeCallingConvention()
- : CallingConvention(kRuntimeParameterCoreRegisters,
- kRuntimeParameterCoreRegistersLength) {}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
-};
-
void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) {
codegen_->MarkNotLeaf();
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
@@ -1099,24 +1126,15 @@ void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instr
break;
}
- case Primitive::kPrimInt: {
- Register value = locations->InAt(1).AsArm().AsCoreRegister();
- __ StoreToOffset(kStoreWord, value, obj, offset);
- break;
- }
-
+ case Primitive::kPrimInt:
case Primitive::kPrimNot: {
Register value = locations->InAt(1).AsArm().AsCoreRegister();
__ StoreToOffset(kStoreWord, value, obj, offset);
-
- Register temp = locations->GetTemp(0).AsArm().AsCoreRegister();
- Register card = locations->GetTemp(1).AsArm().AsCoreRegister();
- Label is_null;
- __ CompareAndBranchIfZero(value, &is_null);
- __ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmWordSize>().Int32Value());
- __ Lsr(temp, obj, gc::accounting::CardTable::kCardShift);
- __ strb(card, Address(card, temp));
- __ Bind(&is_null);
+ if (field_type == Primitive::kPrimNot) {
+ Register temp = locations->GetTemp(0).AsArm().AsCoreRegister();
+ Register card = locations->GetTemp(1).AsArm().AsCoreRegister();
+ codegen_->MarkGCCard(temp, card, obj, value);
+ }
break;
}
@@ -1218,6 +1236,251 @@ void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
__ b(slow_path->GetEntryLabel(), EQ);
}
+void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister());
+ instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ Register obj = locations->InAt(0).AsArm().AsCoreRegister();
+ Location index = locations->InAt(1);
+
+ switch (instruction->GetType()) {
+ case Primitive::kPrimBoolean: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
+ Register out = locations->Out().AsArm().AsCoreRegister();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
+ __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset);
+ } else {
+ __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister()));
+ __ LoadFromOffset(kLoadUnsignedByte, out, IP, data_offset);
+ }
+ break;
+ }
+
+ case Primitive::kPrimByte: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
+ Register out = locations->Out().AsArm().AsCoreRegister();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
+ __ LoadFromOffset(kLoadSignedByte, out, obj, offset);
+ } else {
+ __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister()));
+ __ LoadFromOffset(kLoadSignedByte, out, IP, data_offset);
+ }
+ break;
+ }
+
+ case Primitive::kPrimShort: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
+ Register out = locations->Out().AsArm().AsCoreRegister();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
+ __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset);
+ } else {
+ __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_2));
+ __ LoadFromOffset(kLoadSignedHalfword, out, IP, data_offset);
+ }
+ break;
+ }
+
+ case Primitive::kPrimChar: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
+ Register out = locations->Out().AsArm().AsCoreRegister();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
+ __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset);
+ } else {
+ __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_2));
+ __ LoadFromOffset(kLoadUnsignedHalfword, out, IP, data_offset);
+ }
+ break;
+ }
+
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t));
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+ Register out = locations->Out().AsArm().AsCoreRegister();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ __ LoadFromOffset(kLoadWord, out, obj, offset);
+ } else {
+ __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_4));
+ __ LoadFromOffset(kLoadWord, out, IP, data_offset);
+ }
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
+ ArmManagedRegister out = locations->Out().AsArm();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), obj, offset);
+ } else {
+ __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_8));
+ __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), IP, data_offset);
+ }
+ break;
+ }
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << instruction->GetType();
+ }
+}
+
+void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ Primitive::Type value_type = instruction->InputAt(2)->GetType();
+ if (value_type == Primitive::kPrimNot) {
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetInAt(0, ArmCoreLocation(calling_convention.GetRegisterAt(0)));
+ locations->SetInAt(1, ArmCoreLocation(calling_convention.GetRegisterAt(1)));
+ locations->SetInAt(2, ArmCoreLocation(calling_convention.GetRegisterAt(2)));
+ codegen_->MarkNotLeaf();
+ } else {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+ locations->SetInAt(2, Location::RequiresRegister());
+ }
+ instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ Register obj = locations->InAt(0).AsArm().AsCoreRegister();
+ Location index = locations->InAt(1);
+ Primitive::Type value_type = instruction->InputAt(2)->GetType();
+
+ switch (value_type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
+ Register value = locations->InAt(2).AsArm().AsCoreRegister();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
+ __ StoreToOffset(kStoreByte, value, obj, offset);
+ } else {
+ __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister()));
+ __ StoreToOffset(kStoreByte, value, IP, data_offset);
+ }
+ break;
+ }
+
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
+ Register value = locations->InAt(2).AsArm().AsCoreRegister();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
+ __ StoreToOffset(kStoreHalfword, value, obj, offset);
+ } else {
+ __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_2));
+ __ StoreToOffset(kStoreHalfword, value, IP, data_offset);
+ }
+ break;
+ }
+
+ case Primitive::kPrimInt: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+ Register value = locations->InAt(2).AsArm().AsCoreRegister();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ __ StoreToOffset(kStoreWord, value, obj, offset);
+ } else {
+ __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_4));
+ __ StoreToOffset(kStoreWord, value, IP, data_offset);
+ }
+ break;
+ }
+
+ case Primitive::kPrimNot: {
+ int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAputObject).Int32Value();
+ __ ldr(LR, Address(TR, offset));
+ __ blx(LR);
+ codegen_->RecordPcInfo(instruction->GetDexPc());
+ DCHECK(!codegen_->IsLeafMethod());
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
+ ArmManagedRegister value = locations->InAt(2).AsArm();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), obj, offset);
+ } else {
+ __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_8));
+ __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), IP, data_offset);
+ }
+ break;
+ }
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << instruction->GetType();
+ }
+}
+
+void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
+ instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ uint32_t offset = mirror::Array::LengthOffset().Uint32Value();
+ Register obj = locations->InAt(0).AsArm().AsCoreRegister();
+ Register out = locations->Out().AsArm().AsCoreRegister();
+ __ LoadFromOffset(kLoadWord, out, obj, offset);
+}
+
+void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ // TODO: Have a normalization phase that makes this instruction never used.
+ locations->SetOut(Location::SameAsFirstInput());
+ instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(
+ instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+ codegen_->AddSlowPath(slow_path);
+
+ Register index = locations->InAt(0).AsArm().AsCoreRegister();
+ Register length = locations->InAt(1).AsArm().AsCoreRegister();
+
+ __ cmp(index, ShifterOperand(length));
+ __ b(slow_path->GetEntryLabel(), CS);
+}
+
+void CodeGeneratorARM::MarkGCCard(Register temp, Register card, Register object, Register value) {
+ Label is_null;
+ __ CompareAndBranchIfZero(value, &is_null);
+ __ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmWordSize>().Int32Value());
+ __ Lsr(temp, object, gc::accounting::CardTable::kCardShift);
+ __ strb(card, Address(card, temp));
+ __ Bind(&is_null);
+}
+
void LocationsBuilderARM::VisitTemporary(HTemporary* temp) {
temp->SetLocations(nullptr);
}
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 85ab22b..610625c 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -175,12 +175,15 @@ class CodeGeneratorARM : public CodeGenerator {
return InstructionSet::kThumb2;
}
- private:
// Helper method to move a 32bits value between two locations.
void Move32(Location destination, Location source);
// Helper method to move a 64bits value between two locations.
void Move64(Location destination, Location source);
+ // Emit a write barrier.
+ void MarkGCCard(Register temp, Register card, Register object, Register value);
+
+ private:
LocationsBuilderARM location_builder_;
InstructionCodeGeneratorARM instruction_visitor_;
ParallelMoveResolverARM move_resolver_;
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 251a2ad..2264638 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -39,6 +39,24 @@ static constexpr bool kExplicitStackOverflowCheck = false;
static constexpr int kNumberOfPushedRegistersAtEntry = 1;
static constexpr int kCurrentMethodStackOffset = 0;
+static Location X86CpuLocation(Register reg) {
+ return Location::RegisterLocation(X86ManagedRegister::FromCpuRegister(reg));
+}
+
+static constexpr Register kRuntimeParameterCoreRegisters[] = { EAX, ECX, EDX };
+static constexpr size_t kRuntimeParameterCoreRegistersLength =
+ arraysize(kRuntimeParameterCoreRegisters);
+
+class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
+ public:
+ InvokeRuntimeCallingConvention()
+ : CallingConvention(kRuntimeParameterCoreRegisters,
+ kRuntimeParameterCoreRegistersLength) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
+};
+
#define __ reinterpret_cast<X86Assembler*>(codegen->GetAssembler())->
class NullCheckSlowPathX86 : public SlowPathCode {
@@ -71,6 +89,31 @@ class StackOverflowCheckSlowPathX86 : public SlowPathCode {
DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86);
};
+class BoundsCheckSlowPathX86 : public SlowPathCode {
+ public:
+ explicit BoundsCheckSlowPathX86(uint32_t dex_pc,
+ Location index_location,
+ Location length_location)
+ : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {}
+
+ virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ CodeGeneratorX86* x86_codegen = reinterpret_cast<CodeGeneratorX86*>(codegen);
+ __ Bind(GetEntryLabel());
+ InvokeRuntimeCallingConvention calling_convention;
+ x86_codegen->Move32(X86CpuLocation(calling_convention.GetRegisterAt(0)), index_location_);
+ x86_codegen->Move32(X86CpuLocation(calling_convention.GetRegisterAt(1)), length_location_);
+ __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowArrayBounds)));
+ codegen->RecordPcInfo(dex_pc_);
+ }
+
+ private:
+ const uint32_t dex_pc_;
+ const Location index_location_;
+ const Location length_location_;
+
+ DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86);
+};
+
#undef __
#define __ reinterpret_cast<X86Assembler*>(GetAssembler())->
@@ -188,10 +231,6 @@ size_t CodeGeneratorX86::GetNumberOfRegisters() const {
return kNumberOfRegIds;
}
-static Location X86CpuLocation(Register reg) {
- return Location::RegisterLocation(X86ManagedRegister::FromCpuRegister(reg));
-}
-
InstructionCodeGeneratorX86::InstructionCodeGeneratorX86(HGraph* graph, CodeGeneratorX86* codegen)
: HGraphVisitor(graph),
assembler_(codegen->GetAssembler()),
@@ -260,20 +299,6 @@ Location CodeGeneratorX86::GetStackLocation(HLoadLocal* load) const {
return Location();
}
-static constexpr Register kRuntimeParameterCoreRegisters[] = { EAX, ECX, EDX };
-static constexpr size_t kRuntimeParameterCoreRegistersLength =
- arraysize(kRuntimeParameterCoreRegisters);
-
-class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
- public:
- InvokeRuntimeCallingConvention()
- : CallingConvention(kRuntimeParameterCoreRegisters,
- kRuntimeParameterCoreRegistersLength) {}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
-};
-
Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) {
switch (type) {
case Primitive::kPrimBoolean:
@@ -1048,7 +1073,7 @@ void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction)
locations->SetInAt(1, Location::RequiresRegister());
}
// Temporary registers for the write barrier.
- if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) {
+ if (field_type == Primitive::kPrimNot) {
locations->AddTemp(Location::RequiresRegister());
// Ensure the card is in a byte register.
locations->AddTemp(X86CpuLocation(ECX));
@@ -1077,25 +1102,16 @@ void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instr
break;
}
- case Primitive::kPrimInt: {
- Register value = locations->InAt(1).AsX86().AsCpuRegister();
- __ movl(Address(obj, offset), value);
- break;
- }
-
+ case Primitive::kPrimInt:
case Primitive::kPrimNot: {
Register value = locations->InAt(1).AsX86().AsCpuRegister();
__ movl(Address(obj, offset), value);
- Label is_null;
- Register temp = locations->GetTemp(0).AsX86().AsCpuRegister();
- Register card = locations->GetTemp(1).AsX86().AsCpuRegister();
- __ testl(value, value);
- __ j(kEqual, &is_null);
- __ fs()->movl(card, Address::Absolute(Thread::CardTableOffset<kX86WordSize>().Int32Value()));
- __ movl(temp, obj);
- __ shrl(temp, Immediate(gc::accounting::CardTable::kCardShift));
- __ movb(Address(temp, card, TIMES_1, 0), locations->GetTemp(1).AsX86().AsByteRegister());
- __ Bind(&is_null);
+
+ if (field_type == Primitive::kPrimNot) {
+ Register temp = locations->GetTemp(0).AsX86().AsCpuRegister();
+ Register card = locations->GetTemp(1).AsX86().AsCpuRegister();
+ codegen_->MarkGCCard(temp, card, obj, value);
+ }
break;
}
@@ -1115,6 +1131,18 @@ void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instr
}
}
+void CodeGeneratorX86::MarkGCCard(Register temp, Register card, Register object, Register value) {
+ Label is_null;
+ __ testl(value, value);
+ __ j(kEqual, &is_null);
+ __ fs()->movl(card, Address::Absolute(Thread::CardTableOffset<kX86WordSize>().Int32Value()));
+ __ movl(temp, object);
+ __ shrl(temp, Immediate(gc::accounting::CardTable::kCardShift));
+ __ movb(Address(temp, card, TIMES_1, 0),
+ X86ManagedRegister::FromCpuRegister(card).AsByteRegister());
+ __ Bind(&is_null);
+}
+
void LocationsBuilderX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
locations->SetInAt(0, Location::RequiresRegister());
@@ -1202,6 +1230,243 @@ void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) {
__ j(kEqual, slow_path->GetEntryLabel());
}
+void LocationsBuilderX86::VisitArrayGet(HArrayGet* instruction) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister());
+ instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ Register obj = locations->InAt(0).AsX86().AsCpuRegister();
+ Location index = locations->InAt(1);
+
+ switch (instruction->GetType()) {
+ case Primitive::kPrimBoolean: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
+ Register out = locations->Out().AsX86().AsCpuRegister();
+ if (index.IsConstant()) {
+ __ movzxb(out, Address(obj,
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset));
+ } else {
+ __ movzxb(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_1, data_offset));
+ }
+ break;
+ }
+
+ case Primitive::kPrimByte: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
+ Register out = locations->Out().AsX86().AsCpuRegister();
+ if (index.IsConstant()) {
+ __ movsxb(out, Address(obj,
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset));
+ } else {
+ __ movsxb(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_1, data_offset));
+ }
+ break;
+ }
+
+ case Primitive::kPrimShort: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
+ Register out = locations->Out().AsX86().AsCpuRegister();
+ if (index.IsConstant()) {
+ __ movsxw(out, Address(obj,
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset));
+ } else {
+ __ movsxw(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_2, data_offset));
+ }
+ break;
+ }
+
+ case Primitive::kPrimChar: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
+ Register out = locations->Out().AsX86().AsCpuRegister();
+ if (index.IsConstant()) {
+ __ movzxw(out, Address(obj,
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset));
+ } else {
+ __ movzxw(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_2, data_offset));
+ }
+ break;
+ }
+
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+ Register out = locations->Out().AsX86().AsCpuRegister();
+ if (index.IsConstant()) {
+ __ movl(out, Address(obj,
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset));
+ } else {
+ __ movl(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_4, data_offset));
+ }
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
+ X86ManagedRegister out = locations->Out().AsX86();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ __ movl(out.AsRegisterPairLow(), Address(obj, offset));
+ __ movl(out.AsRegisterPairHigh(), Address(obj, offset + kX86WordSize));
+ } else {
+ __ movl(out.AsRegisterPairLow(),
+ Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset));
+ __ movl(out.AsRegisterPairHigh(),
+ Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset + kX86WordSize));
+ }
+ break;
+ }
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << instruction->GetType();
+ }
+}
+
+void LocationsBuilderX86::VisitArraySet(HArraySet* instruction) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ Primitive::Type value_type = instruction->InputAt(2)->GetType();
+ if (value_type == Primitive::kPrimNot) {
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetInAt(0, X86CpuLocation(calling_convention.GetRegisterAt(0)));
+ locations->SetInAt(1, X86CpuLocation(calling_convention.GetRegisterAt(1)));
+ locations->SetInAt(2, X86CpuLocation(calling_convention.GetRegisterAt(2)));
+ codegen_->MarkNotLeaf();
+ } else {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+ if (value_type == Primitive::kPrimBoolean || value_type == Primitive::kPrimByte) {
+ // Ensure the value is in a byte register.
+ locations->SetInAt(2, X86CpuLocation(EAX));
+ } else {
+ locations->SetInAt(2, Location::RequiresRegister());
+ }
+ }
+
+ instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ Register obj = locations->InAt(0).AsX86().AsCpuRegister();
+ Location index = locations->InAt(1);
+ Primitive::Type value_type = instruction->InputAt(2)->GetType();
+
+ switch (value_type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
+ ByteRegister value = locations->InAt(2).AsX86().AsByteRegister();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
+ __ movb(Address(obj, offset), value);
+ } else {
+ __ movb(Address(obj, index.AsX86().AsCpuRegister(), TIMES_1, data_offset), value);
+ }
+ break;
+ }
+
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
+ Register value = locations->InAt(2).AsX86().AsCpuRegister();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
+ __ movw(Address(obj, offset), value);
+ } else {
+ __ movw(Address(obj, index.AsX86().AsCpuRegister(), TIMES_2, data_offset), value);
+ }
+ break;
+ }
+
+ case Primitive::kPrimInt: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+ Register value = locations->InAt(2).AsX86().AsCpuRegister();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ __ movl(Address(obj, offset), value);
+ } else {
+ __ movl(Address(obj, index.AsX86().AsCpuRegister(), TIMES_4, data_offset), value);
+ }
+ break;
+ }
+
+ case Primitive::kPrimNot: {
+ DCHECK(!codegen_->IsLeafMethod());
+ __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAputObject)));
+ codegen_->RecordPcInfo(instruction->GetDexPc());
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
+ X86ManagedRegister value = locations->InAt(2).AsX86();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ __ movl(Address(obj, offset), value.AsRegisterPairLow());
+ __ movl(Address(obj, offset + kX86WordSize), value.AsRegisterPairHigh());
+ } else {
+ __ movl(Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset),
+ value.AsRegisterPairLow());
+ __ movl(Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset + kX86WordSize),
+ value.AsRegisterPairHigh());
+ }
+ break;
+ }
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << instruction->GetType();
+ }
+}
+
+void LocationsBuilderX86::VisitArrayLength(HArrayLength* instruction) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
+ instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorX86::VisitArrayLength(HArrayLength* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ uint32_t offset = mirror::Array::LengthOffset().Uint32Value();
+ Register obj = locations->InAt(0).AsX86().AsCpuRegister();
+ Register out = locations->Out().AsX86().AsCpuRegister();
+ __ movl(out, Address(obj, offset));
+}
+
+void LocationsBuilderX86::VisitBoundsCheck(HBoundsCheck* instruction) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ // TODO: Have a normalization phase that makes this instruction never used.
+ locations->SetOut(Location::SameAsFirstInput());
+ instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorX86::VisitBoundsCheck(HBoundsCheck* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86(
+ instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+ codegen_->AddSlowPath(slow_path);
+
+ Register index = locations->InAt(0).AsX86().AsCpuRegister();
+ Register length = locations->InAt(1).AsX86().AsCpuRegister();
+
+ __ cmpl(index, length);
+ __ j(kAboveEqual, slow_path->GetEntryLabel());
+}
+
void LocationsBuilderX86::VisitTemporary(HTemporary* temp) {
temp->SetLocations(nullptr);
}
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index b7c2ad8..7c50204 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -177,12 +177,15 @@ class CodeGeneratorX86 : public CodeGenerator {
return InstructionSet::kX86;
}
- private:
// Helper method to move a 32bits value between two locations.
void Move32(Location destination, Location source);
// Helper method to move a 64bits value between two locations.
void Move64(Location destination, Location source);
+ // Emit a write barrier.
+ void MarkGCCard(Register temp, Register card, Register object, Register value);
+
+ private:
LocationsBuilderX86 location_builder_;
InstructionCodeGeneratorX86 instruction_visitor_;
ParallelMoveResolverX86 move_resolver_;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 641e8e1..2ff2a17 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -43,6 +43,23 @@ static constexpr Register TMP = R11;
static constexpr int kNumberOfPushedRegistersAtEntry = 1;
static constexpr int kCurrentMethodStackOffset = 0;
+static Location X86_64CpuLocation(Register reg) {
+ return Location::RegisterLocation(X86_64ManagedRegister::FromCpuRegister(reg));
+}
+
+static constexpr Register kRuntimeParameterCoreRegisters[] = { RDI, RSI, RDX };
+static constexpr size_t kRuntimeParameterCoreRegistersLength =
+ arraysize(kRuntimeParameterCoreRegisters);
+
+class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
+ public:
+ InvokeRuntimeCallingConvention()
+ : CallingConvention(kRuntimeParameterCoreRegisters,
+ kRuntimeParameterCoreRegistersLength) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
+};
#define __ reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler())->
@@ -78,6 +95,32 @@ class StackOverflowCheckSlowPathX86_64 : public SlowPathCode {
DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86_64);
};
+class BoundsCheckSlowPathX86_64 : public SlowPathCode {
+ public:
+ explicit BoundsCheckSlowPathX86_64(uint32_t dex_pc,
+ Location index_location,
+ Location length_location)
+ : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {}
+
+ virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ CodeGeneratorX86_64* x64_codegen = reinterpret_cast<CodeGeneratorX86_64*>(codegen);
+ __ Bind(GetEntryLabel());
+ InvokeRuntimeCallingConvention calling_convention;
+ x64_codegen->Move(X86_64CpuLocation(calling_convention.GetRegisterAt(0)), index_location_);
+ x64_codegen->Move(X86_64CpuLocation(calling_convention.GetRegisterAt(1)), length_location_);
+ __ gs()->call(Address::Absolute(
+ QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowArrayBounds), true));
+ codegen->RecordPcInfo(dex_pc_);
+ }
+
+ private:
+ const uint32_t dex_pc_;
+ const Location index_location_;
+ const Location length_location_;
+
+ DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86_64);
+};
+
#undef __
#define __ reinterpret_cast<X86_64Assembler*>(GetAssembler())->
@@ -103,10 +146,6 @@ void CodeGeneratorX86_64::DumpFloatingPointRegister(std::ostream& stream, int re
stream << X86_64ManagedRegister::FromXmmRegister(FloatRegister(reg));
}
-static Location X86_64CpuLocation(Register reg) {
- return Location::RegisterLocation(X86_64ManagedRegister::FromCpuRegister(reg));
-}
-
CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph)
: CodeGenerator(graph, kNumberOfRegIds),
location_builder_(graph, this),
@@ -117,7 +156,8 @@ size_t CodeGeneratorX86_64::FrameEntrySpillSize() const {
return kNumberOfPushedRegistersAtEntry * kX86_64WordSize;
}
-InstructionCodeGeneratorX86_64::InstructionCodeGeneratorX86_64(HGraph* graph, CodeGeneratorX86_64* codegen)
+InstructionCodeGeneratorX86_64::InstructionCodeGeneratorX86_64(HGraph* graph,
+ CodeGeneratorX86_64* codegen)
: HGraphVisitor(graph),
assembler_(codegen->GetAssembler()),
codegen_(codegen) {}
@@ -603,20 +643,6 @@ void InstructionCodeGeneratorX86_64::VisitReturn(HReturn* ret) {
__ ret();
}
-static constexpr Register kRuntimeParameterCoreRegisters[] = { RDI, RSI, RDX };
-static constexpr size_t kRuntimeParameterCoreRegistersLength =
- arraysize(kRuntimeParameterCoreRegisters);
-
-class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
- public:
- InvokeRuntimeCallingConvention()
- : CallingConvention(kRuntimeParameterCoreRegisters,
- kRuntimeParameterCoreRegistersLength) {}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
-};
-
Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) {
switch (type) {
case Primitive::kPrimBoolean:
@@ -944,24 +970,14 @@ void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* in
break;
}
- case Primitive::kPrimInt: {
- __ movl(Address(obj, offset), value);
- break;
- }
-
+ case Primitive::kPrimInt:
case Primitive::kPrimNot: {
__ movl(Address(obj, offset), value);
- Label is_null;
- CpuRegister temp = locations->GetTemp(0).AsX86_64().AsCpuRegister();
- CpuRegister card = locations->GetTemp(1).AsX86_64().AsCpuRegister();
- __ testl(value, value);
- __ j(kEqual, &is_null);
- __ gs()->movq(card, Address::Absolute(
- Thread::CardTableOffset<kX86_64WordSize>().Int32Value(), true));
- __ movq(temp, obj);
- __ shrq(temp, Immediate(gc::accounting::CardTable::kCardShift));
- __ movb(Address(temp, card, TIMES_1, 0), card);
- __ Bind(&is_null);
+ if (field_type == Primitive::kPrimNot) {
+ CpuRegister temp = locations->GetTemp(0).AsX86_64().AsCpuRegister();
+ CpuRegister card = locations->GetTemp(1).AsX86_64().AsCpuRegister();
+ codegen_->MarkGCCard(temp, card, obj, value);
+ }
break;
}
@@ -1059,6 +1075,245 @@ void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) {
__ j(kEqual, slow_path->GetEntryLabel());
}
+void LocationsBuilderX86_64::VisitArrayGet(HArrayGet* instruction) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister());
+ instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister();
+ Location index = locations->InAt(1);
+
+ switch (instruction->GetType()) {
+ case Primitive::kPrimBoolean: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
+ CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+ if (index.IsConstant()) {
+ __ movzxb(out, Address(obj,
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset));
+ } else {
+ __ movzxb(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_1, data_offset));
+ }
+ break;
+ }
+
+ case Primitive::kPrimByte: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
+ CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+ if (index.IsConstant()) {
+ __ movsxb(out, Address(obj,
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset));
+ } else {
+ __ movsxb(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_1, data_offset));
+ }
+ break;
+ }
+
+ case Primitive::kPrimShort: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
+ CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+ if (index.IsConstant()) {
+ __ movsxw(out, Address(obj,
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset));
+ } else {
+ __ movsxw(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_2, data_offset));
+ }
+ break;
+ }
+
+ case Primitive::kPrimChar: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
+ CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+ if (index.IsConstant()) {
+ __ movzxw(out, Address(obj,
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset));
+ } else {
+ __ movzxw(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_2, data_offset));
+ }
+ break;
+ }
+
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t));
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+ CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+ if (index.IsConstant()) {
+ __ movl(out, Address(obj,
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset));
+ } else {
+ __ movl(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_4, data_offset));
+ }
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
+ CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+ if (index.IsConstant()) {
+ __ movq(out, Address(obj,
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset));
+ } else {
+ __ movq(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_8, data_offset));
+ }
+ break;
+ }
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << instruction->GetType();
+ }
+}
+
+void LocationsBuilderX86_64::VisitArraySet(HArraySet* instruction) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ Primitive::Type value_type = instruction->InputAt(2)->GetType();
+ if (value_type == Primitive::kPrimNot) {
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetInAt(0, X86_64CpuLocation(calling_convention.GetRegisterAt(0)));
+ locations->SetInAt(1, X86_64CpuLocation(calling_convention.GetRegisterAt(1)));
+ locations->SetInAt(2, X86_64CpuLocation(calling_convention.GetRegisterAt(2)));
+ codegen_->MarkNotLeaf();
+ } else {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+ locations->SetInAt(2, Location::RequiresRegister());
+ }
+ instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister();
+ Location index = locations->InAt(1);
+ Primitive::Type value_type = instruction->InputAt(2)->GetType();
+
+ switch (value_type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
+ CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
+ __ movb(Address(obj, offset), value);
+ } else {
+ __ movb(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_1, data_offset), value);
+ }
+ break;
+ }
+
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
+ CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
+ __ movw(Address(obj, offset), value);
+ } else {
+ __ movw(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_2, data_offset), value);
+ }
+ break;
+ }
+
+ case Primitive::kPrimInt: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+ CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ __ movl(Address(obj, offset), value);
+ } else {
+ __ movl(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_4, data_offset), value);
+ }
+ break;
+ }
+
+ case Primitive::kPrimNot: {
+ __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAputObject), true));
+ DCHECK(!codegen_->IsLeafMethod());
+ codegen_->RecordPcInfo(instruction->GetDexPc());
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
+ CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ __ movq(Address(obj, offset), value);
+ } else {
+ __ movq(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_8, data_offset), value);
+ }
+ break;
+ }
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << instruction->GetType();
+ }
+}
+
+void LocationsBuilderX86_64::VisitArrayLength(HArrayLength* instruction) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
+ instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorX86_64::VisitArrayLength(HArrayLength* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ uint32_t offset = mirror::Array::LengthOffset().Uint32Value();
+ CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister();
+ CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+ __ movl(out, Address(obj, offset));
+}
+
+void LocationsBuilderX86_64::VisitBoundsCheck(HBoundsCheck* instruction) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ // TODO: Have a normalization phase that makes this instruction never used.
+ locations->SetOut(Location::SameAsFirstInput());
+ instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorX86_64::VisitBoundsCheck(HBoundsCheck* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86_64(
+ instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+ codegen_->AddSlowPath(slow_path);
+
+ CpuRegister index = locations->InAt(0).AsX86_64().AsCpuRegister();
+ CpuRegister length = locations->InAt(1).AsX86_64().AsCpuRegister();
+
+ __ cmpl(index, length);
+ __ j(kAboveEqual, slow_path->GetEntryLabel());
+}
+
+void CodeGeneratorX86_64::MarkGCCard(CpuRegister temp,
+ CpuRegister card,
+ CpuRegister object,
+ CpuRegister value) {
+ Label is_null;
+ __ testl(value, value);
+ __ j(kEqual, &is_null);
+ __ gs()->movq(card, Address::Absolute(
+ Thread::CardTableOffset<kX86_64WordSize>().Int32Value(), true));
+ __ movq(temp, object);
+ __ shrq(temp, Immediate(gc::accounting::CardTable::kCardShift));
+ __ movb(Address(temp, card, TIMES_1, 0), card);
+ __ Bind(&is_null);
+}
+
void LocationsBuilderX86_64::VisitTemporary(HTemporary* temp) {
temp->SetLocations(nullptr);
}
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index a20ca3f..44552ea 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -176,10 +176,13 @@ class CodeGeneratorX86_64 : public CodeGenerator {
return InstructionSet::kX86_64;
}
- private:
+ // Emit a write barrier.
+ void MarkGCCard(CpuRegister temp, CpuRegister card, CpuRegister object, CpuRegister value);
+
// Helper method to move a value between two locations.
void Move(Location destination, Location source);
+ private:
LocationsBuilderX86_64 location_builder_;
InstructionCodeGeneratorX86_64 instruction_visitor_;
ParallelMoveResolverX86_64 move_resolver_;
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 61a6f6b..cb3dd0f 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -437,6 +437,10 @@ class HBasicBlock : public ArenaObject {
M(Compare) \
M(InstanceFieldGet) \
M(InstanceFieldSet) \
+ M(ArrayGet) \
+ M(ArraySet) \
+ M(ArrayLength) \
+ M(BoundsCheck) \
M(NullCheck) \
M(Temporary) \
@@ -1367,6 +1371,80 @@ class HInstanceFieldSet : public HTemplateInstruction<2> {
DISALLOW_COPY_AND_ASSIGN(HInstanceFieldSet);
};
+class HArrayGet : public HExpression<2> {
+ public:
+ HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type)
+ : HExpression(type) {
+ SetRawInputAt(0, array);
+ SetRawInputAt(1, index);
+ }
+
+ DECLARE_INSTRUCTION(ArrayGet);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HArrayGet);
+};
+
+class HArraySet : public HTemplateInstruction<3> {
+ public:
+ HArraySet(HInstruction* array,
+ HInstruction* index,
+ HInstruction* value,
+ uint32_t dex_pc) : dex_pc_(dex_pc) {
+ SetRawInputAt(0, array);
+ SetRawInputAt(1, index);
+ SetRawInputAt(2, value);
+ }
+
+ virtual bool NeedsEnvironment() const {
+ // We currently always call a runtime method to catch array store
+ // exceptions.
+ return InputAt(2)->GetType() == Primitive::kPrimNot;
+ }
+
+ uint32_t GetDexPc() const { return dex_pc_; }
+
+ DECLARE_INSTRUCTION(ArraySet);
+
+ private:
+ const uint32_t dex_pc_;
+
+ DISALLOW_COPY_AND_ASSIGN(HArraySet);
+};
+
+class HArrayLength : public HExpression<1> {
+ public:
+ explicit HArrayLength(HInstruction* array) : HExpression(Primitive::kPrimInt) {
+ SetRawInputAt(0, array);
+ }
+
+ DECLARE_INSTRUCTION(ArrayLength);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HArrayLength);
+};
+
+class HBoundsCheck : public HExpression<2> {
+ public:
+ HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc)
+ : HExpression(index->GetType()), dex_pc_(dex_pc) {
+ DCHECK(index->GetType() == Primitive::kPrimInt);
+ SetRawInputAt(0, index);
+ SetRawInputAt(1, length);
+ }
+
+ virtual bool NeedsEnvironment() const { return true; }
+
+ uint32_t GetDexPc() const { return dex_pc_; }
+
+ DECLARE_INSTRUCTION(BoundsCheck);
+
+ private:
+ const uint32_t dex_pc_;
+
+ DISALLOW_COPY_AND_ASSIGN(HBoundsCheck);
+};
+
/**
* Some DEX instructions are folded into multiple HInstructions that need
* to stay live until the last HInstruction. This class
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 78ff31a..4904428 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -659,6 +659,10 @@ bool Thumb2Assembler::Is32BitDataProcessing(Condition cond,
if (so.IsImmediate()) {
return true;
}
+
+ if (!can_contain_high_register) {
+ return true;
+ }
}
if (so.IsRegister() && IsHighRegister(so.GetRegister()) && !can_contain_high_register) {
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 3312f8f..891a287 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -312,6 +312,9 @@ TEST(Thumb2AssemblerTest, DataProcessingRegister) {
__ movs(R0, ShifterOperand(R1));
__ mvns(R0, ShifterOperand(R1));
+ // 32 bit variants.
+ __ add(R12, R1, ShifterOperand(R0));
+
size_t cs = __ CodeSize();
std::vector<uint8_t> managed_code(cs);
MemoryRegion code(&managed_code[0], managed_code.size());
@@ -868,6 +871,9 @@ TEST(Thumb2AssemblerTest, StoreToOffset) {
__ StoreToOffset(kStoreWord, R2, R4, 12); // Simple
__ StoreToOffset(kStoreWord, R2, R4, 0x2000); // Offset too big.
+ __ StoreToOffset(kStoreWord, R0, R12, 12);
+ __ StoreToOffset(kStoreHalfword, R0, R12, 12);
+ __ StoreToOffset(kStoreByte, R2, R12, 12);
size_t cs = __ CodeSize();
std::vector<uint8_t> managed_code(cs);
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 18035f3..3f2641c 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -43,6 +43,7 @@ const char* DataProcessingRegisterResults[] = {
" 3e: 42c8 cmn r0, r1\n",
" 40: 0008 movs r0, r1\n",
" 42: 43c8 mvns r0, r1\n",
+ " 44: eb01 0c00 add.w ip, r1, r0\n",
nullptr
};
const char* DataProcessingImmediateResults[] = {
@@ -355,6 +356,9 @@ const char* StoreToOffsetResults[] = {
" 2: f44f 5c00 mov.w ip, #8192 ; 0x2000\n",
" 6: 44a4 add ip, r4\n",
" 8: f8cc 2000 str.w r2, [ip]\n",
+ " c: f8cc 000c str.w r0, [ip, #12]\n",
+ " 10: f8ac 000c strh.w r0, [ip, #12]\n",
+ " 14: f88c 200c strb.w r2, [ip, #12]\n",
nullptr
};
const char* IfThenResults[] = {
diff --git a/disassembler/disassembler.h b/disassembler/disassembler.h
index 7547ab7..183e692 100644
--- a/disassembler/disassembler.h
+++ b/disassembler/disassembler.h
@@ -43,6 +43,10 @@ class Disassembler {
DISALLOW_COPY_AND_ASSIGN(Disassembler);
};
+static inline bool HasBitSet(uint32_t value, uint32_t bit) {
+ return (value & (1 << bit)) != 0;
+}
+
} // namespace art
#endif // ART_DISASSEMBLER_DISASSEMBLER_H_
diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc
index 1f565e5..56023c1 100644
--- a/disassembler/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -1262,7 +1262,7 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr)
case 3:
switch (op2) {
case 0x00: case 0x02: case 0x04: case 0x06: // 000xxx0
- case 0x08: case 0x0A: case 0x0C: case 0x0E: {
+ case 0x08: case 0x09: case 0x0A: case 0x0C: case 0x0E: {
// Store single data item
// |111|11|100|000|0|0000|1111|110000|000000|
// |5 3|21|098|765|4|3 0|5 2|10 6|5 0|
@@ -1275,12 +1275,40 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr)
// uint32_t op4 = (instr >> 6) & 0x3F;
switch (op3) {
case 0x0: case 0x4: {
- // STRB Rt,[Rn,#+/-imm8] - 111 11 00 0 0 00 0 nnnn tttt 1 PUWii ii iiii
- // STRB Rt,[Rn,Rm,lsl #imm2] - 111 11 00 0 0 00 0 nnnn tttt 0 00000 ii mmmm
+ // {ST,LD}RB Rt,[Rn,#+/-imm12] - 111 11 00 0 1 00 0 nnnn tttt 1 PUWii ii iiii
+ // {ST,LD}RB Rt,[Rn,#+/-imm8] - 111 11 00 0 0 00 0 nnnn tttt 1 PUWii ii iiii
+ // {ST,LD}RB Rt,[Rn,Rm,lsl #imm2] - 111 11 00 0 0 00 0 nnnn tttt 0 00000 ii mmmm
ArmRegister Rn(instr, 16);
ArmRegister Rt(instr, 12);
- opcode << "strb";
- if ((instr & 0x800) != 0) {
+ opcode << (HasBitSet(instr, 20) ? "ldrb" : "strb");
+ if (HasBitSet(instr, 23)) {
+ uint32_t imm12 = instr & 0xFFF;
+ args << Rt << ", [" << Rn << ",#" << imm12 << "]";
+ } else if ((instr & 0x800) != 0) {
+ uint32_t imm8 = instr & 0xFF;
+ args << Rt << ", [" << Rn << ",#" << imm8 << "]";
+ } else {
+ uint32_t imm2 = (instr >> 4) & 3;
+ ArmRegister Rm(instr, 0);
+ args << Rt << ", [" << Rn << ", " << Rm;
+ if (imm2 != 0) {
+ args << ", " << "lsl #" << imm2;
+ }
+ args << "]";
+ }
+ break;
+ }
+ case 0x1: case 0x5: {
+ // STRH Rt,[Rn,#+/-imm12] - 111 11 00 0 1 01 0 nnnn tttt 1 PUWii ii iiii
+ // STRH Rt,[Rn,#+/-imm8] - 111 11 00 0 0 01 0 nnnn tttt 1 PUWii ii iiii
+ // STRH Rt,[Rn,Rm,lsl #imm2] - 111 11 00 0 0 01 0 nnnn tttt 0 00000 ii mmmm
+ ArmRegister Rn(instr, 16);
+ ArmRegister Rt(instr, 12);
+ opcode << "strh";
+ if (HasBitSet(instr, 23)) {
+ uint32_t imm12 = instr & 0xFFF;
+ args << Rt << ", [" << Rn << ",#" << imm12 << "]";
+ } else if ((instr & 0x800) != 0) {
uint32_t imm8 = instr & 0xFF;
args << Rt << ", [" << Rn << ",#" << imm8 << "]";
} else {
@@ -1351,8 +1379,8 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr)
break;
}
- case 0x03: case 0x0B: case 0x13: case 0x1B: { // 00xx011
- // Load halfword
+ case 0x03: case 0x0B: case 0x11: case 0x13: case 0x19: case 0x1B: { // 00xx011
+ // Load byte/halfword
// |111|11|10|0 0|00|0|0000|1111|110000|000000|
// |5 3|21|09|8 7|65|4|3 0|5 2|10 6|5 0|
// |---|--|--|---|--|-|----|----|------|------|
@@ -1380,8 +1408,9 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr)
}
} else if (op3 == 3) {
// LDRSH.W Rt, [Rn, #imm12] - 111 11 00 11 011 nnnn tttt iiiiiiiiiiii
+ // LDRSB.W Rt, [Rn, #imm12] - 111 11 00 11 001 nnnn tttt iiiiiiiiiiii
uint32_t imm12 = instr & 0xFFF;
- opcode << "ldrsh.w";
+ opcode << (HasBitSet(instr, 20) ? "ldrsb.w" : "ldrsh.w");
args << Rt << ", [" << Rn << ", #" << imm12 << "]";
if (Rn.r == 9) {
args << " ; ";
@@ -1575,6 +1604,9 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr)
opcode << it_conditions_.back();
it_conditions_.pop_back();
}
+ if (opcode.str().size() == 0) {
+ opcode << "UNKNOWN " << op2;
+ }
os << StringPrintf("%p: %08x\t%-7s ", instr_ptr, instr, opcode.str().c_str()) << args.str() << '\n';
return 4;
diff --git a/test/407-arrays/expected.txt b/test/407-arrays/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/407-arrays/expected.txt
diff --git a/test/407-arrays/info.txt b/test/407-arrays/info.txt
new file mode 100644
index 0000000..f30b667
--- /dev/null
+++ b/test/407-arrays/info.txt
@@ -0,0 +1 @@
+Simple tests for array accesses.
diff --git a/test/407-arrays/src/Main.java b/test/407-arrays/src/Main.java
new file mode 100644
index 0000000..5d27e6d
--- /dev/null
+++ b/test/407-arrays/src/Main.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+// Simple test for array accesses.
+
+public class Main extends TestCase {
+ public static void main(String[] args) {
+ $opt$testReads(new boolean[1], new byte[1], new char[1], new short[1],
+ new int[1], new Object[1], new long[1], 0);
+ $opt$testWrites(new boolean[2], new byte[2], new char[2], new short[2],
+ new int[2], new Object[2], new long[2], 1);
+ ensureThrows(new boolean[2], 2);
+ ensureThrows(new boolean[2], 4);
+ ensureThrows(new boolean[2], -1);
+ ensureThrows(new boolean[2], Integer.MIN_VALUE);
+ ensureThrows(new boolean[2], Integer.MAX_VALUE);
+ }
+
+ static void $opt$testReads(boolean[] bools, byte[] bytes, char[] chars, short[] shorts,
+ int[] ints, Object[] objects, long[] longs, int index) {
+ assertEquals(false, bools[0]);
+ assertEquals(false, bools[index]);
+
+ assertEquals(0, bytes[0]);
+ assertEquals(0, bytes[index]);
+
+ assertEquals(0, chars[0]);
+ assertEquals(0, chars[index]);
+
+ assertEquals(0, shorts[0]);
+ assertEquals(0, shorts[index]);
+
+ assertEquals(0, ints[0]);
+ assertEquals(0, ints[index]);
+
+ assertNull(objects[0]);
+ assertNull(objects[index]);
+
+ assertEquals(0, longs[0]);
+ assertEquals(0, longs[index]);
+ }
+
+ static void $opt$testWrites(boolean[] bools, byte[] bytes, char[] chars, short[] shorts,
+ int[] ints, Object[] objects, long[] longs, int index) {
+ bools[0] = true;
+ assertEquals(true, bools[0]);
+ bools[1] = true;
+ assertEquals(true, bools[index]);
+
+ bytes[0] = -4;
+ assertEquals(-4, bytes[0]);
+ bytes[index] = -8;
+ assertEquals(-8, bytes[index]);
+
+ chars[0] = 'c';
+ assertEquals('c', chars[0]);
+ chars[index] = 'd';
+ assertEquals('d', chars[index]);
+
+ shorts[0] = -42;
+ assertEquals(-42, shorts[0]);
+ shorts[index] = -84;
+ assertEquals(-84, shorts[index]);
+
+ ints[0] = -32;
+ assertEquals(-32, ints[0]);
+ ints[index] = -64;
+ assertEquals(-64, ints[index]);
+
+ Object o1 = new Object();
+ objects[0] = o1;
+ assertEquals(o1, objects[0]);
+ Object o2 = new Object();
+ objects[index] = o2;
+ assertEquals(o2, objects[index]);
+
+ long l = -21876876876876876L;
+ longs[0] = l;
+ assertEquals(l, longs[0]);
+ l = -21876876876876877L;
+ longs[index] = l;
+ assertEquals(l, longs[index]);
+ }
+
+ public static void ensureThrows(boolean[] array, int index) {
+ ArrayIndexOutOfBoundsException exception = null;
+ try {
+ $opt$doArrayLoad(array, index);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ exception = e;
+ }
+
+ assertNotNull(exception);
+ assertTrue(exception.toString().contains(Integer.toString(index)));
+
+ exception = null;
+ try {
+ $opt$doArrayStore(array, index);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ exception = e;
+ }
+
+ assertNotNull(exception);
+ assertTrue(exception.toString().contains(Integer.toString(index)));
+ }
+
+ public static void $opt$doArrayLoad(boolean[] array, int index) {
+ boolean res = array[index];
+ }
+
+ public static void $opt$doArrayStore(boolean[] array, int index) {
+ array[index] = false;
+ }
+}
diff --git a/test/407-arrays/src/TestCase.java b/test/407-arrays/src/TestCase.java
new file mode 100644
index 0000000..ef77f71
--- /dev/null
+++ b/test/407-arrays/src/TestCase.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2014 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 superclass for test cases.
+ */
+
+import java.util.Arrays;
+
+public abstract class TestCase {
+ public static void assertSame(Object expected, Object value) {
+ if (expected != value) {
+ throw new AssertionError("Objects are not the same: expected " +
+ String.valueOf(expected) + ", got " + String.valueOf(value));
+ }
+ }
+
+ public static void assertNotSame(Object expected, Object value) {
+ if (expected == value) {
+ throw new AssertionError(
+ "Objects are the same: " + String.valueOf(expected));
+ }
+ }
+
+ public static void assertEquals(String message, int expected, int actual) {
+ if (expected != actual) {
+ throw new AssertionError(message);
+ }
+ }
+
+ public static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new AssertionError("Expected " + expected + " got " + actual);
+ }
+ }
+
+ public static void assertTrue(String message, boolean condition) {
+ if (!condition) {
+ throw new AssertionError(message);
+ }
+ }
+
+ public static void assertTrue(boolean condition) {
+ assertTrue("Expected true", condition);
+ }
+
+ public static void assertFalse(String message, boolean condition) {
+ if (condition) {
+ throw new AssertionError(message);
+ }
+ }
+
+ public static void assertFalse(boolean condition) {
+ assertFalse("Expected false", condition);
+ }
+
+ public static void assertEquals(Object expected, Object actual) {
+ if (!expected.equals(actual)) {
+ String msg = "Expected \"" + expected + "\" but got \"" + actual + "\"";
+ throw new AssertionError(msg);
+ }
+ }
+
+ public static void assertNotEquals(int expected, int actual) {
+ if (expected == actual) {
+ throw new AssertionError("Expected " + expected + " got " + actual);
+ }
+ }
+
+ public static void assertNotEquals(Object expected, Object actual) {
+ if (expected.equals(actual)) {
+ String msg = "Objects are the same: " + String.valueOf(expected);
+ throw new AssertionError(msg);
+ }
+ }
+
+ public static <T> void assertArrayEquals(T[] actual, T... expected) {
+ assertTrue(Arrays.equals(expected, actual));
+ }
+
+ public static void assertEquals(
+ String message, Object expected, Object actual) {
+ if (!expected.equals(actual)) {
+ throw new AssertionError(message);
+ }
+ }
+
+ public static void assertEquals(
+ String message, long expected, long actual) {
+ if (expected != actual) {
+ throw new AssertionError(message);
+ }
+ }
+
+ public static void assertEquals(long expected, long actual) {
+ if (expected != actual) {
+ throw new AssertionError("Expected " + expected + " got " + actual);
+ }
+ }
+
+ public static void assertEquals(
+ String message, boolean expected, boolean actual) {
+ if (expected != actual) {
+ throw new AssertionError(message);
+ }
+ }
+
+ public static void assertEquals(boolean expected, boolean actual) {
+ if (expected != actual) {
+ throw new AssertionError("Expected " + expected + " got " + actual);
+ }
+ }
+
+ public static void assertEquals(
+ String message, float expected, float actual) {
+ if (expected != actual) {
+ throw new AssertionError(message);
+ }
+ }
+
+ public static void assertEquals(float expected, float actual) {
+ if (expected != actual) {
+ throw new AssertionError("Expected " + expected + " got " + actual);
+ }
+ }
+
+ public static void assertEquals(float expected, float actual,
+ float tolerance) {
+ if ((actual < expected - tolerance) || (expected + tolerance < actual)) {
+ throw new AssertionError("Expected " + expected + " got " + actual +
+ " tolerance " + tolerance);
+ }
+ }
+
+ public static void assertEquals(
+ String message, double expected, double actual) {
+ if (expected != actual) {
+ throw new AssertionError(message);
+ }
+ }
+
+ public static void assertEquals(double expected, double actual) {
+ if (expected != actual) {
+ throw new AssertionError("Expected " + expected + " got " + actual);
+ }
+ }
+
+ public static void assertEquals(double expected, double actual,
+ double tolerance) {
+ if ((actual < expected - tolerance) || (expected + tolerance < actual)) {
+ throw new AssertionError("Expected " + expected + " got " + actual +
+ " tolerance " + tolerance);
+ }
+ }
+
+ public static void assertSame(
+ String message, Object expected, Object actual) {
+ if (expected != actual) {
+ throw new AssertionError(message);
+ }
+ }
+
+ public static void assertNull(String message, Object object) {
+ if (object != null) {
+ throw new AssertionError(message);
+ }
+ }
+
+ public static void assertNull(Object object) {
+ assertNull("Expected null", object);
+ }
+
+ public static void assertNotNull(String message, Object object) {
+ if (object == null) {
+ throw new AssertionError(message);
+ }
+ }
+
+ public static void assertNotNull(Object object) {
+ assertNotNull("Expected non-null", object);
+ }
+
+ public static void fail(String msg) {
+ throw new AssertionError(msg);
+ }
+}