summaryrefslogtreecommitdiff
path: root/compiler/utils
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/utils')
-rw-r--r--compiler/utils/assembler.h4
-rw-r--r--compiler/utils/x86/assembler_x86.cc78
-rw-r--r--compiler/utils/x86/assembler_x86.h29
-rw-r--r--compiler/utils/x86/assembler_x86_test.cc39
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.cc78
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.h29
-rw-r--r--compiler/utils/x86_64/assembler_x86_64_test.cc41
7 files changed, 298 insertions, 0 deletions
diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h
index 3097cd55c0..64d76b881d 100644
--- a/compiler/utils/assembler.h
+++ b/compiler/utils/assembler.h
@@ -53,9 +53,11 @@ namespace mips64 {
}
namespace x86 {
class X86Assembler;
+ class NearLabel;
}
namespace x86_64 {
class X86_64Assembler;
+ class NearLabel;
}
class ExternalLabel {
@@ -126,7 +128,9 @@ class Label {
friend class mips::MipsAssembler;
friend class mips64::Mips64Assembler;
friend class x86::X86Assembler;
+ friend class x86::NearLabel;
friend class x86_64::X86_64Assembler;
+ friend class x86_64::NearLabel;
DISALLOW_COPY_AND_ASSIGN(Label);
};
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index 9b3d792903..a03f857e88 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -1510,6 +1510,38 @@ void X86Assembler::j(Condition condition, Label* label) {
}
+void X86Assembler::j(Condition condition, NearLabel* label) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ if (label->IsBound()) {
+ static const int kShortSize = 2;
+ int offset = label->Position() - buffer_.Size();
+ CHECK_LE(offset, 0);
+ CHECK(IsInt<8>(offset - kShortSize));
+ EmitUint8(0x70 + condition);
+ EmitUint8((offset - kShortSize) & 0xFF);
+ } else {
+ EmitUint8(0x70 + condition);
+ EmitLabelLink(label);
+ }
+}
+
+
+void X86Assembler::jecxz(NearLabel* label) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ if (label->IsBound()) {
+ static const int kShortSize = 2;
+ int offset = label->Position() - buffer_.Size();
+ CHECK_LE(offset, 0);
+ CHECK(IsInt<8>(offset - kShortSize));
+ EmitUint8(0xE3);
+ EmitUint8((offset - kShortSize) & 0xFF);
+ } else {
+ EmitUint8(0xE3);
+ EmitLabelLink(label);
+ }
+}
+
+
void X86Assembler::jmp(Register reg) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xFF);
@@ -1543,6 +1575,22 @@ void X86Assembler::jmp(Label* label) {
}
+void X86Assembler::jmp(NearLabel* label) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ if (label->IsBound()) {
+ static const int kShortSize = 2;
+ int offset = label->Position() - buffer_.Size();
+ CHECK_LE(offset, 0);
+ CHECK(IsInt<8>(offset - kShortSize));
+ EmitUint8(0xEB);
+ EmitUint8((offset - kShortSize) & 0xFF);
+ } else {
+ EmitUint8(0xEB);
+ EmitLabelLink(label);
+ }
+}
+
+
void X86Assembler::repne_scasw() {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
@@ -1675,6 +1723,21 @@ void X86Assembler::Bind(Label* label) {
}
+void X86Assembler::Bind(NearLabel* label) {
+ int bound = buffer_.Size();
+ CHECK(!label->IsBound()); // Labels can only be bound once.
+ while (label->IsLinked()) {
+ int position = label->LinkPosition();
+ uint8_t delta = buffer_.Load<uint8_t>(position);
+ int offset = bound - (position + 1);
+ CHECK(IsInt<8>(offset));
+ buffer_.Store<int8_t>(position, offset);
+ label->position_ = delta != 0u ? label->position_ - delta : 0;
+ }
+ label->BindTo(bound);
+}
+
+
void X86Assembler::EmitOperand(int reg_or_opcode, const Operand& operand) {
CHECK_GE(reg_or_opcode, 0);
CHECK_LT(reg_or_opcode, 8);
@@ -1736,6 +1799,21 @@ void X86Assembler::EmitLabelLink(Label* label) {
}
+void X86Assembler::EmitLabelLink(NearLabel* label) {
+ CHECK(!label->IsBound());
+ int position = buffer_.Size();
+ if (label->IsLinked()) {
+ // Save the delta in the byte that we have to play with.
+ uint32_t delta = position - label->LinkPosition();
+ CHECK(IsUint<8>(delta));
+ EmitUint8(delta & 0xFF);
+ } else {
+ EmitUint8(0);
+ }
+ label->LinkTo(position);
+}
+
+
void X86Assembler::EmitGenericShift(int reg_or_opcode,
const Operand& operand,
const Immediate& imm) {
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index a9227f38b0..0c90f2801a 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -203,6 +203,30 @@ class Address : public Operand {
};
+// This is equivalent to the Label class, used in a slightly different context. We
+// inherit the functionality of the Label class, but prevent unintended
+// derived-to-base conversions by making the base class private.
+class NearLabel : private Label {
+ public:
+ NearLabel() : Label() {}
+
+ // Expose the Label routines that we need.
+ using Label::Position;
+ using Label::LinkPosition;
+ using Label::IsBound;
+ using Label::IsUnused;
+ using Label::IsLinked;
+
+ private:
+ using Label::BindTo;
+ using Label::LinkTo;
+
+ friend class x86::X86Assembler;
+
+ DISALLOW_COPY_AND_ASSIGN(NearLabel);
+};
+
+
class X86Assembler FINAL : public Assembler {
public:
X86Assembler() {}
@@ -464,10 +488,13 @@ class X86Assembler FINAL : public Assembler {
void hlt();
void j(Condition condition, Label* label);
+ void j(Condition condition, NearLabel* label);
+ void jecxz(NearLabel* label);
void jmp(Register reg);
void jmp(const Address& address);
void jmp(Label* label);
+ void jmp(NearLabel* label);
void repne_scasw();
void repe_cmpsw();
@@ -506,6 +533,7 @@ class X86Assembler FINAL : public Assembler {
int PreferredLoopAlignment() { return 16; }
void Align(int alignment, int offset);
void Bind(Label* label);
+ void Bind(NearLabel* label);
//
// Overridden common assembler high-level functionality
@@ -652,6 +680,7 @@ class X86Assembler FINAL : public Assembler {
void EmitComplex(int rm, const Operand& operand, const Immediate& immediate);
void EmitLabel(Label* label, int instruction_size);
void EmitLabelLink(Label* label);
+ void EmitLabelLink(NearLabel* label);
void EmitGenericShift(int rm, const Operand& operand, const Immediate& imm);
void EmitGenericShift(int rm, const Operand& operand, Register shifter);
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index 731b5f4ac5..9ac54afc11 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -243,4 +243,43 @@ TEST_F(AssemblerX86Test, BsrlAddress) {
DriverStr(expected, "bsrl_address");
}
+/////////////////
+// Near labels //
+/////////////////
+
+TEST_F(AssemblerX86Test, Jecxz) {
+ x86::NearLabel target;
+ GetAssembler()->jecxz(&target);
+ GetAssembler()->addl(x86::EDI, x86::Address(x86::ESP, 4));
+ GetAssembler()->Bind(&target);
+ const char* expected =
+ "jecxz 1f\n"
+ "addl 4(%ESP),%EDI\n"
+ "1:\n";
+
+ DriverStr(expected, "jecxz");
+}
+
+TEST_F(AssemblerX86Test, NearLabel) {
+ // Test both forward and backward branches.
+ x86::NearLabel start, target;
+ GetAssembler()->Bind(&start);
+ GetAssembler()->j(x86::kEqual, &target);
+ GetAssembler()->jmp(&target);
+ GetAssembler()->jecxz(&target);
+ GetAssembler()->addl(x86::EDI, x86::Address(x86::ESP, 4));
+ GetAssembler()->Bind(&target);
+ GetAssembler()->j(x86::kNotEqual, &start);
+ GetAssembler()->jmp(&start);
+ const char* expected =
+ "1: je 2f\n"
+ "jmp 2f\n"
+ "jecxz 2f\n"
+ "addl 4(%ESP),%EDI\n"
+ "2: jne 1b\n"
+ "jmp 1b\n";
+
+ DriverStr(expected, "near_label");
+}
+
} // namespace art
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index dc61c992e0..88ea9900fe 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -1971,6 +1971,38 @@ void X86_64Assembler::j(Condition condition, Label* label) {
}
+void X86_64Assembler::j(Condition condition, NearLabel* label) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ if (label->IsBound()) {
+ static const int kShortSize = 2;
+ int offset = label->Position() - buffer_.Size();
+ CHECK_LE(offset, 0);
+ CHECK(IsInt<8>(offset - kShortSize));
+ EmitUint8(0x70 + condition);
+ EmitUint8((offset - kShortSize) & 0xFF);
+ } else {
+ EmitUint8(0x70 + condition);
+ EmitLabelLink(label);
+ }
+}
+
+
+void X86_64Assembler::jrcxz(NearLabel* label) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ if (label->IsBound()) {
+ static const int kShortSize = 2;
+ int offset = label->Position() - buffer_.Size();
+ CHECK_LE(offset, 0);
+ CHECK(IsInt<8>(offset - kShortSize));
+ EmitUint8(0xE3);
+ EmitUint8((offset - kShortSize) & 0xFF);
+ } else {
+ EmitUint8(0xE3);
+ EmitLabelLink(label);
+ }
+}
+
+
void X86_64Assembler::jmp(CpuRegister reg) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitOptionalRex32(reg);
@@ -2006,6 +2038,22 @@ void X86_64Assembler::jmp(Label* label) {
}
+void X86_64Assembler::jmp(NearLabel* label) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ if (label->IsBound()) {
+ static const int kShortSize = 2;
+ int offset = label->Position() - buffer_.Size();
+ CHECK_LE(offset, 0);
+ CHECK(IsInt<8>(offset - kShortSize));
+ EmitUint8(0xEB);
+ EmitUint8((offset - kShortSize) & 0xFF);
+ } else {
+ EmitUint8(0xEB);
+ EmitLabelLink(label);
+ }
+}
+
+
void X86_64Assembler::rep_movsw() {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
@@ -2187,6 +2235,21 @@ void X86_64Assembler::Bind(Label* label) {
}
+void X86_64Assembler::Bind(NearLabel* label) {
+ int bound = buffer_.Size();
+ CHECK(!label->IsBound()); // Labels can only be bound once.
+ while (label->IsLinked()) {
+ int position = label->LinkPosition();
+ uint8_t delta = buffer_.Load<uint8_t>(position);
+ int offset = bound - (position + 1);
+ CHECK(IsInt<8>(offset));
+ buffer_.Store<int8_t>(position, offset);
+ label->position_ = delta != 0u ? label->position_ - delta : 0;
+ }
+ label->BindTo(bound);
+}
+
+
void X86_64Assembler::EmitOperand(uint8_t reg_or_opcode, const Operand& operand) {
CHECK_GE(reg_or_opcode, 0);
CHECK_LT(reg_or_opcode, 8);
@@ -2256,6 +2319,21 @@ void X86_64Assembler::EmitLabelLink(Label* label) {
}
+void X86_64Assembler::EmitLabelLink(NearLabel* label) {
+ CHECK(!label->IsBound());
+ int position = buffer_.Size();
+ if (label->IsLinked()) {
+ // Save the delta in the byte that we have to play with.
+ uint32_t delta = position - label->LinkPosition();
+ CHECK(IsUint<8>(delta));
+ EmitUint8(delta & 0xFF);
+ } else {
+ EmitUint8(0);
+ }
+ label->LinkTo(position);
+}
+
+
void X86_64Assembler::EmitGenericShift(bool wide,
int reg_or_opcode,
CpuRegister reg,
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index da42213048..c38aba5e91 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -302,6 +302,30 @@ class ConstantArea {
};
+// This is equivalent to the Label class, used in a slightly different context. We
+// inherit the functionality of the Label class, but prevent unintended
+// derived-to-base conversions by making the base class private.
+class NearLabel : private Label {
+ public:
+ NearLabel() : Label() {}
+
+ // Expose the Label routines that we need.
+ using Label::Position;
+ using Label::LinkPosition;
+ using Label::IsBound;
+ using Label::IsUnused;
+ using Label::IsLinked;
+
+ private:
+ using Label::BindTo;
+ using Label::LinkTo;
+
+ friend class x86_64::X86_64Assembler;
+
+ DISALLOW_COPY_AND_ASSIGN(NearLabel);
+};
+
+
class X86_64Assembler FINAL : public Assembler {
public:
X86_64Assembler() {}
@@ -588,10 +612,13 @@ class X86_64Assembler FINAL : public Assembler {
void hlt();
void j(Condition condition, Label* label);
+ void j(Condition condition, NearLabel* label);
+ void jrcxz(NearLabel* label);
void jmp(CpuRegister reg);
void jmp(const Address& address);
void jmp(Label* label);
+ void jmp(NearLabel* label);
X86_64Assembler* lock();
void cmpxchgl(const Address& address, CpuRegister reg);
@@ -639,6 +666,7 @@ class X86_64Assembler FINAL : public Assembler {
int PreferredLoopAlignment() { return 16; }
void Align(int alignment, int offset);
void Bind(Label* label);
+ void Bind(NearLabel* label);
//
// Overridden common assembler high-level functionality
@@ -809,6 +837,7 @@ class X86_64Assembler FINAL : public Assembler {
void EmitComplex(uint8_t rm, const Operand& operand, const Immediate& immediate);
void EmitLabel(Label* label, int instruction_size);
void EmitLabelLink(Label* label);
+ void EmitLabelLink(NearLabel* label);
void EmitGenericShift(bool wide, int rm, CpuRegister reg, const Immediate& imm);
void EmitGenericShift(bool wide, int rm, CpuRegister operand, CpuRegister shifter);
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index 8673f039ed..9e64b47c92 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -1179,6 +1179,47 @@ TEST_F(AssemblerX86_64Test, BsrqAddress) {
DriverStr(expected, "bsrq_address");
}
+/////////////////
+// Near labels //
+/////////////////
+
+TEST_F(AssemblerX86_64Test, Jrcxz) {
+ x86_64::NearLabel target;
+ GetAssembler()->jrcxz(&target);
+ GetAssembler()->addl(x86_64::CpuRegister(x86_64::RDI),
+ x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4));
+ GetAssembler()->Bind(&target);
+ const char* expected =
+ "jrcxz 1f\n"
+ "addl 4(%RSP),%EDI\n"
+ "1:\n";
+
+ DriverStr(expected, "jrcxz");
+}
+
+TEST_F(AssemblerX86_64Test, NearLabel) {
+ // Test both forward and backward branches.
+ x86_64::NearLabel start, target;
+ GetAssembler()->Bind(&start);
+ GetAssembler()->j(x86_64::kEqual, &target);
+ GetAssembler()->jmp(&target);
+ GetAssembler()->jrcxz(&target);
+ GetAssembler()->addl(x86_64::CpuRegister(x86_64::RDI),
+ x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4));
+ GetAssembler()->Bind(&target);
+ GetAssembler()->j(x86_64::kNotEqual, &start);
+ GetAssembler()->jmp(&start);
+ const char* expected =
+ "1: je 2f\n"
+ "jmp 2f\n"
+ "jrcxz 2f\n"
+ "addl 4(%RSP),%EDI\n"
+ "2: jne 1b\n"
+ "jmp 1b\n";
+
+ DriverStr(expected, "near_label");
+}
+
std::string setcc_test_fn(AssemblerX86_64Test::Base* assembler_test,
x86_64::X86_64Assembler* assembler) {
// From Condition