Commit a9716807 authored by Jakob Gruber's avatar Jakob Gruber Committed by Commit Bot

[ia32] Add scopes and verification to assist in root register ports

In the near future all ia32 ASM builtins must be audited & possibly refactored
to ensure they do not address ebx (= kRootRegister).

This CL adds mechanisms to verify ebx usage. SupportsRootRegisterScope marks
regions that are root-register-ready (i.e. does not use ebx).
AllowExplicitEbxAccessScope marks regions that are explicitly allowed to use
ebx, e.g. because they spill and restore its value at all boundaries and do not
contain any root-relative accesses.

Consistency is verified by calling the new AssertIsAddressable function at
strategic spots in the Assembler.

All of this code is temporary and should be removed once ia32 fully supports
the kRootRegister.

Bug: v8:6666
Change-Id: I7c5514794db0da889bdae9e3c23bc0d54780879d
Reviewed-on: https://chromium-review.googlesource.com/1226805Reviewed-by: 's avatarSigurd Schneider <sigurds@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55992}
parent f4c14fd9
......@@ -323,6 +323,10 @@ void Assembler::deserialization_set_target_internal_reference_at(
void Operand::set_sib(ScaleFactor scale, Register index, Register base) {
#ifdef DEBUG
AddUsedRegister(index);
AddUsedRegister(base);
#endif
DCHECK_EQ(len_, 1);
DCHECK_EQ(scale & -4, 0);
// Use SIB with no index register only for base esp.
......
......@@ -523,6 +523,7 @@ void Assembler::push_imm32(int32_t imm32) {
void Assembler::push(Register src) {
AssertIsAddressable(src);
EnsureSpace ensure_space(this);
EMIT(0x50 | src.code());
}
......@@ -535,6 +536,7 @@ void Assembler::push(Operand src) {
void Assembler::pop(Register dst) {
AssertIsAddressable(dst);
DCHECK_NOT_NULL(reloc_info_writer.last_pc());
EnsureSpace ensure_space(this);
EMIT(0x58 | dst.code());
......@@ -606,6 +608,7 @@ void Assembler::mov_w(Operand dst, const Immediate& src) {
void Assembler::mov(Register dst, int32_t imm32) {
AssertIsAddressable(dst);
EnsureSpace ensure_space(this);
EMIT(0xB8 | dst.code());
emit(imm32);
......@@ -613,12 +616,14 @@ void Assembler::mov(Register dst, int32_t imm32) {
void Assembler::mov(Register dst, const Immediate& x) {
AssertIsAddressable(dst);
EnsureSpace ensure_space(this);
EMIT(0xB8 | dst.code());
emit(x);
}
void Assembler::mov(Register dst, Handle<HeapObject> handle) {
AssertIsAddressable(dst);
EnsureSpace ensure_space(this);
EMIT(0xB8 | dst.code());
emit(handle);
......@@ -632,6 +637,8 @@ void Assembler::mov(Register dst, Operand src) {
void Assembler::mov(Register dst, Register src) {
AssertIsAddressable(src);
AssertIsAddressable(dst);
EnsureSpace ensure_space(this);
EMIT(0x89);
EMIT(0xC0 | src.code() << 3 | dst.code());
......@@ -736,6 +743,8 @@ void Assembler::stos() {
void Assembler::xchg(Register dst, Register src) {
AssertIsAddressable(src);
AssertIsAddressable(dst);
EnsureSpace ensure_space(this);
if (src == eax || dst == eax) { // Single-byte encoding.
EMIT(0x90 | (src == eax ? dst.code() : src.code()));
......@@ -966,6 +975,7 @@ void Assembler::cmpw_ax(Operand op) {
void Assembler::dec_b(Register dst) {
AssertIsAddressable(dst);
CHECK(dst.is_byte_register());
EnsureSpace ensure_space(this);
EMIT(0xFE);
......@@ -980,6 +990,7 @@ void Assembler::dec_b(Operand dst) {
void Assembler::dec(Register dst) {
AssertIsAddressable(dst);
EnsureSpace ensure_space(this);
EMIT(0x48 | dst.code());
}
......@@ -1010,6 +1021,7 @@ void Assembler::div(Operand src) {
void Assembler::imul(Register reg) {
AssertIsAddressable(reg);
EnsureSpace ensure_space(this);
EMIT(0xF7);
EMIT(0xE8 | reg.code());
......@@ -1042,6 +1054,7 @@ void Assembler::imul(Register dst, Operand src, int32_t imm32) {
void Assembler::inc(Register dst) {
AssertIsAddressable(dst);
EnsureSpace ensure_space(this);
EMIT(0x40 | dst.code());
}
......@@ -1060,6 +1073,7 @@ void Assembler::lea(Register dst, Operand src) {
void Assembler::mul(Register src) {
AssertIsAddressable(src);
EnsureSpace ensure_space(this);
EMIT(0xF7);
EMIT(0xE0 | src.code());
......@@ -1067,6 +1081,7 @@ void Assembler::mul(Register src) {
void Assembler::neg(Register dst) {
AssertIsAddressable(dst);
EnsureSpace ensure_space(this);
EMIT(0xF7);
EMIT(0xD8 | dst.code());
......@@ -1080,6 +1095,7 @@ void Assembler::neg(Operand dst) {
void Assembler::not_(Register dst) {
AssertIsAddressable(dst);
EnsureSpace ensure_space(this);
EMIT(0xF7);
EMIT(0xD0 | dst.code());
......@@ -1116,6 +1132,7 @@ void Assembler::or_(Operand dst, Register src) {
void Assembler::rcl(Register dst, uint8_t imm8) {
AssertIsAddressable(dst);
EnsureSpace ensure_space(this);
DCHECK(is_uint5(imm8)); // illegal shift count
if (imm8 == 1) {
......@@ -1130,6 +1147,7 @@ void Assembler::rcl(Register dst, uint8_t imm8) {
void Assembler::rcr(Register dst, uint8_t imm8) {
AssertIsAddressable(dst);
EnsureSpace ensure_space(this);
DCHECK(is_uint5(imm8)); // illegal shift count
if (imm8 == 1) {
......@@ -1287,6 +1305,7 @@ void Assembler::test(Register reg, const Immediate& imm) {
return;
}
AssertIsAddressable(reg);
EnsureSpace ensure_space(this);
// This is not using emit_arith because test doesn't support
// sign-extension of 8-bit operands.
......@@ -1327,6 +1346,7 @@ void Assembler::test(Operand op, const Immediate& imm) {
}
void Assembler::test_b(Register reg, Immediate imm8) {
AssertIsAddressable(reg);
DCHECK(imm8.is_uint8());
EnsureSpace ensure_space(this);
// Only use test against byte for registers that have a byte
......@@ -1356,6 +1376,7 @@ void Assembler::test_b(Operand op, Immediate imm8) {
}
void Assembler::test_w(Register reg, Immediate imm16) {
AssertIsAddressable(reg);
DCHECK(imm16.is_int16() || imm16.is_uint16());
EnsureSpace ensure_space(this);
if (reg == eax) {
......@@ -1412,6 +1433,7 @@ void Assembler::xor_(Operand dst, const Immediate& x) {
}
void Assembler::bswap(Register dst) {
AssertIsAddressable(dst);
EnsureSpace ensure_space(this);
EMIT(0x0F);
EMIT(0xC8 + dst.code());
......@@ -2163,6 +2185,7 @@ void Assembler::sahf() {
void Assembler::setcc(Condition cc, Register reg) {
AssertIsAddressable(reg);
DCHECK(reg.is_byte_register());
EnsureSpace ensure_space(this);
EMIT(0x0F);
......@@ -3154,11 +3177,13 @@ void Assembler::emit_sse_operand(XMMRegister dst, XMMRegister src) {
void Assembler::emit_sse_operand(Register dst, XMMRegister src) {
AssertIsAddressable(dst);
EMIT(0xC0 | dst.code() << 3 | src.code());
}
void Assembler::emit_sse_operand(XMMRegister dst, Register src) {
AssertIsAddressable(src);
EMIT(0xC0 | (dst.code() << 3) | src.code());
}
......@@ -3244,6 +3269,7 @@ void Assembler::GrowBuffer() {
void Assembler::emit_arith_b(int op1, int op2, Register dst, int imm8) {
AssertIsAddressable(dst);
DCHECK(is_uint8(op1) && is_uint8(op2)); // wrong opcode
DCHECK(is_uint8(imm8));
DCHECK_EQ(op1 & 0x01, 0); // should be 8bit operation
......@@ -3254,6 +3280,7 @@ void Assembler::emit_arith_b(int op1, int op2, Register dst, int imm8) {
void Assembler::emit_arith(int sel, Operand dst, const Immediate& x) {
AssertIsAddressable(dst);
DCHECK((0 <= sel) && (sel <= 7));
Register ireg = Register::from_code(sel);
if (x.is_int8()) {
......@@ -3280,6 +3307,8 @@ void Assembler::emit_operand(XMMRegister reg, Operand adr) {
}
void Assembler::emit_operand(int code, Operand adr) {
AssertIsAddressable(adr);
AssertIsAddressable(Register::from_code(code));
// Isolate-independent code may not embed relocatable addresses.
DCHECK(!options().isolate_independent_code ||
adr.rmode_ != RelocInfo::CODE_TARGET);
......@@ -3367,6 +3396,15 @@ void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
reloc_info_writer.Write(&rinfo);
}
#ifdef DEBUG
void Assembler::AssertIsAddressable(const Operand& operand) {
DCHECK(is_ebx_addressable_ || !operand.UsesEbx());
}
void Assembler::AssertIsAddressable(const Register& reg) {
DCHECK(is_ebx_addressable_ || reg != ebx);
}
#endif // DEBUG
} // namespace internal
} // namespace v8
......
......@@ -361,10 +361,17 @@ class V8_EXPORT_PRIVATE Operand {
// register.
Register reg() const;
#ifdef DEBUG
bool UsesEbx() const { return uses_ebx_; }
#endif // DEBUG
private:
// Set the ModRM byte without an encoded 'reg' register. The
// register is encoded later as part of the emit_operand operation.
inline void set_modrm(int mod, Register rm) {
#ifdef DEBUG
AddUsedRegister(rm);
#endif
DCHECK_EQ(mod & -4, 0);
buf_[0] = mod << 6 | rm.code();
len_ = 1;
......@@ -391,12 +398,23 @@ class V8_EXPORT_PRIVATE Operand {
// Only valid if len_ > 4.
RelocInfo::Mode rmode_ = RelocInfo::NONE;
#ifdef DEBUG
// TODO(v8:6666): Remove once kRootRegister support is complete.
bool uses_ebx_ = false;
void AddUsedRegister(Register reg) {
if (reg == ebx) uses_ebx_ = true;
}
#endif // DEBUG
// TODO(clemensh): Get rid of this friendship, or make Operand immutable.
friend class Assembler;
};
ASSERT_TRIVIALLY_COPYABLE(Operand);
// TODO(v8:6666): Re-enable globally once kRootRegister support is complete.
#ifndef DEBUG
static_assert(sizeof(Operand) <= 2 * kPointerSize,
"Operand must be small enough to pass it by value");
#endif
// -----------------------------------------------------------------------------
// A Displacement describes the 32bit immediate field of an instruction which
......@@ -1760,6 +1778,30 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
UNREACHABLE();
}
// Temporary helper data structures while adding kRootRegister support to ia32
// builtins. The SupportsRootRegisterScope is intended to mark each builtin
// and helper that fully supports the root register, i.e. that does not
// clobber ebx. The AllowExplicitEbxAccessScope marks regions that are allowed
// to clobber ebx, e.g. when ebx is spilled and restored.
// TODO(v8:6666): Remove once kRootRegister is fully supported.
template <bool new_value>
class SetRootRegisterSupportScope final {
public:
explicit SetRootRegisterSupportScope(Assembler* assembler)
: assembler_(assembler), old_value_(assembler->is_ebx_addressable_) {
assembler_->is_ebx_addressable_ = new_value;
}
~SetRootRegisterSupportScope() {
assembler_->is_ebx_addressable_ = old_value_;
}
private:
Assembler* assembler_;
const bool old_value_;
};
typedef SetRootRegisterSupportScope<false> SupportsRootRegisterScope;
typedef SetRootRegisterSupportScope<true> AllowExplicitEbxAccessScope;
protected:
void emit_sse_operand(XMMRegister reg, Operand adr);
void emit_sse_operand(XMMRegister dst, XMMRegister src);
......@@ -1768,6 +1810,16 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
byte* addr_at(int pos) { return buffer_ + pos; }
#ifdef DEBUG
// TODO(v8:6666): Remove once kRootRegister is fully supported.
void AssertIsAddressable(const Register& reg);
void AssertIsAddressable(const Operand& operand);
#else
// An empty inline definition to avoid slowing down release builds.
void AssertIsAddressable(const Register&) {}
void AssertIsAddressable(const Operand&) {}
#endif // DEBUG
bool is_ebx_addressable_ = true;
private:
uint32_t long_at(int pos) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment