Handle double and long on the Managed side, and follow AAPCS on the Native side.
Change-Id: I43a8fa36cb79fb438f075986a46c66ab8258e725
diff --git a/src/assembler_arm.cc b/src/assembler_arm.cc
index a728239..70dd20c 100644
--- a/src/assembler_arm.cc
+++ b/src/assembler_arm.cc
@@ -1516,14 +1516,21 @@
void Assembler::Copy(FrameOffset dest, FrameOffset src, ManagedRegister scratch,
size_t size) {
CHECK(scratch.IsCoreRegister());
+ CHECK(size == 4 || size == 8);
if (size == 4) {
LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
SP, src.Int32Value());
StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
SP, dest.Int32Value());
- } else {
- // TODO: size != 4
- LOG(FATAL) << "Unimplemented";
+ } else if (size == 8) {
+ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
+ SP, src.Int32Value());
+ StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
+ SP, dest.Int32Value());
+ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
+ SP, src.Int32Value() + 4);
+ StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
+ SP, dest.Int32Value() + 4);
}
}
diff --git a/src/calling_convention.cc b/src/calling_convention.cc
index bfd0e34..ec8daeb 100644
--- a/src/calling_convention.cc
+++ b/src/calling_convention.cc
@@ -20,29 +20,34 @@
}
bool ManagedRuntimeCallingConvention::HasNext() {
- return itr_position_ < GetMethod()->NumArgs();
+ return itr_args_ < GetMethod()->NumArgs();
}
void ManagedRuntimeCallingConvention::Next() {
CHECK(HasNext());
- if (((itr_position_ != 0) || GetMethod()->IsStatic()) &&
- GetMethod()->IsParamALongOrDouble(itr_position_)) {
+ if (IsCurrentUserArg() &&
+ GetMethod()->IsParamALongOrDouble(itr_args_)) {
itr_longs_and_doubles_++;
+ itr_slots_++;
}
- itr_position_++;
+ itr_args_++;
+ itr_slots_++;
}
-bool ManagedRuntimeCallingConvention::IsCurrentParamPossiblyNull() {
- // for a virtual method, this should never be NULL
- return GetMethod()->IsStatic() || (itr_position_ != 0);
+bool ManagedRuntimeCallingConvention::IsCurrentUserArg() {
+ if (GetMethod()->IsStatic()) {
+ return true;
+ }
+ // For a virtual method, "this" should never be NULL.
+ return (itr_args_ != 0);
}
size_t ManagedRuntimeCallingConvention::CurrentParamSize() {
- return GetMethod()->ParamSize(itr_position_);
+ return GetMethod()->ParamSize(itr_args_);
}
bool ManagedRuntimeCallingConvention::IsCurrentParamAReference() {
- return GetMethod()->IsParamAReference(itr_position_);
+ return GetMethod()->IsParamAReference(itr_args_);
}
// JNI calling convention
@@ -71,33 +76,35 @@
}
bool JniCallingConvention::HasNext() {
- if (itr_position_ <= kObjectOrClass) {
+ if (itr_args_ <= kObjectOrClass) {
return true;
} else {
- unsigned int arg_pos = itr_position_ - (GetMethod()->IsStatic() ? 2 : 1);
+ unsigned int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(GetMethod());
return arg_pos < GetMethod()->NumArgs();
}
}
void JniCallingConvention::Next() {
CHECK(HasNext());
- if (itr_position_ > kObjectOrClass) {
- int arg_pos = itr_position_ - (GetMethod()->IsStatic() ? 2 : 1);
+ if (itr_args_ > kObjectOrClass) {
+ int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(GetMethod());
if (GetMethod()->IsParamALongOrDouble(arg_pos)) {
itr_longs_and_doubles_++;
+ itr_slots_++;
}
}
- itr_position_++;
+ itr_args_++;
+ itr_slots_++;
}
bool JniCallingConvention::IsCurrentParamAReference() {
- switch (itr_position_) {
+ switch (itr_args_) {
case kJniEnv:
return false; // JNIEnv*
case kObjectOrClass:
return true; // jobject or jclass
default: {
- int arg_pos = itr_position_ - (GetMethod()->IsStatic() ? 2 : 1);
+ int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(GetMethod());
return GetMethod()->IsParamAReference(arg_pos);
}
}
@@ -109,11 +116,11 @@
CHECK_GT(ShbLinkOffset(), ShbNumRefsOffset());
// Address of 1st handle
int result = ShbLinkOffset().Int32Value() + kPointerSize;
- if (itr_position_ != kObjectOrClass) {
- bool is_static = GetMethod()->IsStatic();
- int arg_pos = itr_position_ - (is_static ? 2 : 1);
+ if (itr_args_ != kObjectOrClass) {
+ const Method *method = GetMethod();
+ int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(method);
int previous_refs = GetMethod()->NumReferenceArgsBefore(arg_pos);
- if (is_static) {
+ if (method->IsStatic()) {
previous_refs++; // account for jclass
}
result += previous_refs * kPointerSize;
@@ -123,12 +130,19 @@
}
size_t JniCallingConvention::CurrentParamSize() {
- if (itr_position_ <= kObjectOrClass) {
+ if (itr_args_ <= kObjectOrClass) {
return kPointerSize; // JNIEnv or jobject/jclass
} else {
- int arg_pos = itr_position_ - (GetMethod()->IsStatic() ? 2 : 1);
+ int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(GetMethod());
return GetMethod()->ParamSize(arg_pos);
}
}
+size_t JniCallingConvention::NumberOfExtraArgumentsForJni(
+ const Method* method) {
+ // The first argument is the JNIEnv*.
+ // Static methods have an extra argument which is the jclass.
+ return (method->IsStatic() ? 2 : 1);
+}
+
} // namespace art
diff --git a/src/calling_convention.h b/src/calling_convention.h
index 23ca6d6..b335597 100644
--- a/src/calling_convention.h
+++ b/src/calling_convention.h
@@ -36,7 +36,8 @@
// below the one being iterated over.
void ResetIterator(FrameOffset displacement) {
displacement_ = displacement;
- itr_position_ = 0;
+ itr_slots_ = 0;
+ itr_args_ = 0;
itr_longs_and_doubles_ = 0;
}
@@ -45,9 +46,13 @@
method_(method) {}
const Method* GetMethod() const { return method_; }
- // position along argument list
- unsigned int itr_position_;
- // number of longs and doubles seen along argument list
+ // The slot number for current calling_convention argument.
+ // Note that each slot is 32-bit. When the current argument is bigger
+ // than 32 bits, return the first slot number for this argument.
+ unsigned int itr_slots_;
+ // The argument number along argument list for current argument
+ unsigned int itr_args_;
+ // Number of longs and doubles seen along argument list
unsigned int itr_longs_and_doubles_;
// Space for frames below this on the stack
FrameOffset displacement_;
@@ -70,7 +75,7 @@
bool IsCurrentParamAReference();
bool IsCurrentParamInRegister();
bool IsCurrentParamOnStack();
- bool IsCurrentParamPossiblyNull();
+ bool IsCurrentUserArg();
size_t CurrentParamSize();
ManagedRegister CurrentParamRegister();
FrameOffset CurrentParamStackOffset();
@@ -149,6 +154,7 @@
// located
size_t NumberOfOutgoingStackArgs();
+ static size_t NumberOfExtraArgumentsForJni(const Method* method);
DISALLOW_COPY_AND_ASSIGN(JniCallingConvention);
};
diff --git a/src/calling_convention_arm.cc b/src/calling_convention_arm.cc
index f93dc9b..a7a080e 100644
--- a/src/calling_convention_arm.cc
+++ b/src/calling_convention_arm.cc
@@ -32,36 +32,44 @@
// Managed runtime calling convention
bool ManagedRuntimeCallingConvention::IsCurrentParamInRegister() {
- return itr_position_ < 3;
+ if (itr_slots_ < 3) {
+ return true;
+ }
+ return false; // everything else on the stack
}
bool ManagedRuntimeCallingConvention::IsCurrentParamOnStack() {
- return itr_position_ >= 3;
+ return !IsCurrentParamInRegister();
}
static const Register kManagedArgumentRegisters[] = {
R1, R2, R3
};
ManagedRegister ManagedRuntimeCallingConvention::CurrentParamRegister() {
- CHECK_LT(itr_position_, 3u);
+ CHECK_LT(itr_slots_, 3u); // Otherwise, should have gone through stack
const Method* method = GetMethod();
- if (method->IsParamALongOrDouble(itr_position_)) {
- // TODO: handle a long/double split between registers and the stack, also
- // itr_position_ 0
- if (itr_position_ != 1u) {
- LOG(WARNING) << "Unimplemented";
+ if (method->IsParamALongOrDouble(itr_args_)) {
+ if (itr_slots_ == 0) {
+ // return R1 to denote R1_R2
+ return ManagedRegister::FromCoreRegister(kManagedArgumentRegisters
+ [itr_slots_]);
+ } else if (itr_slots_ == 1) {
+ return ManagedRegister::FromRegisterPair(R2_R3);
+ } else {
+ // This is a long/double split between registers and the stack
+ return ManagedRegister::FromCoreRegister(
+ kManagedArgumentRegisters[itr_slots_]);
}
- return ManagedRegister::FromRegisterPair(R2_R3);
} else {
return
- ManagedRegister::FromCoreRegister(kManagedArgumentRegisters[itr_position_]);
+ ManagedRegister::FromCoreRegister(kManagedArgumentRegisters[itr_slots_]);
}
}
FrameOffset ManagedRuntimeCallingConvention::CurrentParamStackOffset() {
- CHECK_GE(itr_position_, 3u);
+ CHECK_GE(itr_slots_, 3u);
return FrameOffset(displacement_.Int32Value() +
- ((itr_position_ + itr_longs_and_doubles_ - 3) * kPointerSize));
+ ((itr_slots_ - 3) * kPointerSize));
}
// JNI calling convention
@@ -72,37 +80,50 @@
return true;
}
+// JniCallingConvention ABI follows AAPCS
+//
+// In processing each parameter, we know that IsCurrentParamInRegister()
+// or IsCurrentParamOnStack() will be called first.
+// Both functions will ensure that we conform to AAPCS.
+//
bool JniCallingConvention::IsCurrentParamInRegister() {
- return (itr_position_ + itr_longs_and_doubles_) < 4;
+ // AAPCS processing
+ const Method* method = GetMethod();
+ int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(method);
+ if ((itr_args_ >= 2) && method->IsParamALongOrDouble(arg_pos)) {
+ // itr_slots_ needs to be an even number, according to AAPCS.
+ if (itr_slots_ & 0x1u) {
+ itr_slots_++;
+ }
+ }
+
+ return itr_slots_ < 4;
}
bool JniCallingConvention::IsCurrentParamOnStack() {
- return (itr_position_ + itr_longs_and_doubles_) >= 4;
+ return !IsCurrentParamInRegister();
}
static const Register kJniArgumentRegisters[] = {
R0, R1, R2, R3
};
ManagedRegister JniCallingConvention::CurrentParamRegister() {
- CHECK_LT(itr_position_ + itr_longs_and_doubles_, 4u);
+ CHECK_LT(itr_slots_, 4u);
const Method* method = GetMethod();
- int arg_pos = itr_position_ - (method->IsStatic() ? 2 : 1);
- if ((itr_position_ >= 2) && method->IsParamALongOrDouble(arg_pos)) {
- // TODO: handle a long/double split between registers and the stack
- if (itr_position_ != 2u) {
- LOG(WARNING) << "Unimplemented";
- }
+ int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(method);
+ if ((itr_args_ >= 2) && method->IsParamALongOrDouble(arg_pos)) {
+ CHECK_EQ(itr_slots_, 2u);
return ManagedRegister::FromRegisterPair(R2_R3);
} else {
return
- ManagedRegister::FromCoreRegister(kJniArgumentRegisters[itr_position_]);
+ ManagedRegister::FromCoreRegister(kJniArgumentRegisters[itr_slots_]);
}
}
FrameOffset JniCallingConvention::CurrentParamStackOffset() {
- CHECK_GE(itr_position_ + itr_longs_and_doubles_, 4u);
+ CHECK_GE(itr_slots_, 4u);
return FrameOffset(displacement_.Int32Value() - OutArgSize()
- + ((itr_position_ + itr_longs_and_doubles_ - 4) * kPointerSize));
+ + ((itr_slots_ - 4) * kPointerSize));
}
size_t JniCallingConvention::NumberOfOutgoingStackArgs() {
diff --git a/src/calling_convention_x86.cc b/src/calling_convention_x86.cc
index 2724cba..d245d81 100644
--- a/src/calling_convention_x86.cc
+++ b/src/calling_convention_x86.cc
@@ -44,8 +44,7 @@
}
FrameOffset ManagedRuntimeCallingConvention::CurrentParamStackOffset() {
- int num_slots = itr_position_ + itr_longs_and_doubles_;
- return FrameOffset(displacement_.Int32Value() + (num_slots * kPointerSize));
+ return FrameOffset(displacement_.Int32Value() + (itr_slots_ * kPointerSize));
}
// JNI calling convention
@@ -68,9 +67,8 @@
}
FrameOffset JniCallingConvention::CurrentParamStackOffset() {
- int num_slots = itr_position_ + itr_longs_and_doubles_;
return FrameOffset(displacement_.Int32Value() - OutArgSize() +
- (num_slots * kPointerSize));
+ (itr_slots_ * kPointerSize));
}
size_t JniCallingConvention::NumberOfOutgoingStackArgs() {
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
index 8433c27..436d4d8 100644
--- a/src/jni_compiler.cc
+++ b/src/jni_compiler.cc
@@ -72,14 +72,16 @@
// Check handle offset is within frame
CHECK_LT(handle_offset.Uint32Value(), frame_size);
bool input_in_reg = mr_conv.IsCurrentParamInRegister();
- CHECK(input_in_reg || mr_conv.IsCurrentParamOnStack());
+ bool input_on_stack = mr_conv.IsCurrentParamOnStack();
+ CHECK(input_in_reg || input_on_stack);
+
if (input_in_reg) {
ManagedRegister in_reg = mr_conv.CurrentParamRegister();
- jni_asm->ValidateRef(in_reg, mr_conv.IsCurrentParamPossiblyNull());
+ jni_asm->ValidateRef(in_reg, mr_conv.IsCurrentUserArg());
jni_asm->StoreRef(handle_offset, in_reg);
- } else {
+ } else if (input_on_stack) {
FrameOffset in_off = mr_conv.CurrentParamStackOffset();
- jni_asm->ValidateRef(in_off, mr_conv.IsCurrentParamPossiblyNull());
+ jni_asm->ValidateRef(in_off, mr_conv.IsCurrentUserArg());
jni_asm->CopyRef(handle_offset, in_off,
mr_conv.InterproceduralScratchRegister());
}
@@ -296,7 +298,7 @@
CHECK(output_in_reg || jni_conv->IsCurrentParamOnStack());
// References need handlerization and the handle address passing
if (ref_param) {
- null_allowed = mr_conv->IsCurrentParamPossiblyNull();
+ null_allowed = mr_conv->IsCurrentUserArg();
// Compute handle offset. Note null is placed in the SHB but the jobject
// passed to the native code must be null (not a pointer into the SHB
// as with regular references).
diff --git a/src/object.cc b/src/object.cc
index 2297eca..7ea30a5 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -291,7 +291,7 @@
// The number of reference arguments to this method including implicit this
// pointer
size_t Method::NumReferenceArgs() const {
- size_t result = IsStatic() ? 0 : 1;
+ size_t result = IsStatic() ? 0 : 1; // The implicit this pointer.
for (int i = 1; i < shorty_.length(); i++) {
if ((shorty_[i] == 'L') || (shorty_[i] == '[')) {
result++;
diff --git a/src/object.h b/src/object.h
index 63f5b16..1451c54 100644
--- a/src/object.h
+++ b/src/object.h
@@ -433,8 +433,21 @@
return shorty_[0] == 'V';
}
- // The number of arguments that should be supplied to this method
+ // "Args" may refer to any of the 3 levels of "Args."
+ // To avoid confusion, our code will denote which "Args" clearly:
+ // 1. UserArgs: Args that a user see.
+ // 2. Args: Logical JVM-level Args. E.g., the first in Args will be the
+ // receiver.
+ // 3. CConvArgs: Calling Convention Args, which is physical-level Args.
+ // E.g., the first in Args is Method* for both static and non-static
+ // methods. And CConvArgs doesn't deal with the receiver because
+ // receiver is hardwired in an implicit register, so CConvArgs doesn't
+ // need to deal with it.
+ //
+ // The number of Args that should be supplied to this method
size_t NumArgs() const {
+ // "1 +" because the first in Args is the receiver.
+ // "- 1" because we don't count the return type.
return (IsStatic() ? 0 : 1) + shorty_.length() - 1;
}