x86 JNI compiler and unit tests.
Change-Id: I4c2e10328961a2e8e27c90777fe2a93737b21143
diff --git a/src/assembler_arm.cc b/src/assembler_arm.cc
index 3ce5199..91c259e 100644
--- a/src/assembler_arm.cc
+++ b/src/assembler_arm.cc
@@ -2,6 +2,7 @@
#include "src/assembler.h"
#include "src/logging.h"
+#include "src/utils.h"
namespace art {
@@ -69,7 +70,7 @@
if (rhs >= R0 && rhs <= PC) {
os << kRegisterNames[rhs];
} else {
- os << "Register[" << int(rhs) << "]";
+ os << "Register[" << static_cast<int>(rhs) << "]";
}
return os;
}
@@ -77,9 +78,9 @@
std::ostream& operator<<(std::ostream& os, const SRegister& rhs) {
if (rhs >= S0 && rhs < kNumberOfSRegisters) {
- os << "s" << int(rhs);
+ os << "s" << static_cast<int>(rhs);
} else {
- os << "SRegister[" << int(rhs) << "]";
+ os << "SRegister[" << static_cast<int>(rhs) << "]";
}
return os;
}
@@ -87,22 +88,23 @@
std::ostream& operator<<(std::ostream& os, const DRegister& rhs) {
if (rhs >= D0 && rhs < kNumberOfDRegisters) {
- os << "d" << int(rhs);
+ os << "d" << static_cast<int>(rhs);
} else {
- os << "DRegister[" << int(rhs) << "]";
+ os << "DRegister[" << static_cast<int>(rhs) << "]";
}
return os;
}
static const char* kConditionNames[] = {
- "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC", "HI", "LS", "GE", "LT", "GT", "LE", "AL",
+ "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC", "HI", "LS", "GE", "LT", "GT",
+ "LE", "AL",
};
std::ostream& operator<<(std::ostream& os, const Condition& rhs) {
if (rhs >= EQ && rhs <= AL) {
os << kConditionNames[rhs];
} else {
- os << "Condition[" << int(rhs) << "]";
+ os << "Condition[" << static_cast<int>(rhs) << "]";
}
return os;
}
@@ -1076,6 +1078,7 @@
tst(R0, ShifterOperand(data), MI);
}
+
int32_t Assembler::EncodeBranchOffset(int offset, int32_t inst) {
// The offset is off by 8 due to the way the ARM CPUs read PC.
offset -= 8;
@@ -1094,4 +1097,415 @@
return ((((inst & kBranchOffsetMask) << 8) >> 6) + 8);
}
+void Assembler::AddConstant(Register rd, int32_t value, Condition cond) {
+ AddConstant(rd, rd, value, cond);
+}
+
+
+void Assembler::AddConstant(Register rd, Register rn, int32_t value,
+ Condition cond) {
+ if (value == 0) {
+ if (rd != rn) {
+ mov(rd, ShifterOperand(rn), cond);
+ }
+ return;
+ }
+ // We prefer to select the shorter code sequence rather than selecting add for
+ // positive values and sub for negatives ones, which would slightly improve
+ // the readability of generated code for some constants.
+ ShifterOperand shifter_op;
+ if (ShifterOperand::CanHold(value, &shifter_op)) {
+ add(rd, rn, shifter_op, cond);
+ } else if (ShifterOperand::CanHold(-value, &shifter_op)) {
+ sub(rd, rn, shifter_op, cond);
+ } else {
+ CHECK(rn != IP);
+ if (ShifterOperand::CanHold(~value, &shifter_op)) {
+ mvn(IP, shifter_op, cond);
+ add(rd, rn, ShifterOperand(IP), cond);
+ } else if (ShifterOperand::CanHold(~(-value), &shifter_op)) {
+ mvn(IP, shifter_op, cond);
+ sub(rd, rn, ShifterOperand(IP), cond);
+ } else {
+ movw(IP, Low16Bits(value), cond);
+ uint16_t value_high = High16Bits(value);
+ if (value_high != 0) {
+ movt(IP, value_high, cond);
+ }
+ add(rd, rn, ShifterOperand(IP), cond);
+ }
+ }
+}
+
+
+void Assembler::AddConstantSetFlags(Register rd, Register rn, int32_t value,
+ Condition cond) {
+ ShifterOperand shifter_op;
+ if (ShifterOperand::CanHold(value, &shifter_op)) {
+ adds(rd, rn, shifter_op, cond);
+ } else if (ShifterOperand::CanHold(-value, &shifter_op)) {
+ subs(rd, rn, shifter_op, cond);
+ } else {
+ CHECK(rn != IP);
+ if (ShifterOperand::CanHold(~value, &shifter_op)) {
+ mvn(IP, shifter_op, cond);
+ adds(rd, rn, ShifterOperand(IP), cond);
+ } else if (ShifterOperand::CanHold(~(-value), &shifter_op)) {
+ mvn(IP, shifter_op, cond);
+ subs(rd, rn, ShifterOperand(IP), cond);
+ } else {
+ movw(IP, Low16Bits(value), cond);
+ uint16_t value_high = High16Bits(value);
+ if (value_high != 0) {
+ movt(IP, value_high, cond);
+ }
+ adds(rd, rn, ShifterOperand(IP), cond);
+ }
+ }
+}
+
+
+void Assembler::LoadImmediate(Register rd, int32_t value, Condition cond) {
+ ShifterOperand shifter_op;
+ if (ShifterOperand::CanHold(value, &shifter_op)) {
+ mov(rd, shifter_op, cond);
+ } else if (ShifterOperand::CanHold(~value, &shifter_op)) {
+ mvn(rd, shifter_op, cond);
+ } else {
+ movw(rd, Low16Bits(value), cond);
+ uint16_t value_high = High16Bits(value);
+ if (value_high != 0) {
+ movt(rd, value_high, cond);
+ }
+ }
+}
+
+
+bool Address::CanHoldLoadOffset(LoadOperandType type, int offset) {
+ switch (type) {
+ case kLoadSignedByte:
+ case kLoadSignedHalfword:
+ case kLoadUnsignedHalfword:
+ case kLoadWordPair:
+ return IsAbsoluteUint(8, offset); // Addressing mode 3.
+ case kLoadUnsignedByte:
+ case kLoadWord:
+ return IsAbsoluteUint(12, offset); // Addressing mode 2.
+ case kLoadSWord:
+ case kLoadDWord:
+ return IsAbsoluteUint(10, offset); // VFP addressing mode.
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ return false;
+ }
+}
+
+
+bool Address::CanHoldStoreOffset(StoreOperandType type, int offset) {
+ switch (type) {
+ case kStoreHalfword:
+ case kStoreWordPair:
+ return IsAbsoluteUint(8, offset); // Addressing mode 3.
+ case kStoreByte:
+ case kStoreWord:
+ return IsAbsoluteUint(12, offset); // Addressing mode 2.
+ case kStoreSWord:
+ case kStoreDWord:
+ return IsAbsoluteUint(10, offset); // VFP addressing mode.
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ return false;
+ }
+}
+
+
+// Implementation note: this method must emit at most one instruction when
+// Address::CanHoldLoadOffset.
+void Assembler::LoadFromOffset(LoadOperandType type,
+ Register reg,
+ Register base,
+ int32_t offset,
+ Condition cond) {
+ if (!Address::CanHoldLoadOffset(type, offset)) {
+ CHECK(base != IP);
+ LoadImmediate(IP, offset, cond);
+ add(IP, IP, ShifterOperand(base), cond);
+ base = IP;
+ offset = 0;
+ }
+ CHECK(Address::CanHoldLoadOffset(type, offset));
+ switch (type) {
+ case kLoadSignedByte:
+ ldrsb(reg, Address(base, offset), cond);
+ break;
+ case kLoadUnsignedByte:
+ ldrb(reg, Address(base, offset), cond);
+ break;
+ case kLoadSignedHalfword:
+ ldrsh(reg, Address(base, offset), cond);
+ break;
+ case kLoadUnsignedHalfword:
+ ldrh(reg, Address(base, offset), cond);
+ break;
+ case kLoadWord:
+ ldr(reg, Address(base, offset), cond);
+ break;
+ case kLoadWordPair:
+ ldrd(reg, Address(base, offset), cond);
+ break;
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ }
+}
+
+
+// Implementation note: this method must emit at most one instruction when
+// Address::CanHoldStoreOffset.
+void Assembler::StoreToOffset(StoreOperandType type,
+ Register reg,
+ Register base,
+ int32_t offset,
+ Condition cond) {
+ if (!Address::CanHoldStoreOffset(type, offset)) {
+ CHECK(reg != IP);
+ CHECK(base != IP);
+ LoadImmediate(IP, offset, cond);
+ add(IP, IP, ShifterOperand(base), cond);
+ base = IP;
+ offset = 0;
+ }
+ CHECK(Address::CanHoldStoreOffset(type, offset));
+ switch (type) {
+ case kStoreByte:
+ strb(reg, Address(base, offset), cond);
+ break;
+ case kStoreHalfword:
+ strh(reg, Address(base, offset), cond);
+ break;
+ case kStoreWord:
+ str(reg, Address(base, offset), cond);
+ break;
+ case kStoreWordPair:
+ strd(reg, Address(base, offset), cond);
+ break;
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ }
+}
+
+// Emit code that will create an activation on the stack
+void Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg) {
+ CHECK(IsAligned(frame_size, 16));
+ // TODO: use stm/ldm
+ StoreToOffset(kStoreWord, LR, SP, 0);
+ StoreToOffset(kStoreWord, method_reg.AsCoreRegister(), SP, -4);
+ AddConstant(SP, -frame_size);
+}
+
+// Emit code that will remove an activation from the stack
+void Assembler::RemoveFrame(size_t frame_size) {
+ CHECK(IsAligned(frame_size, 16));
+ LoadFromOffset(kLoadWord, LR, SP, 0);
+ AddConstant(SP, frame_size);
+ mov(PC, ShifterOperand(LR));
+}
+
+void Assembler::IncreaseFrameSize(size_t adjust) {
+ CHECK(IsAligned(adjust, 16));
+ AddConstant(SP, -adjust);
+}
+
+void Assembler::DecreaseFrameSize(size_t adjust) {
+ CHECK(IsAligned(adjust, 16));
+ AddConstant(SP, adjust);
+}
+
+// Store bytes from the given register onto the stack
+void Assembler::Store(FrameOffset dest, ManagedRegister src, size_t size) {
+ if (src.IsCoreRegister()) {
+ CHECK_EQ(4u, size);
+ StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
+ } else {
+ // VFP
+ LOG(FATAL) << "TODO";
+ }
+}
+
+void Assembler::StoreRef(FrameOffset dest, ManagedRegister src) {
+ CHECK(src.IsCoreRegister());
+ StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
+}
+
+void Assembler::CopyRef(FrameOffset dest, FrameOffset src,
+ ManagedRegister scratch) {
+ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
+ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
+}
+
+void Assembler::LoadRef(ManagedRegister dest, ManagedRegister base,
+ MemberOffset offs) {
+ CHECK(dest.IsCoreRegister() && dest.IsCoreRegister());
+ LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
+ base.AsCoreRegister(), offs.Int32Value());
+}
+
+void Assembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm,
+ ManagedRegister scratch) {
+ CHECK(scratch.IsCoreRegister());
+ LoadImmediate(scratch.AsCoreRegister(), imm);
+ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
+}
+
+void Assembler::StoreImmediateToThread(ThreadOffset dest, uint32_t imm,
+ ManagedRegister scratch) {
+ CHECK(scratch.IsCoreRegister());
+ LoadImmediate(scratch.AsCoreRegister(), imm);
+ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), TR, dest.Int32Value());
+}
+
+void Assembler::Load(ManagedRegister dest, FrameOffset src, size_t size) {
+ if (dest.IsCoreRegister()) {
+ CHECK_EQ(4u, size);
+ LoadFromOffset(kLoadWord, dest.AsCoreRegister(), SP, src.Int32Value());
+ } else {
+ // TODO: VFP
+ LOG(FATAL) << "Unimplemented";
+ }
+}
+
+void Assembler::LoadRawPtrFromThread(ManagedRegister dest, ThreadOffset offs) {
+ CHECK(dest.IsCoreRegister());
+ LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
+ TR, offs.Int32Value());
+}
+
+void Assembler::CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset thr_offs,
+ ManagedRegister scratch) {
+ CHECK(scratch.IsCoreRegister());
+ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
+ TR, thr_offs.Int32Value());
+ StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
+ SP, fr_offs.Int32Value());
+}
+
+void Assembler::CopyRawPtrToThread(ThreadOffset thr_offs, FrameOffset fr_offs,
+ ManagedRegister scratch) {
+ CHECK(scratch.IsCoreRegister());
+ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
+ SP, fr_offs.Int32Value());
+ StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
+ TR, thr_offs.Int32Value());
+}
+
+void Assembler::StoreStackOffsetToThread(ThreadOffset thr_offs,
+ FrameOffset fr_offs,
+ ManagedRegister scratch) {
+ CHECK(scratch.IsCoreRegister());
+ AddConstant(scratch.AsCoreRegister(), SP, fr_offs.Int32Value(), AL);
+ StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
+ TR, thr_offs.Int32Value());
+}
+
+void Assembler::Move(ManagedRegister dest, ManagedRegister src) {
+ if (dest.IsCoreRegister()) {
+ CHECK(src.IsCoreRegister());
+ mov(dest.AsCoreRegister(), ShifterOperand(src.AsCoreRegister()));
+ } else {
+ // TODO: VFP
+ LOG(FATAL) << "Unimplemented";
+ }
+}
+
+void Assembler::Copy(FrameOffset dest, FrameOffset src, ManagedRegister scratch,
+ size_t size) {
+ CHECK(scratch.IsCoreRegister());
+ if (size == 4) {
+ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
+ SP, src.Int32Value());
+ StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
+ SP, dest.Int32Value());
+ } else {
+ // TODO: size != 4
+ LOG(FATAL) << "Unimplemented";
+ }
+}
+
+void Assembler::CreateStackHandle(ManagedRegister out_reg,
+ FrameOffset handle_offset,
+ ManagedRegister in_reg, bool null_allowed) {
+ CHECK(in_reg.IsCoreRegister());
+ CHECK(out_reg.IsCoreRegister());
+ if (null_allowed) {
+ // Null values get a handle value of 0. Otherwise, the handle value is
+ // the address in the stack handle block holding the reference.
+ // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset)
+ cmp(in_reg.AsCoreRegister(), ShifterOperand(0));
+ if (!out_reg.Equals(in_reg)) {
+ LoadImmediate(out_reg.AsCoreRegister(), 0, EQ);
+ }
+ AddConstant(out_reg.AsCoreRegister(), SP, handle_offset.Int32Value(), NE);
+ } else {
+ AddConstant(out_reg.AsCoreRegister(), SP, handle_offset.Int32Value(), AL);
+ }
+}
+
+void Assembler::CreateStackHandle(FrameOffset out_off,
+ FrameOffset handle_offset,
+ ManagedRegister scratch, bool null_allowed) {
+ CHECK(scratch.IsCoreRegister());
+ if (null_allowed) {
+ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP,
+ handle_offset.Int32Value());
+ // Null values get a handle value of 0. Otherwise, the handle value is
+ // the address in the stack handle block holding the reference.
+ // e.g. scratch = (handle == 0) ? 0 : (SP+handle_offset)
+ cmp(scratch.AsCoreRegister(), ShifterOperand(0));
+ AddConstant(scratch.AsCoreRegister(), SP, handle_offset.Int32Value(), NE);
+ } else {
+ AddConstant(scratch.AsCoreRegister(), SP, handle_offset.Int32Value(), AL);
+ }
+ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, out_off.Int32Value());
+}
+
+void Assembler::LoadReferenceFromStackHandle(ManagedRegister out_reg,
+ ManagedRegister in_reg,
+ FrameOffset shb_offset) {
+ CHECK(out_reg.IsCoreRegister());
+ CHECK(in_reg.IsCoreRegister());
+ Label null_arg;
+ if (!out_reg.Equals(in_reg)) {
+ LoadImmediate(out_reg.AsCoreRegister(), 0, EQ);
+ }
+ cmp(in_reg.AsCoreRegister(), ShifterOperand(0));
+ LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(), in_reg.AsCoreRegister(),
+ shb_offset.Int32Value(), NE);
+}
+
+void Assembler::ValidateRef(ManagedRegister src, bool could_be_null) {
+ // TODO: not validating references
+}
+
+void Assembler::ValidateRef(FrameOffset src, bool could_be_null) {
+ // TODO: not validating references
+}
+
+void Assembler::Call(ManagedRegister base, MemberOffset offset,
+ ManagedRegister scratch) {
+ CHECK(base.IsCoreRegister());
+ CHECK(scratch.IsCoreRegister());
+ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
+ base.AsCoreRegister(), offset.Int32Value());
+ blx(scratch.AsCoreRegister());
+ // TODO: place reference map on call
+}
+
+// Emit code that will lock the reference in the given register
+void Assembler::LockReferenceOnStack(FrameOffset fr_offs) {
+ LOG(FATAL) << "TODO";
+}
+// Emit code that will unlock the reference in the given register
+void Assembler::UnLockReferenceOnStack(FrameOffset fr_offs) {
+ LOG(FATAL) << "TODO";
+}
+
} // namespace art