Skip to content

Commit

Permalink
Clean up JNI calling convention callee saves.
Browse files Browse the repository at this point in the history
Precalculate callee saves at compile time and return them
as ArrayRef<> instead of keeping then in a std::vector<>.

Change-Id: I4fd7d2bbf6138dc31b0fe8554eac35b0777ec9ef
  • Loading branch information
vmarko authored and xboxfanj committed Dec 17, 2016
1 parent 17c106f commit d869441
Show file tree
Hide file tree
Showing 38 changed files with 499 additions and 356 deletions.
2 changes: 1 addition & 1 deletion compiler/jni/jni_cfi_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class JNICFITest : public CFITest {
std::unique_ptr<ManagedRuntimeCallingConvention> mr_conv(
ManagedRuntimeCallingConvention::Create(&arena, is_static, is_synchronized, shorty, isa));
const int frame_size(jni_conv->FrameSize());
const std::vector<ManagedRegister>& callee_save_regs = jni_conv->CalleeSaveRegisters();
ArrayRef<const ManagedRegister> callee_save_regs = jni_conv->CalleeSaveRegisters();

// Assemble the method.
std::unique_ptr<Assembler> jni_asm(Assembler::Create(&arena, isa));
Expand Down
80 changes: 57 additions & 23 deletions compiler/jni/quick/arm/calling_convention_arm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,64 @@ static const SRegister kHFSArgumentRegisters[] = {
S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15
};

static const SRegister kHFSCalleeSaveRegisters[] = {
S16, S17, S18, S19, S20, S21, S22, S23, S24, S25, S26, S27, S28, S29, S30, S31
};

static const DRegister kHFDArgumentRegisters[] = {
D0, D1, D2, D3, D4, D5, D6, D7
};

static_assert(arraysize(kHFDArgumentRegisters) * 2 == arraysize(kHFSArgumentRegisters),
"ks d argument registers mismatch");

static constexpr ManagedRegister kCalleeSaveRegisters[] = {
// Core registers.
ArmManagedRegister::FromCoreRegister(R5),
ArmManagedRegister::FromCoreRegister(R6),
ArmManagedRegister::FromCoreRegister(R7),
ArmManagedRegister::FromCoreRegister(R8),
ArmManagedRegister::FromCoreRegister(R10),
ArmManagedRegister::FromCoreRegister(R11),
// Hard float registers.
ArmManagedRegister::FromSRegister(S16),
ArmManagedRegister::FromSRegister(S17),
ArmManagedRegister::FromSRegister(S18),
ArmManagedRegister::FromSRegister(S19),
ArmManagedRegister::FromSRegister(S20),
ArmManagedRegister::FromSRegister(S21),
ArmManagedRegister::FromSRegister(S22),
ArmManagedRegister::FromSRegister(S23),
ArmManagedRegister::FromSRegister(S24),
ArmManagedRegister::FromSRegister(S25),
ArmManagedRegister::FromSRegister(S26),
ArmManagedRegister::FromSRegister(S27),
ArmManagedRegister::FromSRegister(S28),
ArmManagedRegister::FromSRegister(S29),
ArmManagedRegister::FromSRegister(S30),
ArmManagedRegister::FromSRegister(S31)
};

static constexpr uint32_t CalculateCoreCalleeSpillMask() {
// LR is a special callee save which is not reported by CalleeSaveRegisters().
uint32_t result = 1 << LR;
for (auto&& r : kCalleeSaveRegisters) {
if (r.AsArm().IsCoreRegister()) {
result |= (1 << r.AsArm().AsCoreRegister());
}
}
return result;
}

static constexpr uint32_t CalculateFpCalleeSpillMask() {
uint32_t result = 0;
for (auto&& r : kCalleeSaveRegisters) {
if (r.AsArm().IsSRegister()) {
result |= (1 << r.AsArm().AsSRegister());
}
}
return result;
}

static constexpr uint32_t kCoreCalleeSpillMask = CalculateCoreCalleeSpillMask();
static constexpr uint32_t kFpCalleeSpillMask = CalculateFpCalleeSpillMask();

// Calling convention

ManagedRegister ArmManagedRuntimeCallingConvention::InterproceduralScratchRegister() {
Expand Down Expand Up @@ -223,32 +270,15 @@ ArmJniCallingConvention::ArmJniCallingConvention(bool is_static, bool is_synchro
cur_reg++; // bump the iterator for every argument
}
padding_ = padding;

callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R5));
callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R6));
callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R7));
callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R8));
callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R10));
callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R11));

for (size_t i = 0; i < arraysize(kHFSCalleeSaveRegisters); ++i) {
callee_save_regs_.push_back(ArmManagedRegister::FromSRegister(kHFSCalleeSaveRegisters[i]));
}
}

uint32_t ArmJniCallingConvention::CoreSpillMask() const {
// Compute spill mask to agree with callee saves initialized in the constructor
uint32_t result = 0;
result = 1 << R5 | 1 << R6 | 1 << R7 | 1 << R8 | 1 << R10 | 1 << R11 | 1 << LR;
return result;
return kCoreCalleeSpillMask;
}

uint32_t ArmJniCallingConvention::FpSpillMask() const {
uint32_t result = 0;
for (size_t i = 0; i < arraysize(kHFSCalleeSaveRegisters); ++i) {
result |= (1 << kHFSCalleeSaveRegisters[i]);
}
return result;
return kFpCalleeSpillMask;
}

ManagedRegister ArmJniCallingConvention::ReturnScratchRegister() const {
Expand All @@ -269,6 +299,10 @@ size_t ArmJniCallingConvention::OutArgSize() {
kStackAlignment);
}

ArrayRef<const ManagedRegister> ArmJniCallingConvention::CalleeSaveRegisters() const {
return ArrayRef<const ManagedRegister>(kCalleeSaveRegisters);
}

// JniCallingConvention ABI follows AAPCS where longs and doubles must occur
// in even register numbers and stack slots
void ArmJniCallingConvention::Next() {
Expand Down
7 changes: 1 addition & 6 deletions compiler/jni/quick/arm/calling_convention_arm.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ class ArmJniCallingConvention FINAL : public JniCallingConvention {
void Next() OVERRIDE; // Override default behavior for AAPCS
size_t FrameSize() OVERRIDE;
size_t OutArgSize() OVERRIDE;
const std::vector<ManagedRegister>& CalleeSaveRegisters() const OVERRIDE {
return callee_save_regs_;
}
ArrayRef<const ManagedRegister> CalleeSaveRegisters() const OVERRIDE;
ManagedRegister ReturnScratchRegister() const OVERRIDE;
uint32_t CoreSpillMask() const OVERRIDE;
uint32_t FpSpillMask() const OVERRIDE;
Expand All @@ -78,9 +76,6 @@ class ArmJniCallingConvention FINAL : public JniCallingConvention {
size_t NumberOfOutgoingStackArgs() OVERRIDE;

private:
// TODO: these values aren't unique and can be shared amongst instances
std::vector<ManagedRegister> callee_save_regs_;

// Padding to ensure longs and doubles are not split in AAPCS
size_t padding_;

Expand Down
100 changes: 63 additions & 37 deletions compiler/jni/quick/arm64/calling_convention_arm64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,65 @@ static const SRegister kSArgumentRegisters[] = {
S0, S1, S2, S3, S4, S5, S6, S7
};

static const DRegister kDCalleeSaveRegisters[] = {
D8, D9, D10, D11, D12, D13, D14, D15
static constexpr ManagedRegister kCalleeSaveRegisters[] = {
// Core registers.
// Note: The native jni function may call to some VM runtime functions which may suspend
// or trigger GC. And the jni method frame will become top quick frame in those cases.
// So we need to satisfy GC to save LR and callee-save registers which is similar to
// CalleeSaveMethod(RefOnly) frame.
// Jni function is the native function which the java code wants to call.
// Jni method is the method that is compiled by jni compiler.
// Call chain: managed code(java) --> jni method --> jni function.
// Thread register(X19) is saved on stack.
Arm64ManagedRegister::FromXRegister(X19),
Arm64ManagedRegister::FromXRegister(X20),
Arm64ManagedRegister::FromXRegister(X21),
Arm64ManagedRegister::FromXRegister(X22),
Arm64ManagedRegister::FromXRegister(X23),
Arm64ManagedRegister::FromXRegister(X24),
Arm64ManagedRegister::FromXRegister(X25),
Arm64ManagedRegister::FromXRegister(X26),
Arm64ManagedRegister::FromXRegister(X27),
Arm64ManagedRegister::FromXRegister(X28),
Arm64ManagedRegister::FromXRegister(X29),
Arm64ManagedRegister::FromXRegister(LR),
// Hard float registers.
// Considering the case, java_method_1 --> jni method --> jni function --> java_method_2,
// we may break on java_method_2 and we still need to find out the values of DEX registers
// in java_method_1. So all callee-saves(in managed code) need to be saved.
Arm64ManagedRegister::FromDRegister(D8),
Arm64ManagedRegister::FromDRegister(D9),
Arm64ManagedRegister::FromDRegister(D10),
Arm64ManagedRegister::FromDRegister(D11),
Arm64ManagedRegister::FromDRegister(D12),
Arm64ManagedRegister::FromDRegister(D13),
Arm64ManagedRegister::FromDRegister(D14),
Arm64ManagedRegister::FromDRegister(D15),
};

static constexpr uint32_t CalculateCoreCalleeSpillMask() {
uint32_t result = 0u;
for (auto&& r : kCalleeSaveRegisters) {
if (r.AsArm64().IsXRegister()) {
result |= (1 << r.AsArm64().AsXRegister());
}
}
return result;
}

static constexpr uint32_t CalculateFpCalleeSpillMask() {
uint32_t result = 0;
for (auto&& r : kCalleeSaveRegisters) {
if (r.AsArm64().IsDRegister()) {
result |= (1 << r.AsArm64().AsDRegister());
}
}
return result;
}

static constexpr uint32_t kCoreCalleeSpillMask = CalculateCoreCalleeSpillMask();
static constexpr uint32_t kFpCalleeSpillMask = CalculateFpCalleeSpillMask();

// Calling convention
ManagedRegister Arm64ManagedRuntimeCallingConvention::InterproceduralScratchRegister() {
return Arm64ManagedRegister::FromXRegister(X20); // saved on entry restored on exit
Expand Down Expand Up @@ -157,47 +212,14 @@ const ManagedRegisterEntrySpills& Arm64ManagedRuntimeCallingConvention::EntrySpi
Arm64JniCallingConvention::Arm64JniCallingConvention(bool is_static, bool is_synchronized,
const char* shorty)
: JniCallingConvention(is_static, is_synchronized, shorty, kFramePointerSize) {
uint32_t core_spill_mask = CoreSpillMask();
DCHECK_EQ(XZR, kNumberOfXRegisters - 1); // Exclude XZR from the loop (avoid 1 << 32).
for (int x_reg = 0; x_reg < kNumberOfXRegisters - 1; ++x_reg) {
if (((1 << x_reg) & core_spill_mask) != 0) {
callee_save_regs_.push_back(
Arm64ManagedRegister::FromXRegister(static_cast<XRegister>(x_reg)));
}
}

uint32_t fp_spill_mask = FpSpillMask();
for (int d_reg = 0; d_reg < kNumberOfDRegisters; ++d_reg) {
if (((1 << d_reg) & fp_spill_mask) != 0) {
callee_save_regs_.push_back(
Arm64ManagedRegister::FromDRegister(static_cast<DRegister>(d_reg)));
}
}
}

uint32_t Arm64JniCallingConvention::CoreSpillMask() const {
// Compute spill mask to agree with callee saves initialized in the constructor.
// Note: The native jni function may call to some VM runtime functions which may suspend
// or trigger GC. And the jni method frame will become top quick frame in those cases.
// So we need to satisfy GC to save LR and callee-save registers which is similar to
// CalleeSaveMethod(RefOnly) frame.
// Jni function is the native function which the java code wants to call.
// Jni method is the method that compiled by jni compiler.
// Call chain: managed code(java) --> jni method --> jni function.
// Thread register(X19) is saved on stack.
return 1 << X19 | 1 << X20 | 1 << X21 | 1 << X22 | 1 << X23 | 1 << X24 |
1 << X25 | 1 << X26 | 1 << X27 | 1 << X28 | 1 << X29 | 1 << LR;
return kCoreCalleeSpillMask;
}

uint32_t Arm64JniCallingConvention::FpSpillMask() const {
// Considering the case, java_method_1 --> jni method --> jni function --> java_method_2, we may
// break on java_method_2 and we still need to find out the values of DEX registers in
// java_method_1. So all callee-saves(in managed code) need to be saved.
uint32_t result = 0;
for (size_t i = 0; i < arraysize(kDCalleeSaveRegisters); ++i) {
result |= (1 << kDCalleeSaveRegisters[i]);
}
return result;
return kFpCalleeSpillMask;
}

ManagedRegister Arm64JniCallingConvention::ReturnScratchRegister() const {
Expand All @@ -218,6 +240,10 @@ size_t Arm64JniCallingConvention::OutArgSize() {
return RoundUp(NumberOfOutgoingStackArgs() * kFramePointerSize, kStackAlignment);
}

ArrayRef<const ManagedRegister> Arm64JniCallingConvention::CalleeSaveRegisters() const {
return ArrayRef<const ManagedRegister>(kCalleeSaveRegisters);
}

bool Arm64JniCallingConvention::IsCurrentParamInRegister() {
if (IsCurrentParamAFloatOrDouble()) {
return (itr_float_and_doubles_ < 8);
Expand Down
7 changes: 1 addition & 6 deletions compiler/jni/quick/arm64/calling_convention_arm64.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,7 @@ class Arm64JniCallingConvention FINAL : public JniCallingConvention {
// JNI calling convention
size_t FrameSize() OVERRIDE;
size_t OutArgSize() OVERRIDE;
const std::vector<ManagedRegister>& CalleeSaveRegisters() const OVERRIDE {
return callee_save_regs_;
}
ArrayRef<const ManagedRegister> CalleeSaveRegisters() const OVERRIDE;
ManagedRegister ReturnScratchRegister() const OVERRIDE;
uint32_t CoreSpillMask() const OVERRIDE;
uint32_t FpSpillMask() const OVERRIDE;
Expand All @@ -77,9 +75,6 @@ class Arm64JniCallingConvention FINAL : public JniCallingConvention {
size_t NumberOfOutgoingStackArgs() OVERRIDE;

private:
// TODO: these values aren't unique and can be shared amongst instances
std::vector<ManagedRegister> callee_save_regs_;

DISALLOW_COPY_AND_ASSIGN(Arm64JniCallingConvention);
};

Expand Down
5 changes: 2 additions & 3 deletions compiler/jni/quick/calling_convention.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@
#ifndef ART_COMPILER_JNI_QUICK_CALLING_CONVENTION_H_
#define ART_COMPILER_JNI_QUICK_CALLING_CONVENTION_H_

#include <vector>

#include "base/arena_object.h"
#include "handle_scope.h"
#include "primitive.h"
#include "thread.h"
#include "utils/array_ref.h"
#include "utils/managed_register.h"

namespace art {
Expand Down Expand Up @@ -301,7 +300,7 @@ class JniCallingConvention : public CallingConvention {
virtual bool RequiresSmallResultTypeExtension() const = 0;

// Callee save registers to spill prior to native code (which may clobber)
virtual const std::vector<ManagedRegister>& CalleeSaveRegisters() const = 0;
virtual ArrayRef<const ManagedRegister> CalleeSaveRegisters() const = 0;

// Spill mask values
virtual uint32_t CoreSpillMask() const = 0;
Expand Down
2 changes: 1 addition & 1 deletion compiler/jni/quick/jni_compiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver,

// 1. Build the frame saving all callee saves
const size_t frame_size(main_jni_conv->FrameSize());
const std::vector<ManagedRegister>& callee_save_regs = main_jni_conv->CalleeSaveRegisters();
ArrayRef<const ManagedRegister> callee_save_regs = main_jni_conv->CalleeSaveRegisters();
__ BuildFrame(frame_size, mr_conv->MethodRegister(), callee_save_regs, mr_conv->EntrySpills());
DCHECK_EQ(jni_asm->cfi().GetCurrentCFAOffset(), static_cast<int>(frame_size));

Expand Down
47 changes: 35 additions & 12 deletions compiler/jni/quick/mips/calling_convention_mips.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,32 @@ static const Register kCoreArgumentRegisters[] = { A0, A1, A2, A3 };
static const FRegister kFArgumentRegisters[] = { F12, F14 };
static const DRegister kDArgumentRegisters[] = { D6, D7 };

static constexpr ManagedRegister kCalleeSaveRegisters[] = {
// Core registers.
MipsManagedRegister::FromCoreRegister(S2),
MipsManagedRegister::FromCoreRegister(S3),
MipsManagedRegister::FromCoreRegister(S4),
MipsManagedRegister::FromCoreRegister(S5),
MipsManagedRegister::FromCoreRegister(S6),
MipsManagedRegister::FromCoreRegister(S7),
MipsManagedRegister::FromCoreRegister(FP),
// No hard float callee saves.
};

static constexpr uint32_t CalculateCoreCalleeSpillMask() {
// RA is a special callee save which is not reported by CalleeSaveRegisters().
uint32_t result = 1 << RA;
for (auto&& r : kCalleeSaveRegisters) {
if (r.AsMips().IsCoreRegister()) {
result |= (1 << r.AsMips().AsCoreRegister());
}
}
return result;
}

static constexpr uint32_t kCoreCalleeSpillMask = CalculateCoreCalleeSpillMask();
static constexpr uint32_t kFpCalleeSpillMask = 0u;

// Calling convention
ManagedRegister MipsManagedRuntimeCallingConvention::InterproceduralScratchRegister() {
return MipsManagedRegister::FromCoreRegister(T9);
Expand Down Expand Up @@ -161,21 +187,14 @@ MipsJniCallingConvention::MipsJniCallingConvention(bool is_static, bool is_synch
cur_reg++; // bump the iterator for every argument
}
padding_ = padding;

callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(S2));
callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(S3));
callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(S4));
callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(S5));
callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(S6));
callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(S7));
callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(FP));
}

uint32_t MipsJniCallingConvention::CoreSpillMask() const {
// Compute spill mask to agree with callee saves initialized in the constructor
uint32_t result = 0;
result = 1 << S2 | 1 << S3 | 1 << S4 | 1 << S5 | 1 << S6 | 1 << S7 | 1 << FP | 1 << RA;
return result;
return kCoreCalleeSpillMask;
}

uint32_t MipsJniCallingConvention::FpSpillMask() const {
return kFpCalleeSpillMask;
}

ManagedRegister MipsJniCallingConvention::ReturnScratchRegister() const {
Expand All @@ -196,6 +215,10 @@ size_t MipsJniCallingConvention::OutArgSize() {
return RoundUp(NumberOfOutgoingStackArgs() * kFramePointerSize + padding_, kStackAlignment);
}

ArrayRef<const ManagedRegister> MipsJniCallingConvention::CalleeSaveRegisters() const {
return ArrayRef<const ManagedRegister>(kCalleeSaveRegisters);
}

// JniCallingConvention ABI follows AAPCS where longs and doubles must occur
// in even register numbers and stack slots
void MipsJniCallingConvention::Next() {
Expand Down
Loading

0 comments on commit d869441

Please sign in to comment.