[liftoff][arm] Initial 32-bit port.

Bug: v8:6600
Change-Id: I9ca4c52cec6fe6d6a88483072084dbd5a174a603
Reviewed-on: https://chromium-review.googlesource.com/c/1309755
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Ben Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57405}
This commit is contained in:
George Wort 2018-11-08 15:34:58 +00:00 committed by Commit Bot
parent f321afeefd
commit 3baca98580
5 changed files with 252 additions and 24 deletions

View File

@ -5394,6 +5394,13 @@ PatchingAssembler::~PatchingAssembler() {
void PatchingAssembler::Emit(Address addr) { emit(static_cast<Instr>(addr)); }
void PatchingAssembler::PadWithNops() {
DCHECK_LE(pc_, buffer_ + buffer_size_ - kGap);
while (pc_ < buffer_ + buffer_size_ - kGap) {
nop();
}
}
UseScratchRegisterScope::UseScratchRegisterScope(Assembler* assembler)
: assembler_(assembler),
old_available_(*assembler->GetScratchRegisterList()),

View File

@ -625,6 +625,11 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
Assembler(const AssemblerOptions& options, void* buffer, int buffer_size);
virtual ~Assembler();
virtual void AbortedCodeGeneration() {
pending_32_bit_constants_.clear();
pending_64_bit_constants_.clear();
}
// GetCode emits any pending (non-emitted) code and fills the descriptor
// desc. GetCode() is idempotent; it returns the same result if no other
// Assembler functions are invoked in between GetCode() calls.
@ -1700,6 +1705,7 @@ class PatchingAssembler : public Assembler {
~PatchingAssembler();
void Emit(Address addr);
void PadWithNops();
};
// This scope utility allows scratch registers to be managed safely. The

View File

@ -13,19 +13,104 @@ namespace v8 {
namespace internal {
namespace wasm {
namespace liftoff {
// half
// slot Frame
// -----+--------------------+---------------------------
// n+3 | parameter n |
// ... | ... |
// 4 | parameter 1 | or parameter 2
// 3 | parameter 0 | or parameter 1
// 2 | (result address) | or parameter 0
// -----+--------------------+---------------------------
// 1 | return addr (lr) |
// 0 | previous frame (fp)|
// -----+--------------------+ <-- frame ptr (fp)
// -1 | 0xa: WASM_COMPILED |
// -2 | instance |
// -----+--------------------+---------------------------
// -3 | slot 0 (high) | ^
// -4 | slot 0 (low) | |
// -5 | slot 1 (high) | Frame slots
// -6 | slot 1 (low) | |
// | | v
// -----+--------------------+ <-- stack ptr (sp)
//
static_assert(2 * kPointerSize == LiftoffAssembler::kStackSlotSize,
"Slot size should be twice the size of the 32 bit pointer.");
constexpr int32_t kInstanceOffset = 2 * kPointerSize;
constexpr int32_t kFirstStackSlotOffset = kInstanceOffset + 2 * kPointerSize;
constexpr int32_t kConstantStackSpace = kPointerSize;
// kPatchInstructionsRequired sets a maximum limit of how many instructions that
// PatchPrepareStackFrame will use in order to increase the stack appropriately.
// Three instructions are required to sub a large constant, movw + movt + sub.
constexpr int32_t kPatchInstructionsRequired = 3;
inline MemOperand GetStackSlot(uint32_t index) {
int32_t offset =
kFirstStackSlotOffset + index * LiftoffAssembler::kStackSlotSize;
return MemOperand(fp, -offset);
}
inline MemOperand GetHalfStackSlot(uint32_t half_index) {
int32_t offset = kFirstStackSlotOffset +
half_index * (LiftoffAssembler::kStackSlotSize / 2);
return MemOperand(fp, -offset);
}
inline MemOperand GetHalfStackSlot(uint32_t index, RegPairHalf half) {
if (half == kLowWord) {
return GetHalfStackSlot(2 * index);
} else {
return GetHalfStackSlot(2 * index - 1);
}
}
inline MemOperand GetInstanceOperand() {
return MemOperand(fp, -kInstanceOffset);
}
} // namespace liftoff
int LiftoffAssembler::PrepareStackFrame() {
BAILOUT("PrepareStackFrame");
return 0;
if (!CpuFeatures::IsSupported(ARMv7)) {
BAILOUT("Armv6 not supported");
return 0;
}
uint32_t offset = static_cast<uint32_t>(pc_offset());
// PatchPrepareStackFrame will patch this in order to increase the stack
// appropriately. Additional nops are required as the bytes operand might
// require extra moves to encode.
for (int i = 0; i < liftoff::kPatchInstructionsRequired; i++) {
nop();
}
DCHECK_EQ(offset + liftoff::kPatchInstructionsRequired * kInstrSize,
pc_offset());
return offset;
}
void LiftoffAssembler::PatchPrepareStackFrame(int offset,
uint32_t stack_slots) {
BAILOUT("PatchPrepareStackFrame");
// Allocate space for instance plus what is needed for the frame slots.
uint32_t bytes = liftoff::kConstantStackSpace + kStackSlotSize * stack_slots;
#ifdef USE_SIMULATOR
// When using the simulator, deal with Liftoff which allocates the stack
// before checking it.
// TODO(arm): Remove this when the stack check mechanism will be updated.
if (bytes > KB / 2) {
BAILOUT("Stack limited to 512 bytes to avoid a bug in StackCheck");
return;
}
#endif
PatchingAssembler patching_assembler(AssemblerOptions{}, buffer_ + offset,
liftoff::kPatchInstructionsRequired);
patching_assembler.sub(sp, sp, Operand(bytes));
patching_assembler.PadWithNops();
}
void LiftoffAssembler::FinishCode() { CheckConstPool(true, false); }
void LiftoffAssembler::AbortCompilation() { FinishCode(); }
void LiftoffAssembler::AbortCompilation() { AbortedCodeGeneration(); }
void LiftoffAssembler::LoadConstant(LiftoffRegister reg, WasmValue value,
RelocInfo::Mode rmode) {
@ -34,11 +119,14 @@ void LiftoffAssembler::LoadConstant(LiftoffRegister reg, WasmValue value,
void LiftoffAssembler::LoadFromInstance(Register dst, uint32_t offset,
int size) {
BAILOUT("LoadFromInstance");
DCHECK_LE(offset, kMaxInt);
DCHECK_EQ(4, size);
ldr(dst, liftoff::GetInstanceOperand());
ldr(dst, MemOperand(dst, offset));
}
void LiftoffAssembler::SpillInstance(Register instance) {
BAILOUT("SpillInstance");
str(instance, liftoff::GetInstanceOperand());
}
void LiftoffAssembler::FillInstanceInto(Register dst) {
@ -71,7 +159,9 @@ void LiftoffAssembler::MoveStackValue(uint32_t dst_index, uint32_t src_index,
}
void LiftoffAssembler::Move(Register dst, Register src, ValueType type) {
BAILOUT("Move Register");
DCHECK_NE(dst, src);
DCHECK_EQ(type, kWasmI32);
TurboAssembler::Move(dst, src);
}
void LiftoffAssembler::Move(DoubleRegister dst, DoubleRegister src,
@ -282,9 +372,9 @@ void LiftoffAssembler::emit_i64_signextend_i32(LiftoffRegister dst,
BAILOUT("emit_i64_signextend_i32");
}
void LiftoffAssembler::emit_jump(Label* label) { BAILOUT("emit_jump"); }
void LiftoffAssembler::emit_jump(Label* label) { b(label); }
void LiftoffAssembler::emit_jump(Register target) { BAILOUT("emit_jump"); }
void LiftoffAssembler::emit_jump(Register target) { bx(target); }
void LiftoffAssembler::emit_cond_jump(Condition cond, Label* label,
ValueType type, Register lhs,
@ -324,7 +414,9 @@ void LiftoffAssembler::emit_f64_set_cond(Condition cond, Register dst,
}
void LiftoffAssembler::StackCheck(Label* ool_code, Register limit_address) {
BAILOUT("StackCheck");
ldr(limit_address, MemOperand(limit_address));
cmp(sp, limit_address);
b(ool_code, ls);
}
void LiftoffAssembler::CallTrapCallbackForTesting() {
@ -336,15 +428,54 @@ void LiftoffAssembler::AssertUnreachable(AbortReason reason) {
}
void LiftoffAssembler::PushRegisters(LiftoffRegList regs) {
BAILOUT("PushRegisters");
RegList core_regs = regs.GetGpList();
if (core_regs != 0) {
stm(db_w, sp, core_regs);
}
LiftoffRegList fp_regs = regs & kFpCacheRegList;
while (!fp_regs.is_empty()) {
LiftoffRegister reg = fp_regs.GetFirstRegSet();
DoubleRegister first = reg.fp();
DoubleRegister last = first;
fp_regs.clear(reg);
while (!fp_regs.is_empty()) {
LiftoffRegister reg = fp_regs.GetFirstRegSet();
int code = reg.fp().code();
// vstm can not push more than 16 registers. We have to make sure the
// condition is met.
if ((code != last.code() + 1) || ((code - first.code() + 1) > 16)) break;
last = reg.fp();
fp_regs.clear(reg);
}
vstm(db_w, sp, first, last);
}
}
void LiftoffAssembler::PopRegisters(LiftoffRegList regs) {
BAILOUT("PopRegisters");
LiftoffRegList fp_regs = regs & kFpCacheRegList;
while (!fp_regs.is_empty()) {
LiftoffRegister reg = fp_regs.GetLastRegSet();
DoubleRegister last = reg.fp();
DoubleRegister first = last;
fp_regs.clear(reg);
while (!fp_regs.is_empty()) {
LiftoffRegister reg = fp_regs.GetLastRegSet();
int code = reg.fp().code();
if ((code != first.code() - 1) || ((last.code() - code + 1) > 16)) break;
first = reg.fp();
fp_regs.clear(reg);
}
vldm(ia_w, sp, first, last);
}
RegList core_regs = regs.GetGpList();
if (core_regs != 0) {
ldm(ia_w, sp, core_regs);
}
}
void LiftoffAssembler::DropStackSlotsAndRet(uint32_t num_stack_slots) {
BAILOUT("DropStackSlotsAndRet");
Drop(num_stack_slots);
Ret();
}
void LiftoffAssembler::CallC(wasm::FunctionSig* sig,
@ -366,7 +497,9 @@ void LiftoffAssembler::CallIndirect(wasm::FunctionSig* sig,
}
void LiftoffAssembler::CallRuntimeStub(WasmCode::RuntimeStubId sid) {
BAILOUT("CallRuntimeStub");
// A direct call to a wasm runtime stub defined in this module.
// Just encode the stub index. This will be patched at relocation.
Call(static_cast<Address>(sid), RelocInfo::WASM_STUB_CALL);
}
void LiftoffAssembler::AllocateStackSlot(Register addr, uint32_t size) {
@ -378,7 +511,68 @@ void LiftoffAssembler::DeallocateStackSlot(uint32_t size) {
}
void LiftoffStackSlots::Construct() {
asm_->BAILOUT("LiftoffStackSlots::Construct");
for (auto& slot : slots_) {
const LiftoffAssembler::VarState& src = slot.src_;
switch (src.loc()) {
case LiftoffAssembler::VarState::kStack: {
switch (src.type()) {
// i32 and i64 can be treated as similar cases, i64 being previously
// split into two i32 registers
case kWasmI32:
case kWasmI64: {
UseScratchRegisterScope temps(asm_);
Register scratch = temps.Acquire();
asm_->ldr(scratch,
liftoff::GetHalfStackSlot(slot.src_index_, slot.half_));
asm_->Push(scratch);
} break;
case kWasmF32:
asm_->BAILOUT("Construct F32 from kStack");
break;
case kWasmF64: {
UseScratchRegisterScope temps(asm_);
DwVfpRegister scratch = temps.AcquireD();
asm_->vldr(scratch, liftoff::GetStackSlot(slot.src_index_));
asm_->vpush(scratch);
} break;
default:
UNREACHABLE();
}
break;
}
case LiftoffAssembler::VarState::kRegister:
switch (src.type()) {
case kWasmI64: {
LiftoffRegister reg =
slot.half_ == kLowWord ? src.reg().low() : src.reg().high();
asm_->push(reg.gp());
} break;
case kWasmI32:
asm_->push(src.reg().gp());
break;
case kWasmF32:
asm_->BAILOUT("Construct F32 from kRegister");
break;
case kWasmF64:
asm_->vpush(src.reg().fp());
break;
default:
UNREACHABLE();
}
break;
case LiftoffAssembler::VarState::KIntConst: {
DCHECK(src.type() == kWasmI32 || src.type() == kWasmI64);
UseScratchRegisterScope temps(asm_);
Register scratch = temps.Acquire();
// The high word is the sign extension of the low word.
asm_->mov(scratch,
Operand(slot.half_ == kLowWord ? src.i32_const()
: src.i32_const() >> 31));
asm_->push(scratch);
break;
}
}
}
}
} // namespace wasm

View File

@ -47,6 +47,17 @@ constexpr RegList kLiftoffAssemblerFpCacheRegs =
DoubleRegister::ListOf<f0, f2, f4, f6, f8, f10, f12, f14, f16, f18, f20,
f22, f24, f26>();
#elif V8_TARGET_ARCH_ARM
// r7: cp, r10: root, r11: fp, r12: ip, r13: sp, r14: lr, r15: pc.
constexpr RegList kLiftoffAssemblerGpCacheRegs =
Register::ListOf<r0, r1, r2, r3, r4, r5, r6, r8, r9>();
// d13: zero, d14-d15: scratch
constexpr RegList kLiftoffAssemblerFpCacheRegs =
LowDwVfpRegister::ListOf<d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11,
d12>();
#elif V8_TARGET_ARCH_ARM64
// x16: ip0, x17: ip1, x26: root, x27: cp, x29: fp, x30: lr, x31: xzr.

View File

@ -104,8 +104,15 @@ compiler::CallDescriptor* GetLoweredCallDescriptor(
: call_desc;
}
constexpr ValueType kTypesArr_ilfd[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64};
constexpr Vector<const ValueType> kTypes_ilfd = ArrayVector(kTypesArr_ilfd);
// TODO(arm): Add support for F32 registers. Fix arm32 FP registers alias.
#if V8_TARGET_ARCH_ARM
constexpr ValueType kSupportedTypesArr[] = {kWasmI32, kWasmI64, kWasmF64};
#else
constexpr ValueType kSupportedTypesArr[] = {kWasmI32, kWasmI64, kWasmF32,
kWasmF64};
#endif
constexpr Vector<const ValueType> kSupportedTypes =
ArrayVector(kSupportedTypesArr);
class LiftoffCompiler {
public:
@ -293,7 +300,8 @@ class LiftoffCompiler {
void StartFunctionBody(FullDecoder* decoder, Control* block) {
for (uint32_t i = 0; i < __ num_locals(); ++i) {
if (!CheckSupportedType(decoder, kTypes_ilfd, __ local_type(i), "param"))
if (!CheckSupportedType(decoder, kSupportedTypes, __ local_type(i),
"param"))
return;
}
@ -1161,7 +1169,7 @@ class LiftoffCompiler {
void GetGlobal(FullDecoder* decoder, Value* result,
const GlobalIndexImmediate<validate>& imm) {
const auto* global = &env_->module->globals[imm.index];
if (!CheckSupportedType(decoder, kTypes_ilfd, global->type, "global"))
if (!CheckSupportedType(decoder, kSupportedTypes, global->type, "global"))
return;
LiftoffRegList pinned;
uint32_t offset = 0;
@ -1176,7 +1184,7 @@ class LiftoffCompiler {
void SetGlobal(FullDecoder* decoder, const Value& value,
const GlobalIndexImmediate<validate>& imm) {
auto* global = &env_->module->globals[imm.index];
if (!CheckSupportedType(decoder, kTypes_ilfd, global->type, "global"))
if (!CheckSupportedType(decoder, kSupportedTypes, global->type, "global"))
return;
LiftoffRegList pinned;
uint32_t offset = 0;
@ -1483,7 +1491,8 @@ class LiftoffCompiler {
const MemoryAccessImmediate<validate>& imm,
const Value& index_val, Value* result) {
ValueType value_type = type.value_type();
if (!CheckSupportedType(decoder, kTypes_ilfd, value_type, "load")) return;
if (!CheckSupportedType(decoder, kSupportedTypes, value_type, "load"))
return;
LiftoffRegList pinned;
Register index = pinned.set(__ PopToRegister()).gp();
if (BoundsCheckMem(decoder, type.size(), imm.offset, index, pinned)) {
@ -1515,7 +1524,8 @@ class LiftoffCompiler {
const MemoryAccessImmediate<validate>& imm,
const Value& index_val, const Value& value_val) {
ValueType value_type = type.value_type();
if (!CheckSupportedType(decoder, kTypes_ilfd, value_type, "store")) return;
if (!CheckSupportedType(decoder, kSupportedTypes, value_type, "store"))
return;
LiftoffRegList pinned;
LiftoffRegister value = pinned.set(__ PopToRegister());
Register index = pinned.set(__ PopToRegister(pinned)).gp();
@ -1588,7 +1598,7 @@ class LiftoffCompiler {
if (imm.sig->return_count() > 1)
return unsupported(decoder, "multi-return");
if (imm.sig->return_count() == 1 &&
!CheckSupportedType(decoder, kTypes_ilfd, imm.sig->GetReturn(0),
!CheckSupportedType(decoder, kSupportedTypes, imm.sig->GetReturn(0),
"return"))
return;
@ -1653,7 +1663,7 @@ class LiftoffCompiler {
return unsupported(decoder, "multi-return");
}
if (imm.sig->return_count() == 1 &&
!CheckSupportedType(decoder, kTypes_ilfd, imm.sig->GetReturn(0),
!CheckSupportedType(decoder, kSupportedTypes, imm.sig->GetReturn(0),
"return")) {
return;
}