ARM: VIXL32: Fix PackedSwitch.
Fix the case when the jumptable is very far and offset doesn't
fit ADR offset field.
Test: export ART_USE_VIXL_ARM_BACKEND=true && \
mma test-art-host dist && mma test-art-target dist
Change-Id: I2b8ac04eb343947f9a9301f3ce6285794ddad4b8
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 3df55ae..61e6b4b 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -680,6 +680,15 @@
CodeBufferCheckScope::kMaximumSize);
// TODO(VIXL): Check that using lower case bind is fine here.
codegen->GetVIXLAssembler()->bind(&table_start_);
+ for (uint32_t i = 0; i < num_entries; i++) {
+ codegen->GetVIXLAssembler()->place(bb_addresses_[i].get());
+ }
+}
+
+void JumpTableARMVIXL::FixTable(CodeGeneratorARMVIXL* codegen) {
+ uint32_t num_entries = switch_instr_->GetNumEntries();
+ DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
+
const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors();
for (uint32_t i = 0; i < num_entries; i++) {
vixl32::Label* target_label = codegen->GetLabelOf(successors[i]);
@@ -691,21 +700,21 @@
}
DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min());
DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max());
- vixl32::Literal<int32_t> literal(jump_offset);
- codegen->GetVIXLAssembler()->place(&literal);
+
+ bb_addresses_[i].get()->UpdateValue(jump_offset, &codegen->GetVIXLAssembler()->GetBuffer());
}
}
-void CodeGeneratorARMVIXL::EmitJumpTables() {
+void CodeGeneratorARMVIXL::FixJumpTables() {
for (auto&& jump_table : jump_tables_) {
- jump_table->EmitTable(this);
+ jump_table->FixTable(this);
}
}
#define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()-> // NOLINT
void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) {
- EmitJumpTables();
+ FixJumpTables();
GetAssembler()->FinalizeCode();
CodeGenerator::Finalize(allocator);
}
@@ -6113,6 +6122,8 @@
vixl32::Register target_address = table_base;
__ Add(target_address, table_base, jump_offset);
__ Bx(target_address);
+
+ jump_table->EmitTable(codegen_);
}
}
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index e8bc2a9..302ee38 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -194,16 +194,28 @@
class JumpTableARMVIXL : public DeletableArenaObject<kArenaAllocSwitchTable> {
public:
+ typedef vixl::aarch32::Literal<int32_t> IntLiteral;
+
explicit JumpTableARMVIXL(HPackedSwitch* switch_instr)
- : switch_instr_(switch_instr), table_start_() {}
+ : switch_instr_(switch_instr),
+ table_start_(),
+ bb_addresses_(switch_instr->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
+ uint32_t num_entries = switch_instr_->GetNumEntries();
+ for (uint32_t i = 0; i < num_entries; i++) {
+ IntLiteral *lit = new IntLiteral(0);
+ bb_addresses_.emplace_back(lit);
+ }
+ }
vixl::aarch32::Label* GetTableStartLabel() { return &table_start_; }
void EmitTable(CodeGeneratorARMVIXL* codegen);
+ void FixTable(CodeGeneratorARMVIXL* codegen);
private:
HPackedSwitch* const switch_instr_;
vixl::aarch32::Label table_start_;
+ ArenaVector<std::unique_ptr<IntLiteral>> bb_addresses_;
DISALLOW_COPY_AND_ASSIGN(JumpTableARMVIXL);
};
@@ -513,7 +525,7 @@
HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; }
- void EmitJumpTables();
+ void FixJumpTables();
void GenerateMemoryBarrier(MemBarrierKind kind);
void Finalize(CodeAllocator* allocator) OVERRIDE;
void SetupBlockedRegisters() const OVERRIDE;
diff --git a/test/015-switch/src/Main.java b/test/015-switch/src/Main.java
index 2a7995a..2b724a1 100644
--- a/test/015-switch/src/Main.java
+++ b/test/015-switch/src/Main.java
@@ -113,7 +113,7 @@
}
// Long packed-switch that might lead to not creating chained-ifs.
- public static void packedSwitch7(int value) {
+ public static long packedSwitch7(int value) {
switch (value) {
case 1:
System.out.println(1); break;
@@ -148,6 +148,113 @@
default:
System.out.println("default"); break;
}
+
+ // Jump tables previously were emitted in the end of the method code buffer. The
+ // following boilerplate code aims to fill the emitted code buffer extensively
+ // and check that even for big method jump table is correctly emitted, its address
+ // is within a range of corresponded pc-relative instructions (this applies to
+ // ARM mainly).
+ long temp = value;
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+ temp = Long.rotateLeft(temp, value);
+
+ return temp;
}
// Sparse switch, just leave a gap.