GlobalISel: translate dynamic alloca instructions.

llvm-svn: 294022
This commit is contained in:
Tim Northover 2017-02-03 18:22:45 +00:00
parent 6de201ec32
commit c3e3f59d12
5 changed files with 181 additions and 16 deletions

View File

@ -155,11 +155,6 @@ private:
bool translateCast(unsigned Opcode, const User &U,
MachineIRBuilder &MIRBuilder);
/// Translate static alloca instruction (i.e. one of constant size and in the
/// first basic block).
bool translateStaticAlloca(const AllocaInst &Inst,
MachineIRBuilder &MIRBuilder);
/// Translate a phi instruction.
bool translatePHI(const User &U, MachineIRBuilder &MIRBuilder);
@ -202,6 +197,8 @@ private:
bool translateGetElementPtr(const User &U, MachineIRBuilder &MIRBuilder);
bool translateAlloca(const User &U, MachineIRBuilder &MIRBuilder);
/// Translate return (ret) instruction.
/// The target needs to implement CallLowering::lowerReturn for
/// this to succeed.
@ -239,9 +236,6 @@ private:
bool translateSRem(const User &U, MachineIRBuilder &MIRBuilder) {
return translateBinaryOp(TargetOpcode::G_SREM, U, MIRBuilder);
}
bool translateAlloca(const User &U, MachineIRBuilder &MIRBuilder) {
return translateStaticAlloca(cast<AllocaInst>(U), MIRBuilder);
}
bool translateIntToPtr(const User &U, MachineIRBuilder &MIRBuilder) {
return translateCast(TargetOpcode::G_INTTOPTR, U, MIRBuilder);
}

View File

@ -246,6 +246,19 @@ public:
MachineInstrBuilder buildUAdde(unsigned Res, unsigned CarryOut, unsigned Op0,
unsigned Op1, unsigned CarryIn);
/// Build and insert \p Res<def> = G_AND \p Op0, \p Op1
///
/// G_AND sets \p Res to the bitwise and of integer parameters \p Op0 and \p
/// Op1.
///
/// \pre setBasicBlock or setMI must have been called.
/// \pre \p Res, \p Op0 and \p Op1 must be generic virtual registers
/// with the same (scalar or vector) type).
///
/// \return a MachineInstrBuilder for the newly created instruction.
MachineInstrBuilder buildAnd(unsigned Res, unsigned Op0,
unsigned Op1);
/// Build and insert \p Res<def> = G_ANYEXT \p Op0
///
/// G_ANYEXT produces a register of the specified width, with bits 0 to
@ -299,6 +312,16 @@ public:
/// \return The newly created instruction.
MachineInstrBuilder buildSExtOrTrunc(unsigned Res, unsigned Op);
/// Build and insert \p Res<def> = G_ZEXT \p Op, \p Res = G_TRUNC \p Op, or
/// \p Res = COPY \p Op depending on the differing sizes of \p Res and \p Op.
/// ///
/// \pre setBasicBlock or setMI must have been called.
/// \pre \p Res must be a generic virtual register with scalar or vector type.
/// \pre \p Op must be a generic virtual register with scalar or vector type.
///
/// \return The newly created instruction.
MachineInstrBuilder buildZExtOrTrunc(unsigned Res, unsigned Op);
/// Build and insert G_BR \p Dest
///
/// G_BR is an unconditional branch to \p Dest.

View File

@ -28,6 +28,7 @@
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Value.h"
#include "llvm/Target/TargetFrameLowering.h"
#include "llvm/Target/TargetIntrinsicInfo.h"
#include "llvm/Target/TargetLowering.h"
@ -820,15 +821,81 @@ bool IRTranslator::translateLandingPad(const User &U,
return true;
}
bool IRTranslator::translateStaticAlloca(const AllocaInst &AI,
MachineIRBuilder &MIRBuilder) {
if (!TPC->isGlobalISelAbortEnabled() && !AI.isStaticAlloca())
return false;
bool IRTranslator::translateAlloca(const User &U,
MachineIRBuilder &MIRBuilder) {
auto &AI = cast<AllocaInst>(U);
assert(AI.isStaticAlloca() && "only handle static allocas now");
unsigned Res = getOrCreateVReg(AI);
int FI = getOrCreateFrameIndex(AI);
MIRBuilder.buildFrameIndex(Res, FI);
if (AI.isStaticAlloca()) {
unsigned Res = getOrCreateVReg(AI);
int FI = getOrCreateFrameIndex(AI);
MIRBuilder.buildFrameIndex(Res, FI);
return true;
}
// Now we're in the harder dynamic case.
Type *Ty = AI.getAllocatedType();
unsigned Align =
std::max((unsigned)DL->getPrefTypeAlignment(Ty), AI.getAlignment());
unsigned NumElts = getOrCreateVReg(*AI.getArraySize());
LLT IntPtrTy = LLT::scalar(DL->getPointerSizeInBits());
if (MRI->getType(NumElts) != IntPtrTy) {
unsigned ExtElts = MRI->createGenericVirtualRegister(IntPtrTy);
MIRBuilder.buildZExtOrTrunc(ExtElts, NumElts);
NumElts = ExtElts;
}
unsigned AllocSize = MRI->createGenericVirtualRegister(IntPtrTy);
unsigned TySize = MRI->createGenericVirtualRegister(IntPtrTy);
MIRBuilder.buildConstant(TySize, DL->getTypeAllocSize(Ty));
MIRBuilder.buildMul(AllocSize, NumElts, TySize);
LLT PtrTy = LLT{*AI.getType(), *DL};
auto &TLI = *MF->getSubtarget().getTargetLowering();
unsigned SPReg = TLI.getStackPointerRegisterToSaveRestore();
unsigned SPTmp = MRI->createGenericVirtualRegister(PtrTy);
MIRBuilder.buildCopy(SPTmp, SPReg);
unsigned SPInt = MRI->createGenericVirtualRegister(IntPtrTy);
MIRBuilder.buildInstr(TargetOpcode::G_PTRTOINT).addDef(SPInt).addUse(SPTmp);
unsigned AllocInt = MRI->createGenericVirtualRegister(IntPtrTy);
MIRBuilder.buildSub(AllocInt, SPInt, AllocSize);
// Handle alignment. We have to realign if the allocation granule was smaller
// than stack alignment, or the specific alloca requires more than stack
// alignment.
unsigned StackAlign =
MF->getSubtarget().getFrameLowering()->getStackAlignment();
Align = std::max(Align, StackAlign);
if (Align > StackAlign || DL->getTypeAllocSize(Ty) % StackAlign != 0) {
// Round the size of the allocation up to the stack alignment size
// by add SA-1 to the size. This doesn't overflow because we're computing
// an address inside an alloca.
unsigned TmpSize = MRI->createGenericVirtualRegister(IntPtrTy);
unsigned AlignMinus1 = MRI->createGenericVirtualRegister(IntPtrTy);
MIRBuilder.buildConstant(AlignMinus1, Align - 1);
MIRBuilder.buildSub(TmpSize, AllocInt, AlignMinus1);
unsigned AlignedAlloc = MRI->createGenericVirtualRegister(IntPtrTy);
unsigned AlignMask = MRI->createGenericVirtualRegister(IntPtrTy);
MIRBuilder.buildConstant(AlignMask, -(uint64_t)Align);
MIRBuilder.buildAnd(AlignedAlloc, TmpSize, AlignMask);
AllocInt = AlignedAlloc;
}
unsigned DstReg = getOrCreateVReg(AI);
MIRBuilder.buildInstr(TargetOpcode::G_INTTOPTR)
.addDef(DstReg)
.addUse(AllocInt);
MIRBuilder.buildCopy(SPReg, DstReg);
MF->getFrameInfo().CreateVariableSizedObject(Align ? Align : 1, &AI);
assert(MF->getFrameInfo().hasVarSizedObjects());
return true;
}

View File

@ -213,6 +213,19 @@ MachineInstrBuilder MachineIRBuilder::buildMul(unsigned Res, unsigned Op0,
.addUse(Op1);
}
MachineInstrBuilder MachineIRBuilder::buildAnd(unsigned Res, unsigned Op0,
unsigned Op1) {
assert((MRI->getType(Res).isScalar() || MRI->getType(Res).isVector()) &&
"invalid operand type");
assert(MRI->getType(Res) == MRI->getType(Op0) &&
MRI->getType(Res) == MRI->getType(Op1) && "type mismatch");
return buildInstr(TargetOpcode::G_AND)
.addDef(Res)
.addUse(Op0)
.addUse(Op1);
}
MachineInstrBuilder MachineIRBuilder::buildBr(MachineBasicBlock &Dest) {
return buildInstr(TargetOpcode::G_BR).addMBB(&Dest);
}
@ -327,6 +340,17 @@ MachineInstrBuilder MachineIRBuilder::buildSExtOrTrunc(unsigned Res,
return buildInstr(Opcode).addDef(Res).addUse(Op);
}
MachineInstrBuilder MachineIRBuilder::buildZExtOrTrunc(unsigned Res,
unsigned Op) {
unsigned Opcode = TargetOpcode::COPY;
if (MRI->getType(Res).getSizeInBits() > MRI->getType(Op).getSizeInBits())
Opcode = TargetOpcode::G_ZEXT;
else if (MRI->getType(Res).getSizeInBits() < MRI->getType(Op).getSizeInBits())
Opcode = TargetOpcode::G_TRUNC;
return buildInstr(Opcode).addDef(Res).addUse(Op);
}
MachineInstrBuilder MachineIRBuilder::buildExtract(ArrayRef<unsigned> Results,
ArrayRef<uint64_t> Indices,
unsigned Src) {

View File

@ -0,0 +1,57 @@
; RUN: llc -mtriple=aarch64 -global-isel %s -o - -stop-after=irtranslator | FileCheck %s
; CHECK-LABEL: name: test_simple_alloca
; CHECK: [[NUMELTS:%[0-9]+]](s32) = COPY %w0
; CHECK: [[NUMELTS_64:%[0-9]+]](s64) = G_ZEXT [[NUMELTS]](s32)
; CHECK: [[TYPE_SIZE:%[0-9]+]](s64) = G_CONSTANT i64 1
; CHECK: [[NUMBYTES:%[0-9]+]](s64) = G_MUL [[NUMELTS_64]], [[TYPE_SIZE]]
; CHECK: [[SP_TMP:%[0-9]+]](p0) = COPY %sp
; CHECK: [[SP_INT:%[0-9]+]](s64) = G_PTRTOINT [[SP_TMP]](p0)
; CHECK: [[ALLOC:%[0-9]+]](s64) = G_SUB [[SP_INT]], [[NUMBYTES]]
; CHECK: [[ALIGN_M_1:%[0-9]+]](s64) = G_CONSTANT i64 15
; CHECK: [[ALIGN_TMP:%[0-9]+]](s64) = G_SUB [[ALLOC]], [[ALIGN_M_1]]
; CHECK: [[ALIGN_MASK:%[0-9]+]](s64) = G_CONSTANT i64 -16
; CHECK: [[ALIGNED_ALLOC:%[0-9]+]](s64) = G_AND [[ALIGN_TMP]], [[ALIGN_MASK]]
; CHECK: [[ALLOC_PTR:%[0-9]+]](p0) = G_INTTOPTR [[ALIGNED_ALLOC]](s64)
; CHECK: %sp = COPY [[ALLOC_PTR]]
; CHECK: %x0 = COPY [[ALLOC_PTR]]
define i8* @test_simple_alloca(i32 %numelts) {
%addr = alloca i8, i32 %numelts
ret i8* %addr
}
; CHECK-LABEL: name: test_aligned_alloca
; CHECK: [[NUMELTS:%[0-9]+]](s32) = COPY %w0
; CHECK: [[NUMELTS_64:%[0-9]+]](s64) = G_ZEXT [[NUMELTS]](s32)
; CHECK: [[TYPE_SIZE:%[0-9]+]](s64) = G_CONSTANT i64 1
; CHECK: [[NUMBYTES:%[0-9]+]](s64) = G_MUL [[NUMELTS_64]], [[TYPE_SIZE]]
; CHECK: [[SP_TMP:%[0-9]+]](p0) = COPY %sp
; CHECK: [[SP_INT:%[0-9]+]](s64) = G_PTRTOINT [[SP_TMP]](p0)
; CHECK: [[ALLOC:%[0-9]+]](s64) = G_SUB [[SP_INT]], [[NUMBYTES]]
; CHECK: [[ALIGN_M_1:%[0-9]+]](s64) = G_CONSTANT i64 31
; CHECK: [[ALIGN_TMP:%[0-9]+]](s64) = G_SUB [[ALLOC]], [[ALIGN_M_1]]
; CHECK: [[ALIGN_MASK:%[0-9]+]](s64) = G_CONSTANT i64 -32
; CHECK: [[ALIGNED_ALLOC:%[0-9]+]](s64) = G_AND [[ALIGN_TMP]], [[ALIGN_MASK]]
; CHECK: [[ALLOC_PTR:%[0-9]+]](p0) = G_INTTOPTR [[ALIGNED_ALLOC]](s64)
; CHECK: %sp = COPY [[ALLOC_PTR]]
; CHECK: %x0 = COPY [[ALLOC_PTR]]
define i8* @test_aligned_alloca(i32 %numelts) {
%addr = alloca i8, i32 %numelts, align 32
ret i8* %addr
}
; CHECK-LABEL: name: test_natural_alloca
; CHECK: [[NUMELTS:%[0-9]+]](s32) = COPY %w0
; CHECK: [[NUMELTS_64:%[0-9]+]](s64) = G_ZEXT [[NUMELTS]](s32)
; CHECK: [[TYPE_SIZE:%[0-9]+]](s64) = G_CONSTANT i64 16
; CHECK: [[NUMBYTES:%[0-9]+]](s64) = G_MUL [[NUMELTS_64]], [[TYPE_SIZE]]
; CHECK: [[SP_TMP:%[0-9]+]](p0) = COPY %sp
; CHECK: [[SP_INT:%[0-9]+]](s64) = G_PTRTOINT [[SP_TMP]](p0)
; CHECK: [[ALLOC:%[0-9]+]](s64) = G_SUB [[SP_INT]], [[NUMBYTES]]
; CHECK: [[ALLOC_PTR:%[0-9]+]](p0) = G_INTTOPTR [[ALLOC]](s64)
; CHECK: %sp = COPY [[ALLOC_PTR]]
; CHECK: %x0 = COPY [[ALLOC_PTR]]
define i128* @test_natural_alloca(i32 %numelts) {
%addr = alloca i128, i32 %numelts
ret i128* %addr
}