Chromium Code Reviews| Index: src/IceTargetLoweringX8664.cpp |
| diff --git a/src/IceTargetLoweringX8664.cpp b/src/IceTargetLoweringX8664.cpp |
| index 0616ba104690ce1140db45565e3499f1713e3575..13603caa9df1fe76111971aadaff486ca34379c1 100644 |
| --- a/src/IceTargetLoweringX8664.cpp |
| +++ b/src/IceTargetLoweringX8664.cpp |
| @@ -12,9 +12,9 @@ |
| /// entirely of the lowering sequence for each high-level instruction. |
| /// |
| //===----------------------------------------------------------------------===// |
| - |
| #include "IceTargetLoweringX8664.h" |
| +#include "IceDefs.h" |
| #include "IceTargetLoweringX8664Traits.h" |
| namespace X8664 { |
| @@ -130,6 +130,270 @@ FixupKind TargetX86Base<X8664::Traits>::AbsFixup = |
| // \/_____/\/_____/\/_/ \/_/\/_____/\/_/ /_/\/_/\/_/ \/_/\/_____/ |
| // |
| //------------------------------------------------------------------------------ |
| +void TargetX8664::_add_sp(Operand *Adjustment) { |
| + Variable *rsp = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_rsp, IceType_i64); |
| + if (!NeedSandboxing) { |
| + _add(rsp, Adjustment); |
| + return; |
| + } |
| + |
| + Variable *esp = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_esp, IceType_i32); |
| + Variable *r15 = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_r15, IceType_i64); |
| + |
| + // When incrementing rsp, NaCl sandboxing requires the following sequence |
| + // |
| + // .bundle_start |
| + // add Adjustment, %esp |
| + // add %r15, %rsp |
| + // .bundle_end |
| + // |
| + // In Subzero, even though rsp and esp alias each other, defining one does not |
| + // define the other. Therefore, we must emit |
| + // |
| + // .bundle_start |
| + // %esp = fake-def %rsp |
| + // add Adjustment, %esp |
| + // %rsp = fake-def %esp |
| + // add %r15, %rsp |
| + // .bundle_end |
| + // |
| + // The fake-defs ensure that the |
| + // |
| + // add Adjustment, %esp |
| + // |
| + // instruction is not DCE'd. |
| + _bundle_lock(); |
| + _redefined(Context.insert<InstFakeDef>(esp, rsp)); |
| + _add(esp, Adjustment); |
| + _redefined(Context.insert<InstFakeDef>(rsp, esp)); |
| + _add(rsp, r15); |
| + _bundle_unlock(); |
| +} |
| + |
| +void TargetX8664::_mov_sp(Operand *NewValue) { |
| + assert(NewValue->getType() == IceType_i32); |
| + |
| + Variable *esp = getPhysicalRegister(Traits::RegisterSet::Reg_esp); |
| + Variable *rsp = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_rsp, IceType_i64); |
| + |
| + if (NeedSandboxing) { |
| + _bundle_lock(); |
| + } |
| + |
| + _redefined(Context.insert<InstFakeDef>(esp, rsp)); |
| + _redefined(_mov(esp, NewValue)); |
| + _redefined(Context.insert<InstFakeDef>(rsp, esp)); |
| + |
| + if (!NeedSandboxing) { |
| + return; |
| + } |
| + |
| + Variable *r15 = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_r15, IceType_i64); |
| + _add(rsp, r15); |
| + _bundle_unlock(); |
| +} |
| + |
| +void TargetX8664::_push_rbp() { |
| + assert(NeedSandboxing); |
| + |
| + Constant *_0 = Ctx->getConstantZero(IceType_i32); |
| + Variable *ebp = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_ebp, IceType_i32); |
| + Variable *rsp = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_rsp, IceType_i64); |
| + auto *TopOfStack = llvm::cast<X86OperandMem>( |
| + legalize(X86OperandMem::create(Func, IceType_i32, rsp, _0), |
| + Legal_Reg | Legal_Mem)); |
| + |
| + // Emits a sequence: |
| + // |
| + // .bundle_start |
| + // push 0 |
| + // mov %ebp, %(rsp) |
| + // .bundle_end |
| + // |
| + // to avoid leaking the upper 32-bits (i.e., the sandbox address.) |
| + _bundle_lock(); |
| + _push(_0); |
| + Context.insert<typename Traits::Insts::Store>(ebp, TopOfStack); |
| + _bundle_unlock(); |
| +} |
| + |
| +Traits::X86OperandMem *TargetX8664::_sandbox_mem_reference(X86OperandMem *Mem) { |
| + // In x86_64-nacl, all memory references are relative to %r15 (i.e., %rzp.) |
| + // NaCl sandboxing also requires that any registers that are not %rsp and |
| + // %rbp to be 'truncted' to 32-bit before memory access. |
|
Jim Stichnoth
2016/01/14 00:09:52
truncated
John
2016/01/14 23:18:25
(I meant to write trunc'ed.)
Done.
|
| + assert(NeedSandboxing); |
| + Variable *Base = Mem->getBase(); |
| + Variable *Index = Mem->getIndex(); |
| + uint16_t Shift = 0; |
| + Variable *r15 = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_r15, IceType_i64); |
| + Constant *Offset = Mem->getOffset(); |
| + Variable *T = nullptr; |
| + |
| + if (Mem->getIsRebased()) { |
| + // If Mem.IsRebased, then we don't need to update Mem to contain a reference |
| + // to %r15, but we still need to truncate Mem.Index (if any) to 32-bit. |
| + assert(r15 == Base); |
| + T = Index; |
| + Shift = Mem->getShift(); |
| + } else if (Base != nullptr && Index != nullptr) { |
| + // Another approach could be to emit an |
| + // |
| + // lea Mem, %T |
| + // |
| + // And then update Mem.Base = r15, Mem.Index = T, Mem.Shift = 0 |
| + llvm::report_fatal_error("memory reference contains base and index."); |
| + } else if (Base != nullptr) { |
| + T = Base; |
| + } else if (Index != nullptr) { |
| + T = Index; |
| + Shift = Mem->getShift(); |
| + } |
| + |
| + // NeedsLea is a flags indicating whether Mem needs to be materialized to a |
| + // GPR prior to being used. A LEA is needed if Mem.Offset is a constant |
| + // relocatable, or if Mem.Offset is negative. In both these cases, the LEA is |
| + // needed to ensure the sandboxed memory operand will only use the lower |
| + // 32-bits of T+Offset. |
| + bool NeedsLea = false; |
| + if (const auto *Offset = Mem->getOffset()) { |
| + if (llvm::isa<ConstantRelocatable>(Offset)) { |
| + NeedsLea = true; |
| + } else if (const auto *Imm = llvm::cast<ConstantInteger32>(Offset)) { |
| + NeedsLea = Imm->getValue() < 0; |
| + } |
| + } |
| + |
| + int32_t RegNum = Variable::NoRegister; |
| + int32_t RegNum32 = Variable::NoRegister; |
| + if (T != nullptr) { |
| + if (T->hasReg()) { |
| + RegNum = Traits::getGprForType(IceType_i64, T->getRegNum()); |
| + RegNum32 = Traits::getGprForType(IceType_i32, RegNum); |
| + switch (RegNum) { |
| + case Traits::RegisterSet::Reg_rsp: |
| + case Traits::RegisterSet::Reg_rbp: |
| + // Memory operands referencing rsp/rbp do not need to be sandboxed. |
| + return Mem; |
| + } |
| + } |
| + |
| + switch (T->getType()) { |
| + default: |
| + case IceType_i64: |
| + // Even though "default:" would also catch T.Type == IceType_i64, an |
| + // explicit 'case IceType_i64' shows that memory operands are always |
| + // supposed to be 32-bits. |
| + llvm::report_fatal_error("Mem pointer should be 32-bit."); |
| + case IceType_i32: { |
| + Variable *T64 = makeReg(IceType_i64, RegNum); |
| + auto *Movzx = _movzx(T64, T); |
| + if (!NeedsLea) { |
| + // This movzx is only needed when Mem does not need to be lea'd into a |
| + // temporary. If an lea is goign to be emitted, then eliding this movzx |
|
Jim Stichnoth
2016/01/14 00:09:52
going
John
2016/01/14 23:18:25
Done.
|
| + // is safe because the emitted lea will write a 32-bit result -- |
| + // implicitly zero-extended to 64-bit. |
| + Movzx->setMustKeep(); |
| + } |
| + T = T64; |
| + } break; |
| + } |
| + } |
| + |
| + if (NeedsLea) { |
| + Variable *NewT = makeReg(IceType_i32, RegNum32); |
| + Variable *Base = T; |
| + Variable *Index = T; |
| + static constexpr bool NotRebased = false; |
| + if (Shift == 0) { |
| + Index = nullptr; |
| + } else { |
| + Base = nullptr; |
| + } |
| + _lea(NewT, Traits::X86OperandMem::create( |
| + Func, Mem->getType(), Base, Offset, Index, Shift, |
| + Traits::X86OperandMem::DefaultSegment, NotRebased)); |
| + |
| + T = makeReg(IceType_i64, RegNum); |
| + _movzx(T, NewT); |
| + Shift = 0; |
| + Offset = nullptr; |
| + } |
| + |
| + static constexpr bool IsRebased = true; |
| + return Traits::X86OperandMem::create( |
| + Func, Mem->getType(), r15, Offset, T, Shift, |
| + Traits::X86OperandMem::DefaultSegment, IsRebased); |
| +} |
| + |
| +void TargetX8664::_sub_sp(Operand *Adjustment) { |
| + Variable *rsp = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_rsp, IceType_i64); |
| + if (!NeedSandboxing) { |
| + _sub(rsp, Adjustment); |
| + return; |
| + } |
| + |
| + Variable *esp = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_esp, IceType_i32); |
| + Variable *r15 = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_r15, IceType_i64); |
| + |
| + // .bundle_start |
| + // sub Adjustment, %esp |
| + // add %r15, %rsp |
| + // .bundle_end |
| + _bundle_lock(); |
| + _redefined(Context.insert<InstFakeDef>(esp, rsp)); |
| + _sub(esp, Adjustment); |
| + _redefined(Context.insert<InstFakeDef>(rsp, esp)); |
| + _add(rsp, r15); |
| + _bundle_unlock(); |
| +} |
| + |
| +void TargetX8664::initSandbox() { |
| + assert(NeedSandboxing); |
| + Context.init(Func->getEntryNode()); |
| + Context.setInsertPoint(Context.getCur()); |
| + Variable *r15 = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_r15, IceType_i64); |
| + Context.insert<InstFakeDef>(r15); |
| + Context.insert<InstFakeUse>(r15); |
| +} |
| + |
| +void TargetX8664::lowerIndirectJump(Variable *JumpTarget) { |
| + if (!NeedSandboxing) { |
| + Variable *T = makeReg(IceType_i64); |
| + _movzx(T, JumpTarget); |
| + JumpTarget = T; |
| + } else { |
| + Variable *T = makeReg(IceType_i32); |
| + Variable *T64 = makeReg(IceType_i64); |
| + Variable *r15 = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_r15, IceType_i64); |
| + |
| + _mov(T, JumpTarget); |
| + _bundle_lock(); |
| + const SizeT BundleSize = |
| + 1 << Func->getAssembler<>()->getBundleAlignLog2Bytes(); |
| + _and(T, Ctx->getConstantInt32(~(BundleSize - 1))); |
| + _movzx(T64, T); |
| + _add(T64, r15); |
| + JumpTarget = T64; |
| + } |
| + |
| + _jmp(JumpTarget); |
| + if (NeedSandboxing) |
| + _bundle_unlock(); |
| +} |
| + |
| namespace { |
| static inline TargetX8664::Traits::RegisterSet::AllRegisters |
| getRegisterForXmmArgNum(uint32_t ArgNum) { |
| @@ -317,21 +581,53 @@ void TargetX8664::lowerCall(const InstCall *Instr) { |
| } |
| } |
| - Operand *CallTarget = legalize(Instr->getCallTarget(), Legal_Reg | Legal_Imm); |
| - if (auto *CallTargetR = llvm::dyn_cast<Variable>(CallTarget)) { |
| - // x86-64 in Subzero is ILP32. Therefore, CallTarget is i32, but the emitted |
| - // call needs a i64 register (for textual asm.) |
| - Variable *T = makeReg(IceType_i64); |
| - _movzx(T, CallTargetR); |
| - CallTarget = T; |
| - } |
| - const bool NeedSandboxing = Ctx->getFlags().getUseSandboxing(); |
| - if (NeedSandboxing) { |
| - llvm_unreachable("X86-64 Sandboxing codegen not implemented."); |
| - } |
| - auto *NewCall = Context.insert<Traits::Insts::Call>(ReturnReg, CallTarget); |
| - if (NeedSandboxing) { |
| - llvm_unreachable("X86-64 Sandboxing codegen not implemented."); |
| + InstX86Label *ReturnAddress = nullptr; |
| + Operand *CallTarget = |
| + legalize(Instr->getCallTarget(), Legal_Reg | Legal_Imm | Legal_AddrAbs); |
| + auto *CallTargetR = llvm::dyn_cast<Variable>(CallTarget); |
| + Inst *NewCall = nullptr; |
| + if (!NeedSandboxing) { |
| + if (CallTargetR != nullptr) { |
| + // x86-64 in Subzero is ILP32. Therefore, CallTarget is i32, but the |
| + // emitted call needs a i64 register (for textual asm.) |
| + Variable *T = makeReg(IceType_i64); |
| + _movzx(T, CallTargetR); |
| + CallTarget = T; |
| + } |
| + NewCall = Context.insert<Traits::Insts::Call>(ReturnReg, CallTarget); |
| + } else { |
| + ReturnAddress = InstX86Label::create(Func, this); |
| + ReturnAddress->setIsReturnLocation(true); |
| + constexpr bool SuppressMangling = true; |
| + if (CallTargetR == nullptr) { |
| + _bundle_lock(InstBundleLock::Opt_PadToEnd); |
| + _push(Ctx->getConstantSym(0, ReturnAddress->getName(Func), |
| + SuppressMangling)); |
| + } else { |
| + Variable *T = makeReg(IceType_i32); |
| + Variable *T64 = makeReg(IceType_i64); |
| + Variable *r15 = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_r15, IceType_i64); |
| + |
| + _mov(T, CallTargetR); |
| + _bundle_lock(InstBundleLock::Opt_PadToEnd); |
| + _push(Ctx->getConstantSym(0, ReturnAddress->getName(Func), |
| + SuppressMangling)); |
| + const SizeT BundleSize = |
| + 1 << Func->getAssembler<>()->getBundleAlignLog2Bytes(); |
| + _and(T, Ctx->getConstantInt32(~(BundleSize - 1))); |
| + _movzx(T64, T); |
| + _add(T64, r15); |
| + CallTarget = T64; |
| + } |
| + |
| + NewCall = Context.insert<Traits::Insts::Jmp>(CallTarget); |
| + _bundle_unlock(); |
| + if (ReturnReg != nullptr) { |
| + Context.insert<InstFakeDef>(ReturnReg); |
| + } |
| + |
| + Context.insert(ReturnAddress); |
| } |
| // Insert a register-kill pseudo instruction. |
| @@ -523,13 +819,26 @@ void TargetX8664::addProlog(CfgNode *Node) { |
| if (CalleeSaves[i] && RegsUsed[i]) |
| Pushed[Canonical] = true; |
| } |
| + |
| + Variable *rbp = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_rbp, IceType_i64); |
| + Variable *ebp = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_ebp, IceType_i32); |
| + Variable *rsp = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_rsp, IceType_i64); |
| + |
| for (SizeT i = 0; i < Pushed.size(); ++i) { |
| if (!Pushed[i]) |
| continue; |
| assert(static_cast<int32_t>(i) == Traits::getBaseReg(i)); |
| ++NumCallee; |
| PreservedRegsSizeBytes += typeWidthInBytes(IceType_i64); |
| - _push(getPhysicalRegister(i, IceType_i64)); |
| + Variable *Src = getPhysicalRegister(i, IceType_i64); |
| + if (Src != rbp || !NeedSandboxing) { |
| + _push(getPhysicalRegister(i, IceType_i64)); |
| + } else { |
| + _push_rbp(); |
| + } |
| } |
| Ctx->statsUpdateRegistersSaved(NumCallee); |
| @@ -538,14 +847,27 @@ void TargetX8664::addProlog(CfgNode *Node) { |
| assert((RegsUsed & getRegisterSet(RegSet_FramePointer, RegSet_None)) |
| .count() == 0); |
| PreservedRegsSizeBytes += typeWidthInBytes(IceType_i64); |
| - Variable *ebp = |
| - getPhysicalRegister(Traits::RegisterSet::Reg_rbp, IceType_i64); |
| Variable *esp = |
| - getPhysicalRegister(Traits::RegisterSet::Reg_rsp, IceType_i64); |
| - _push(ebp); |
| - _mov(ebp, esp); |
| + getPhysicalRegister(Traits::RegisterSet::Reg_esp, IceType_i32); |
| + Variable *r15 = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_r15, IceType_i64); |
| + |
| + if (!NeedSandboxing) { |
| + _push(rbp); |
| + _mov(rbp, rsp); |
| + } else { |
| + _push_rbp(); |
| + |
| + _bundle_lock(); |
| + _redefined(Context.insert<InstFakeDef>(ebp, rbp)); |
| + _redefined(Context.insert<InstFakeDef>(esp, rsp)); |
| + _mov(ebp, esp); |
| + _redefined(Context.insert<InstFakeDef>(rsp, esp)); |
| + _add(rbp, r15); |
| + _bundle_unlock(); |
| + } |
| // Keep ebp live for late-stage liveness analysis (e.g. asm-verbose mode). |
| - Context.insert<InstFakeUse>(ebp); |
| + Context.insert<InstFakeUse>(rbp); |
| } |
| // Align the variables area. SpillAreaPaddingBytes is the size of the region |
| @@ -582,8 +904,12 @@ void TargetX8664::addProlog(CfgNode *Node) { |
| SpillAreaSizeBytes += FixedAllocaSizeBytes; |
| // Generate "sub esp, SpillAreaSizeBytes" |
| if (SpillAreaSizeBytes) { |
| - _sub(getPhysicalRegister(getStackReg(), IceType_i64), |
| - Ctx->getConstantInt32(SpillAreaSizeBytes)); |
| + if (NeedSandboxing) { |
| + _sub_sp(Ctx->getConstantInt32(SpillAreaSizeBytes)); |
| + } else { |
| + _sub(getPhysicalRegister(getStackReg(), IceType_i64), |
| + Ctx->getConstantInt32(SpillAreaSizeBytes)); |
| + } |
| // If the fixed allocas are aligned more than the stack frame, align the |
| // stack pointer accordingly. |
| if (PrologEmitsFixedAllocas && |
| @@ -699,21 +1025,45 @@ void TargetX8664::addEpilog(CfgNode *Node) { |
| Context.init(Node); |
| Context.setInsertPoint(InsertPoint); |
| - Variable *esp = |
| + Variable *rsp = |
| getPhysicalRegister(Traits::RegisterSet::Reg_rsp, IceType_i64); |
| - if (IsEbpBasedFrame) { |
| - Variable *ebp = |
| + |
| + if (!IsEbpBasedFrame) { |
| + // add rsp, SpillAreaSizeBytes |
| + if (SpillAreaSizeBytes != 0) { |
| + _add_sp(Ctx->getConstantInt32(SpillAreaSizeBytes)); |
| + } |
| + } else { |
| + Variable *rbp = |
| getPhysicalRegister(Traits::RegisterSet::Reg_rbp, IceType_i64); |
| + Variable *ebp = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_ebp, IceType_i32); |
| // For late-stage liveness analysis (e.g. asm-verbose mode), adding a fake |
| - // use of esp before the assignment of esp=ebp keeps previous esp |
| + // use of rsp before the assignment of rsp=rbp keeps previous rsp |
| // adjustments from being dead-code eliminated. |
| - Context.insert<InstFakeUse>(esp); |
| - _mov(esp, ebp); |
| - _pop(ebp); |
| - } else { |
| - // add esp, SpillAreaSizeBytes |
| - if (SpillAreaSizeBytes) |
| - _add(esp, Ctx->getConstantInt32(SpillAreaSizeBytes)); |
| + Context.insert<InstFakeUse>(rsp); |
| + if (!NeedSandboxing) { |
| + _mov(rsp, rbp); |
| + _pop(rbp); |
| + } else { |
| + _mov_sp(ebp); |
| + |
| + Variable *r15 = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_r15, IceType_i64); |
| + Variable *rcx = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_rcx, IceType_i64); |
| + Variable *ecx = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_ecx, IceType_i32); |
| + |
| + _pop(rcx); |
| + Context.insert<InstFakeDef>(ecx, rcx); |
| + _bundle_lock(); |
| + _mov(ebp, ecx); |
| + |
| + _redefined(Context.insert<InstFakeDef>(rbp, ebp)); |
| + _add(rbp, r15); |
| + _bundle_unlock(); |
| + } |
| } |
| // Add pop instructions for preserved registers. |
| @@ -734,9 +1084,34 @@ void TargetX8664::addEpilog(CfgNode *Node) { |
| _pop(getPhysicalRegister(i, IceType_i64)); |
| } |
| - if (Ctx->getFlags().getUseSandboxing()) { |
| - llvm_unreachable("X86-64 Sandboxing codegen not implemented."); |
| + if (!NeedSandboxing) { |
| + return; |
| + } |
| + |
| + Variable *T_rcx = makeReg(IceType_i64, Traits::RegisterSet::Reg_rcx); |
| + Variable *T_ecx = makeReg(IceType_i32, Traits::RegisterSet::Reg_ecx); |
| + _pop(T_rcx); |
| + _mov(T_ecx, T_rcx); |
| + |
| + // lowerIndirectJump(T_ecx); |
| + Variable *r15 = |
| + getPhysicalRegister(Traits::RegisterSet::Reg_r15, IceType_i64); |
| + |
| + _bundle_lock(); |
| + const SizeT BundleSize = 1 |
| + << Func->getAssembler<>()->getBundleAlignLog2Bytes(); |
| + _and(T_ecx, Ctx->getConstantInt32(~(BundleSize - 1))); |
| + Context.insert<InstFakeDef>(T_rcx, T_ecx); |
| + _add(T_rcx, r15); |
| + |
| + _jmp(T_rcx); |
| + _bundle_unlock(); |
| + |
| + if (RI->getSrcSize()) { |
| + auto *RetValue = llvm::cast<Variable>(RI->getSrc(0)); |
| + Context.insert<InstFakeUse>(RetValue); |
| } |
| + RI->setDeleted(); |
| } |
| void TargetX8664::emitJumpTable(const Cfg *Func, |