summaryrefslogtreecommitdiff
path: root/compiler/utils/x86
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/utils/x86')
-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
3 files changed, 146 insertions, 0 deletions
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