Support for SetVReg and add SetGPR.
These changes are useful for debugging and are required for being able
to change all GC roots.
MIPS is untested.
Change-Id: I2ba055de64264098bffe869a4fb192d0975f1c8f
diff --git a/src/oat/runtime/arm/context_arm.cc b/src/oat/runtime/arm/context_arm.cc
index 057f41e..5bd4b3d 100644
--- a/src/oat/runtime/arm/context_arm.cc
+++ b/src/oat/runtime/arm/context_arm.cc
@@ -21,16 +21,20 @@
namespace art {
namespace arm {
-ArmContext::ArmContext() {
-#ifndef NDEBUG
- // Initialize registers with easy to spot debug values
- for (int i = 0; i < 16; i++) {
- gprs_[i] = kBadGprBase + i;
+static const uint32_t gZero = 0;
+
+void ArmContext::Reset() {
+ for (size_t i = 0; i < kNumberOfCoreRegisters; i++) {
+ gprs_[i] = NULL;
}
- for (int i = 0; i < 32; i++) {
- fprs_[i] = kBadFprBase + i;
+ for (size_t i = 0; i < kNumberOfSRegisters; i++) {
+ fprs_[i] = NULL;
}
-#endif
+ gprs_[SP] = &sp_;
+ gprs_[PC] = &pc_;
+ // Initialize registers with easy to spot debug values.
+ sp_ = ArmContext::kBadGprBase + SP;
+ pc_ = ArmContext::kBadGprBase + PC;
}
void ArmContext::FillCalleeSaves(const StackVisitor& fr) {
@@ -41,40 +45,55 @@
size_t fp_spill_count = __builtin_popcount(fp_core_spills);
size_t frame_size = method->GetFrameSizeInBytes();
if (spill_count > 0) {
- // Lowest number spill is furthest away, walk registers and fill into context
+ // Lowest number spill is farthest away, walk registers and fill into context
int j = 1;
- for (int i = 0; i < 16; i++) {
+ for (size_t i = 0; i < kNumberOfCoreRegisters; i++) {
if (((core_spills >> i) & 1) != 0) {
- gprs_[i] = fr.LoadCalleeSave(spill_count - j, frame_size);
+ gprs_[i] = fr.CalleeSaveAddress(spill_count - j, frame_size);
j++;
}
}
}
if (fp_spill_count > 0) {
- // Lowest number spill is furthest away, walk registers and fill into context
+ // Lowest number spill is farthest away, walk registers and fill into context
int j = 1;
- for (int i = 0; i < 32; i++) {
+ for (size_t i = 0; i < kNumberOfSRegisters; i++) {
if (((fp_core_spills >> i) & 1) != 0) {
- fprs_[i] = fr.LoadCalleeSave(spill_count + fp_spill_count - j, frame_size);
+ fprs_[i] = fr.CalleeSaveAddress(spill_count + fp_spill_count - j, frame_size);
j++;
}
}
}
}
+void ArmContext::SetGPR(uint32_t reg, uintptr_t value) {
+ CHECK_LT(reg, kNumberOfCoreRegisters);
+ CHECK_NE(gprs_[reg], &gZero); // Can't overwrite this static value since they are never reset.
+ CHECK(gprs_[reg] != NULL);
+ *gprs_[reg] = value;
+}
+
void ArmContext::SmashCallerSaves() {
- gprs_[0] = 0; // This needs to be 0 because we want a null/zero return value.
- gprs_[1] = kBadGprBase + 1;
- gprs_[2] = kBadGprBase + 2;
- gprs_[3] = kBadGprBase + 3;
- gprs_[IP] = kBadGprBase + IP;
- gprs_[LR] = kBadGprBase + LR;
+ // This needs to be 0 because we want a null/zero return value.
+ gprs_[R0] = const_cast<uint32_t*>(&gZero);
+ gprs_[R1] = const_cast<uint32_t*>(&gZero);
+ gprs_[R2] = NULL;
+ gprs_[R3] = NULL;
}
extern "C" void art_do_long_jump(uint32_t*, uint32_t*);
void ArmContext::DoLongJump() {
- art_do_long_jump(&gprs_[0], &fprs_[S0]);
+ uintptr_t gprs[16];
+ uint32_t fprs[32];
+ for (size_t i = 0; i < kNumberOfCoreRegisters; ++i) {
+ gprs[i] = gprs_[i] != NULL ? *gprs_[i] : ArmContext::kBadGprBase + i;
+ }
+ for (size_t i = 0; i < kNumberOfSRegisters; ++i) {
+ fprs[i] = fprs_[i] != NULL ? *fprs_[i] : ArmContext::kBadGprBase + i;
+ }
+ DCHECK_EQ(reinterpret_cast<uintptr_t>(Thread::Current()), gprs[TR]);
+ art_do_long_jump(gprs, fprs);
}
} // namespace arm
diff --git a/src/oat/runtime/arm/context_arm.h b/src/oat/runtime/arm/context_arm.h
index 6f42cc3..a2b9ebe 100644
--- a/src/oat/runtime/arm/context_arm.h
+++ b/src/oat/runtime/arm/context_arm.h
@@ -17,6 +17,7 @@
#ifndef ART_SRC_OAT_RUNTIME_ARM_CONTEXT_ARM_H_
#define ART_SRC_OAT_RUNTIME_ARM_CONTEXT_ARM_H_
+#include "locks.h"
#include "constants_arm.h"
#include "oat/runtime/context.h"
@@ -25,31 +26,39 @@
class ArmContext : public Context {
public:
- ArmContext();
+ ArmContext() {
+ Reset();
+ }
+
virtual ~ArmContext() {}
+ virtual void Reset();
+
virtual void FillCalleeSaves(const StackVisitor& fr);
virtual void SetSP(uintptr_t new_sp) {
- gprs_[SP] = new_sp;
+ SetGPR(SP, new_sp);
}
virtual void SetPC(uintptr_t new_pc) {
- gprs_[PC] = new_pc;
+ SetGPR(PC, new_pc);
}
virtual uintptr_t GetGPR(uint32_t reg) {
- CHECK_GE(reg, 0u);
- CHECK_LT(reg, 16u);
- return gprs_[reg];
+ CHECK_LT(reg, kNumberOfCoreRegisters);
+ return *gprs_[reg];
}
+ virtual void SetGPR(uint32_t reg, uintptr_t value);
virtual void SmashCallerSaves();
virtual void DoLongJump();
private:
- uintptr_t gprs_[16];
- uint32_t fprs_[32];
+ // Pointers to register locations, initialized to NULL or the specific registers below.
+ uintptr_t* gprs_[kNumberOfCoreRegisters];
+ uint32_t* fprs_[kNumberOfSRegisters];
+ // Hold values for sp and pc if they are not located within a stack frame.
+ uintptr_t sp_, pc_;
};
} // namespace arm
diff --git a/src/oat/runtime/context.h b/src/oat/runtime/context.h
index 317030f..895abf9 100644
--- a/src/oat/runtime/context.h
+++ b/src/oat/runtime/context.h
@@ -24,7 +24,8 @@
class StackVisitor;
-// Representation of a thread's context on the executing machine
+// Representation of a thread's context on the executing machine, used to implement long jumps in
+// the quick stack frame layout.
class Context {
public:
// Creates a context for the running architecture
@@ -32,6 +33,9 @@
virtual ~Context() {}
+ // Re-initializes the registers for context re-use.
+ virtual void Reset() = 0;
+
// Read values from callee saves in the given frame. The frame also holds
// the method that holds the layout.
virtual void FillCalleeSaves(const StackVisitor& fr) = 0;
@@ -45,12 +49,16 @@
// Read the given GPR
virtual uintptr_t GetGPR(uint32_t reg) = 0;
+ // Set the given GPR.
+ virtual void SetGPR(uint32_t reg, uintptr_t value) = 0;
+
// Smash the caller save registers. If we're throwing, we don't want to return bogus values.
virtual void SmashCallerSaves() = 0;
// Switch execution of the executing context to this context
virtual void DoLongJump() = 0;
+ protected:
enum {
kBadGprBase = 0xebad6070,
kBadFprBase = 0xebad8070,
diff --git a/src/oat/runtime/mips/context_mips.cc b/src/oat/runtime/mips/context_mips.cc
index dc13c63..0c2f915 100644
--- a/src/oat/runtime/mips/context_mips.cc
+++ b/src/oat/runtime/mips/context_mips.cc
@@ -21,16 +21,20 @@
namespace art {
namespace mips {
-MipsContext::MipsContext() {
-#ifndef NDEBUG
+static const uint32_t gZero = 0;
+
+void MipsContext::Reset() {
+ for (size_t i = 0; i < kNumberOfCoreRegisters; i++) {
+ gprs_[i] = NULL;
+ }
+ for (size_t i = 0; i < kNumberOfFRegisters; i++) {
+ fprs_[i] = NULL;
+ }
+ gprs_[SP] = &sp_;
+ gprs_[RA] = &ra_;
// Initialize registers with easy to spot debug values.
- for (int i = 0; i < 32; i++) {
- gprs_[i] = kBadGprBase + i;
- }
- for (int i = 0; i < 32; i++) {
- fprs_[i] = kBadGprBase + i;
- }
-#endif
+ sp_ = MipsContext::kBadGprBase + SP;
+ ra_ = MipsContext::kBadGprBase + RA;
}
void MipsContext::FillCalleeSaves(const StackVisitor& fr) {
@@ -41,39 +45,55 @@
size_t fp_spill_count = __builtin_popcount(fp_core_spills);
size_t frame_size = method->GetFrameSizeInBytes();
if (spill_count > 0) {
- // Lowest number spill is furthest away, walk registers and fill into context.
+ // Lowest number spill is farthest away, walk registers and fill into context.
int j = 1;
- for (int i = 0; i < 32; i++) {
+ for (size_t i = 0; i < kNumberOfCoreRegisters; i++) {
if (((core_spills >> i) & 1) != 0) {
- gprs_[i] = fr.LoadCalleeSave(spill_count - j, frame_size);
+ gprs_[i] = fr.CalleeSaveAddress(spill_count - j, frame_size);
j++;
}
}
}
if (fp_spill_count > 0) {
- // Lowest number spill is furthest away, walk registers and fill into context.
+ // Lowest number spill is farthest away, walk registers and fill into context.
int j = 1;
- for (int i = 0; i < 32; i++) {
+ for (size_t i = 0; i < kNumberOfFRegisters; i++) {
if (((fp_core_spills >> i) & 1) != 0) {
- fprs_[i] = fr.LoadCalleeSave(spill_count + fp_spill_count - j, frame_size);
+ fprs_[i] = fr.CalleeSaveAddress(spill_count + fp_spill_count - j, frame_size);
j++;
}
}
}
}
+void MipsContext::SetGPR(uint32_t reg, uintptr_t value) {
+ CHECK_LT(reg, kNumberOfCoreRegisters);
+ CHECK_NE(gprs_[reg], &gZero); // Can't overwrite this static value since they are never reset.
+ CHECK(gprs_[reg] != NULL);
+ *gprs_[reg] = value;
+}
+
void MipsContext::SmashCallerSaves() {
- gprs_[V0] = 0; // This needs to be 0 because we want a null/zero return value.
- gprs_[V1] = 0; // This needs to be 0 because we want a null/zero return value.
- gprs_[A1] = kBadGprBase + A1;
- gprs_[A2] = kBadGprBase + A2;
- gprs_[A3] = kBadGprBase + A3;
+ // This needs to be 0 because we want a null/zero return value.
+ gprs_[V0] = const_cast<uint32_t*>(&gZero);
+ gprs_[V1] = const_cast<uint32_t*>(&gZero);
+ gprs_[A1] = NULL;
+ gprs_[A2] = NULL;
+ gprs_[A3] = NULL;
}
extern "C" void art_do_long_jump(uint32_t*, uint32_t*);
void MipsContext::DoLongJump() {
- art_do_long_jump(&gprs_[ZERO], &fprs_[F0]);
+ uintptr_t gprs[kNumberOfCoreRegisters];
+ uint32_t fprs[kNumberOfFRegisters];
+ for (size_t i = 0; i < kNumberOfCoreRegisters; ++i) {
+ gprs[i] = gprs_[i] != NULL ? *gprs_[i] : MipsContext::kBadGprBase + i;
+ }
+ for (size_t i = 0; i < kNumberOfFRegisters; ++i) {
+ fprs[i] = fprs_[i] != NULL ? *fprs_[i] : MipsContext::kBadGprBase + i;
+ }
+ art_do_long_jump(gprs, fprs);
}
} // namespace mips
diff --git a/src/oat/runtime/mips/context_mips.h b/src/oat/runtime/mips/context_mips.h
index 1a86ca3..d4944a6 100644
--- a/src/oat/runtime/mips/context_mips.h
+++ b/src/oat/runtime/mips/context_mips.h
@@ -25,32 +25,38 @@
class MipsContext : public Context {
public:
- MipsContext();
+ MipsContext() {
+ Reset();
+ }
virtual ~MipsContext() {}
- // No callee saves on mips
+ virtual void Reset();
+
virtual void FillCalleeSaves(const StackVisitor& fr);
virtual void SetSP(uintptr_t new_sp) {
- gprs_[SP] = new_sp;
+ SetGPR(SP, new_sp);
}
virtual void SetPC(uintptr_t new_pc) {
- gprs_[RA] = new_pc;
+ SetGPR(RA, new_pc);
}
virtual uintptr_t GetGPR(uint32_t reg) {
- CHECK_GE(reg, 0u);
- CHECK_LT(reg, 32u);
+ CHECK_LT(reg, kNumberOfCoreRegisters);
return gprs_[reg];
}
+ virtual void SetGPR(uint32_t reg, uintptr_t value);
virtual void SmashCallerSaves();
virtual void DoLongJump();
private:
- uintptr_t gprs_[32];
- uint32_t fprs_[32];
+ // Pointers to registers in the stack, initialized to NULL except for the special cases below.
+ uintptr_t* gprs_[kNumberOfCoreRegisters];
+ uint32_t* fprs_[kNumberOfFRegisters];
+ // Hold values for sp and ra (return address) if they are not located within a stack frame.
+ uintptr_t sp_, ra_;
};
} // namespace mips
} // namespace art
diff --git a/src/oat/runtime/x86/context_x86.cc b/src/oat/runtime/x86/context_x86.cc
index 4ff2283..4efdf81 100644
--- a/src/oat/runtime/x86/context_x86.cc
+++ b/src/oat/runtime/x86/context_x86.cc
@@ -21,14 +21,16 @@
namespace art {
namespace x86 {
-X86Context::X86Context() {
-#ifndef NDEBUG
- // Initialize registers with easy to spot debug values.
- for (int i = 0; i < 8; i++) {
- gprs_[i] = kBadGprBase + i;
+static const uint32_t gZero = 0;
+
+void X86Context::Reset() {
+ for (int i = 0; i < kNumberOfCpuRegisters; i++) {
+ gprs_[i] = NULL;
}
- eip_ = 0xEBAD601F;
-#endif
+ gprs_[ESP] = &esp_;
+ // Initialize registers with easy to spot debug values.
+ esp_ = X86Context::kBadGprBase + ESP;
+ eip_ = X86Context::kBadGprBase + kNumberOfCpuRegisters;
}
void X86Context::FillCalleeSaves(const StackVisitor& fr) {
@@ -38,11 +40,11 @@
DCHECK_EQ(method->GetFpSpillMask(), 0u);
size_t frame_size = method->GetFrameSizeInBytes();
if (spill_count > 0) {
- // Lowest number spill is furthest away, walk registers and fill into context.
+ // Lowest number spill is farthest away, walk registers and fill into context.
int j = 2; // Offset j to skip return address spill.
- for (int i = 0; i < 8; i++) {
+ for (int i = 0; i < kNumberOfCpuRegisters; i++) {
if (((core_spills >> i) & 1) != 0) {
- gprs_[i] = fr.LoadCalleeSave(spill_count - j, frame_size);
+ gprs_[i] = fr.CalleeSaveAddress(spill_count - j, frame_size);
j++;
}
}
@@ -50,37 +52,40 @@
}
void X86Context::SmashCallerSaves() {
- gprs_[EAX] = 0; // This needs to be 0 because we want a null/zero return value.
- gprs_[ECX] = kBadGprBase + ECX;
- gprs_[EDX] = kBadGprBase + EDX;
- gprs_[EBX] = kBadGprBase + EBX;
+ // This needs to be 0 because we want a null/zero return value.
+ gprs_[EAX] = const_cast<uint32_t*>(&gZero);
+ gprs_[EDX] = const_cast<uint32_t*>(&gZero);
+ gprs_[ECX] = NULL;
+ gprs_[EBX] = NULL;
+}
+
+void X86Context::SetGPR(uint32_t reg, uintptr_t value){
+ CHECK_LT(reg, kNumberOfCpuRegisters);
+ CHECK_NE(gprs_[reg], &gZero);
+ CHECK(gprs_[reg] != NULL);
+ *gprs_[reg] = value;
}
void X86Context::DoLongJump() {
#if defined(__i386__)
- // We push all the registers using memory-memory pushes, we then pop-all to get the registers
- // set up, we then pop esp which will move us down the stack to the delivery address. At the frame
- // where the exception will be delivered, we push EIP so that the return will take us to the
- // correct delivery instruction.
- gprs_[ESP] -= 4;
- *(reinterpret_cast<uintptr_t*>(gprs_[ESP])) = eip_;
+ // Array of GPR values, filled from the context backward for the long jump pop. We add a slot at
+ // the top for the stack pointer that doesn't get popped in a pop-all.
+ volatile uintptr_t gprs[kNumberOfCpuRegisters + 1];
+ for (size_t i = 0; i < kNumberOfCpuRegisters; ++i) {
+ gprs[kNumberOfCpuRegisters - i - 1] = gprs_[i] != NULL ? *gprs_[i] : X86Context::kBadGprBase + i;
+ }
+ // We want to load the stack pointer one slot below so that the ret will pop eip.
+ uintptr_t esp = gprs[kNumberOfCpuRegisters - ESP - 1] - kWordSize;
+ gprs[kNumberOfCpuRegisters] = esp;
+ *(reinterpret_cast<uintptr_t*>(esp)) = eip_;
__asm__ __volatile__(
- "pushl %4\n\t"
- "pushl %0\n\t"
- "pushl %1\n\t"
- "pushl %2\n\t"
- "pushl %3\n\t"
- "pushl %4\n\t"
- "pushl %5\n\t"
- "pushl %6\n\t"
- "pushl %7\n\t"
- "popal\n\t"
- "popl %%esp\n\t"
- "ret\n\t"
- : //output
- : "g"(gprs_[EAX]), "g"(gprs_[ECX]), "g"(gprs_[EDX]), "g"(gprs_[EBX]),
- "g"(gprs_[ESP]), "g"(gprs_[EBP]), "g"(gprs_[ESI]), "g"(gprs_[EDI])
- :); // clobber
+ "movl %0, %%esp\n\t" // ESP points to gprs.
+ "popal\n\t" // Load all registers except ESP and EIP with values in gprs.
+ "popl %%esp\n\t" // Load stack pointer.
+ "ret\n\t" // From higher in the stack pop eip.
+ : // output.
+ : "g"(&gprs[0]) // input.
+ :); // clobber.
#else
UNIMPLEMENTED(FATAL);
#endif
diff --git a/src/oat/runtime/x86/context_x86.h b/src/oat/runtime/x86/context_x86.h
index 3d6b1d9..ad49f8d 100644
--- a/src/oat/runtime/x86/context_x86.h
+++ b/src/oat/runtime/x86/context_x86.h
@@ -25,14 +25,17 @@
class X86Context : public Context {
public:
- X86Context();
+ X86Context() {
+ Reset();
+ }
virtual ~X86Context() {}
- // No callee saves on X86
+ virtual void Reset();
+
virtual void FillCalleeSaves(const StackVisitor& fr);
virtual void SetSP(uintptr_t new_sp) {
- gprs_[ESP] = new_sp;
+ SetGPR(ESP, new_sp);
}
virtual void SetPC(uintptr_t new_pc) {
@@ -40,17 +43,23 @@
}
virtual uintptr_t GetGPR(uint32_t reg) {
- CHECK_GE(reg, 0u);
- CHECK_LT(reg, 8u);
- return gprs_[reg];
+ CHECK_LT(reg, kNumberOfCpuRegisters);
+ return *gprs_[reg];
}
+ virtual void SetGPR(uint32_t reg, uintptr_t value);
+
virtual void SmashCallerSaves();
virtual void DoLongJump();
private:
- uintptr_t gprs_[8];
- uintptr_t eip_;
+ // Pointers to register locations, floating point registers are all caller save. Values are
+ // initialized to NULL or the special registers below.
+ uintptr_t* gprs_[kNumberOfCpuRegisters];
+ // Hold values for esp and eip if they are not located within a stack frame. EIP is somewhat
+ // special in that it cannot be encoded normally as a register operand to an instruction (except
+ // in 64bit addressing modes).
+ uintptr_t esp_, eip_;
};
} // namespace x86
} // namespace art