mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-12 13:30:51 +00:00
Extended syntax of vector version of getelementptr instruction.
The justification of this change is here: http://lists.cs.uiuc.edu/pipermail/llvmdev/2015-March/082989.html According to the current GEP syntax, vector GEP requires that each index must be a vector with the same number of elements. %A = getelementptr i8, <4 x i8*> %ptrs, <4 x i64> %offsets In this implementation I let each index be or vector or scalar. All vector indices must have the same number of elements. The scalar value will mean the splat vector value. (1) %A = getelementptr i8, i8* %ptr, <4 x i64> %offsets or (2) %A = getelementptr i8, <4 x i8*> %ptrs, i64 %offset In all cases the %A type is <4 x i8*> In the case (2) we add the same offset to all pointers. The case (1) covers C[B[i]] case, when we have the same base C and different offsets B[i]. The documentation is updated. http://reviews.llvm.org/D10496 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@241788 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
f959df643a
commit
43afab3bdb
@ -6718,7 +6718,8 @@ Overview:
|
||||
|
||||
The '``getelementptr``' instruction is used to get the address of a
|
||||
subelement of an :ref:`aggregate <t_aggregate>` data structure. It performs
|
||||
address calculation only and does not access memory.
|
||||
address calculation only and does not access memory. The instruction can also
|
||||
be used to calculate a vector of such addresses.
|
||||
|
||||
Arguments:
|
||||
""""""""""
|
||||
@ -6844,12 +6845,61 @@ Example:
|
||||
; yields i32*:iptr
|
||||
%iptr = getelementptr [10 x i32], [10 x i32]* @arr, i16 0, i16 0
|
||||
|
||||
In cases where the pointer argument is a vector of pointers, each index
|
||||
must be a vector with the same number of elements. For example:
|
||||
Vector of pointers:
|
||||
"""""""""""""""""""
|
||||
|
||||
The ``getelementptr`` returns a vector of pointers, instead of a single address,
|
||||
when one or more of its arguments is a vector. In such cases, all vector
|
||||
arguments should have the same number of elements, and every scalar argument
|
||||
will be effectively broadcast into a vector during address calculation.
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
%A = getelementptr i8, <4 x i8*> %ptrs, <4 x i64> %offsets,
|
||||
; All arguments are vectors:
|
||||
; A[i] = ptrs[i] + offsets[i]*sizeof(i8)
|
||||
%A = getelementptr i8, <4 x i8*> %ptrs, <4 x i64> %offsets
|
||||
|
||||
; Add the same scalar offset to each pointer of a vector:
|
||||
; A[i] = ptrs[i] + offset*sizeof(i8)
|
||||
%A = getelementptr i8, <4 x i8*> %ptrs, i64 %offset
|
||||
|
||||
; Add distinct offsets to the same pointer:
|
||||
; A[i] = ptr + offsets[i]*sizeof(i8)
|
||||
%A = getelementptr i8, i8* %ptr, <4 x i64> %offsets
|
||||
|
||||
; In all cases described above the type of the result is <4 x i8*>
|
||||
|
||||
The two following instructions are equivalent:
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
getelementptr %struct.ST, <4 x %struct.ST*> %s, <4 x i64> %ind1,
|
||||
<4 x i32> <i32 2, i32 2, i32 2, i32 2>,
|
||||
<4 x i32> <i32 1, i32 1, i32 1, i32 1>,
|
||||
<4 x i32> %ind4,
|
||||
<4 x i64> <i64 13, i64 13, i64 13, i64 13>
|
||||
|
||||
getelementptr %struct.ST, <4 x %struct.ST*> %s, <4 x i64> %ind1,
|
||||
i32 2, i32 1, <4 x i32> %ind4, i64 13
|
||||
|
||||
Let's look at the C code, where the vector version of ``getelementptr``
|
||||
makes sense:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
// Let's assume that we vectorize the following loop:
|
||||
double *A, B; int *C;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
A[i] = B[C[i]];
|
||||
}
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
; get pointers for 8 elements from array B
|
||||
%ptrs = getelementptr double, double* %B, <8 x i32> %C
|
||||
; load 8 elements from array B into A
|
||||
%A = call <8 x double> @llvm.masked.gather.v8f64(<8 x double*> %ptrs,
|
||||
i32 8, <8 x i1> %mask, <8 x double> %passthru)
|
||||
|
||||
Conversion Operations
|
||||
---------------------
|
||||
|
@ -990,10 +990,14 @@ public:
|
||||
Ptr->getType()->getPointerAddressSpace());
|
||||
// Vector GEP
|
||||
if (Ptr->getType()->isVectorTy()) {
|
||||
unsigned NumElem = cast<VectorType>(Ptr->getType())->getNumElements();
|
||||
unsigned NumElem = Ptr->getType()->getVectorNumElements();
|
||||
return VectorType::get(PtrTy, NumElem);
|
||||
}
|
||||
|
||||
for (Value *Index : IdxList)
|
||||
if (Index->getType()->isVectorTy()) {
|
||||
unsigned NumElem = Index->getType()->getVectorNumElements();
|
||||
return VectorType::get(PtrTy, NumElem);
|
||||
}
|
||||
// Scalar GEP
|
||||
return PtrTy;
|
||||
}
|
||||
|
@ -2873,8 +2873,8 @@ bool LLParser::ParseValID(ValID &ID, PerFunctionState *PFS) {
|
||||
if (ValTy->isVectorTy() != BaseType->isVectorTy())
|
||||
return Error(ID.Loc, "getelementptr index type missmatch");
|
||||
if (ValTy->isVectorTy()) {
|
||||
unsigned ValNumEl = cast<VectorType>(ValTy)->getNumElements();
|
||||
unsigned PtrNumEl = cast<VectorType>(BaseType)->getNumElements();
|
||||
unsigned ValNumEl = ValTy->getVectorNumElements();
|
||||
unsigned PtrNumEl = BaseType->getVectorNumElements();
|
||||
if (ValNumEl != PtrNumEl)
|
||||
return Error(
|
||||
ID.Loc,
|
||||
@ -5572,6 +5572,11 @@ int LLParser::ParseGetElementPtr(Instruction *&Inst, PerFunctionState &PFS) {
|
||||
|
||||
SmallVector<Value*, 16> Indices;
|
||||
bool AteExtraComma = false;
|
||||
// GEP returns a vector of pointers if at least one of parameters is a vector.
|
||||
// All vector parameters should have the same vector width.
|
||||
unsigned GEPWidth = BaseType->isVectorTy() ?
|
||||
BaseType->getVectorNumElements() : 0;
|
||||
|
||||
while (EatIfPresent(lltok::comma)) {
|
||||
if (Lex.getKind() == lltok::MetadataVar) {
|
||||
AteExtraComma = true;
|
||||
@ -5580,14 +5585,13 @@ int LLParser::ParseGetElementPtr(Instruction *&Inst, PerFunctionState &PFS) {
|
||||
if (ParseTypeAndValue(Val, EltLoc, PFS)) return true;
|
||||
if (!Val->getType()->getScalarType()->isIntegerTy())
|
||||
return Error(EltLoc, "getelementptr index must be an integer");
|
||||
if (Val->getType()->isVectorTy() != Ptr->getType()->isVectorTy())
|
||||
return Error(EltLoc, "getelementptr index type missmatch");
|
||||
|
||||
if (Val->getType()->isVectorTy()) {
|
||||
unsigned ValNumEl = cast<VectorType>(Val->getType())->getNumElements();
|
||||
unsigned PtrNumEl = cast<VectorType>(Ptr->getType())->getNumElements();
|
||||
if (ValNumEl != PtrNumEl)
|
||||
unsigned ValNumEl = Val->getType()->getVectorNumElements();
|
||||
if (GEPWidth && GEPWidth != ValNumEl)
|
||||
return Error(EltLoc,
|
||||
"getelementptr vector index has a wrong number of elements");
|
||||
GEPWidth = ValNumEl;
|
||||
}
|
||||
Indices.push_back(Val);
|
||||
}
|
||||
|
@ -2787,6 +2787,16 @@ void SelectionDAGBuilder::visitGetElementPtr(const User &I) {
|
||||
SDValue N = getValue(Op0);
|
||||
SDLoc dl = getCurSDLoc();
|
||||
|
||||
// Normalize Vector GEP - all scalar operands should be converted to the
|
||||
// splat vector.
|
||||
unsigned VectorWidth = I.getType()->isVectorTy() ?
|
||||
cast<VectorType>(I.getType())->getVectorNumElements() : 0;
|
||||
|
||||
if (VectorWidth && !N.getValueType().isVector()) {
|
||||
MVT VT = MVT::getVectorVT(N.getValueType().getSimpleVT(), VectorWidth);
|
||||
SmallVector<SDValue, 16> Ops(VectorWidth, N);
|
||||
N = DAG.getNode(ISD::BUILD_VECTOR, dl, VT, Ops);
|
||||
}
|
||||
for (GetElementPtrInst::const_op_iterator OI = I.op_begin()+1, E = I.op_end();
|
||||
OI != E; ++OI) {
|
||||
const Value *Idx = *OI;
|
||||
@ -2807,12 +2817,20 @@ void SelectionDAGBuilder::visitGetElementPtr(const User &I) {
|
||||
unsigned PtrSize = PtrTy.getSizeInBits();
|
||||
APInt ElementSize(PtrSize, DL->getTypeAllocSize(Ty));
|
||||
|
||||
// If this is a constant subscript, handle it quickly.
|
||||
if (const auto *CI = dyn_cast<ConstantInt>(Idx)) {
|
||||
// If this is a scalar constant or a splat vector of constants,
|
||||
// handle it quickly.
|
||||
const auto *CI = dyn_cast<ConstantInt>(Idx);
|
||||
if (!CI && isa<ConstantDataVector>(Idx) &&
|
||||
cast<ConstantDataVector>(Idx)->getSplatValue())
|
||||
CI = cast<ConstantInt>(cast<ConstantDataVector>(Idx)->getSplatValue());
|
||||
|
||||
if (CI) {
|
||||
if (CI->isZero())
|
||||
continue;
|
||||
APInt Offs = ElementSize * CI->getValue().sextOrTrunc(PtrSize);
|
||||
SDValue OffsVal = DAG.getConstant(Offs, dl, PtrTy);
|
||||
SDValue OffsVal = VectorWidth ?
|
||||
DAG.getConstant(Offs, dl, MVT::getVectorVT(PtrTy, VectorWidth)) :
|
||||
DAG.getConstant(Offs, dl, PtrTy);
|
||||
N = DAG.getNode(ISD::ADD, dl, N.getValueType(), N, OffsVal);
|
||||
continue;
|
||||
}
|
||||
@ -2820,6 +2838,11 @@ void SelectionDAGBuilder::visitGetElementPtr(const User &I) {
|
||||
// N = N + Idx * ElementSize;
|
||||
SDValue IdxN = getValue(Idx);
|
||||
|
||||
if (!IdxN.getValueType().isVector() && VectorWidth) {
|
||||
MVT VT = MVT::getVectorVT(IdxN.getValueType().getSimpleVT(), VectorWidth);
|
||||
SmallVector<SDValue, 16> Ops(VectorWidth, IdxN);
|
||||
IdxN = DAG.getNode(ISD::BUILD_VECTOR, dl, VT, Ops);
|
||||
}
|
||||
// If the index is smaller or larger than intptr_t, truncate or extend
|
||||
// it.
|
||||
IdxN = DAG.getSExtOrTrunc(IdxN, dl, N.getValueType());
|
||||
|
@ -2538,10 +2538,6 @@ void Verifier::visitGetElementPtrInst(GetElementPtrInst &GEP) {
|
||||
Assert(isa<PointerType>(TargetTy),
|
||||
"GEP base pointer is not a vector or a vector of pointers", &GEP);
|
||||
Assert(GEP.getSourceElementType()->isSized(), "GEP into unsized type!", &GEP);
|
||||
Assert(GEP.getPointerOperandType()->isVectorTy() ==
|
||||
GEP.getType()->isVectorTy(),
|
||||
"Vector GEP must return a vector value", &GEP);
|
||||
|
||||
SmallVector<Value*, 16> Idxs(GEP.idx_begin(), GEP.idx_end());
|
||||
Type *ElTy =
|
||||
GetElementPtrInst::getIndexedType(GEP.getSourceElementType(), Idxs);
|
||||
@ -2551,17 +2547,20 @@ void Verifier::visitGetElementPtrInst(GetElementPtrInst &GEP) {
|
||||
GEP.getResultElementType() == ElTy,
|
||||
"GEP is not of right type for indices!", &GEP, ElTy);
|
||||
|
||||
if (GEP.getPointerOperandType()->isVectorTy()) {
|
||||
if (GEP.getType()->isVectorTy()) {
|
||||
// Additional checks for vector GEPs.
|
||||
unsigned GepWidth = GEP.getPointerOperandType()->getVectorNumElements();
|
||||
Assert(GepWidth == GEP.getType()->getVectorNumElements(),
|
||||
"Vector GEP result width doesn't match operand's", &GEP);
|
||||
unsigned GEPWidth = GEP.getType()->getVectorNumElements();
|
||||
if (GEP.getPointerOperandType()->isVectorTy())
|
||||
Assert(GEPWidth == GEP.getPointerOperandType()->getVectorNumElements(),
|
||||
"Vector GEP result width doesn't match operand's", &GEP);
|
||||
for (unsigned i = 0, e = Idxs.size(); i != e; ++i) {
|
||||
Type *IndexTy = Idxs[i]->getType();
|
||||
Assert(IndexTy->isVectorTy(), "Vector GEP must have vector indices!",
|
||||
&GEP);
|
||||
unsigned IndexWidth = IndexTy->getVectorNumElements();
|
||||
Assert(IndexWidth == GepWidth, "Invalid GEP index vector width", &GEP);
|
||||
if (IndexTy->isVectorTy()) {
|
||||
unsigned IndexWidth = IndexTy->getVectorNumElements();
|
||||
Assert(IndexWidth == GEPWidth, "Invalid GEP index vector width", &GEP);
|
||||
}
|
||||
Assert(IndexTy->getScalarType()->isIntegerTy(),
|
||||
"All GEP indices should be of integer type");
|
||||
}
|
||||
}
|
||||
visitInstruction(GEP);
|
||||
|
@ -1,8 +1,8 @@
|
||||
; RUN: not llvm-as < %s >/dev/null 2> %t
|
||||
; RUN: FileCheck %s < %t
|
||||
; Test that a vector index is only used with a vector pointer.
|
||||
; Test that a vector GEP may be used with a scalar base, the result is a vector of pointers
|
||||
|
||||
; CHECK: getelementptr index type missmatch
|
||||
; CHECK: '%w' defined with type '<2 x i32*>
|
||||
|
||||
define i32 @test(i32* %a) {
|
||||
%w = getelementptr i32, i32* %a, <2 x i32> <i32 5, i32 9>
|
||||
|
@ -1,10 +1,24 @@
|
||||
; RUN: not llvm-as < %s >/dev/null 2> %t
|
||||
; RUN: FileCheck %s < %t
|
||||
; Test that a vector pointer is only used with a vector index.
|
||||
; Test that a vector pointer may be used with a scalar index.
|
||||
; Test that a vector pointer and vector index should have the same vector width
|
||||
|
||||
; CHECK: getelementptr index type missmatch
|
||||
|
||||
define <2 x i32> @test(<2 x i32*> %a) {
|
||||
; This code is correct
|
||||
define <2 x i32*> @test2(<2 x i32*> %a) {
|
||||
%w = getelementptr i32, <2 x i32*> %a, i32 2
|
||||
ret <2 x i32*> %w
|
||||
}
|
||||
|
||||
; This code is correct
|
||||
define <2 x i32*> @test3(i32* %a) {
|
||||
%w = getelementptr i32, i32* %a, <2 x i32> <i32 2, i32 2>
|
||||
ret <2 x i32*> %w
|
||||
}
|
||||
|
||||
; CHECK: getelementptr vector index has a wrong number of elements
|
||||
|
||||
define <2 x i32> @test1(<2 x i32*> %a) {
|
||||
%w = getelementptr i32, <2 x i32*> %a, <4 x i32><i32 2, i32 2, i32 2, i32 2>
|
||||
ret <2 x i32> %w
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
; RUN: not llvm-as < %s >/dev/null 2> %t
|
||||
; RUN: FileCheck %s < %t
|
||||
; Test that vector indices have the same number of elements as the pointer.
|
||||
; Test that a vector GEP may be used with a scalar base, the result is a vector of pointers
|
||||
|
||||
; CHECK: getelementptr index type missmatch
|
||||
; CHECK: '%w' defined with type '<2 x <4 x i32>*>'
|
||||
|
||||
define <4 x i32> @test(<4 x i32>* %a) {
|
||||
%w = getelementptr <4 x i32>, <4 x i32>* %a, <2 x i32> <i32 5, i32 9>
|
||||
|
@ -92,3 +92,25 @@ entry:
|
||||
;CHECK: ret
|
||||
}
|
||||
|
||||
;CHECK-LABEL: AGEP7:
|
||||
define <4 x i8*> @AGEP7(<4 x i8*> %param, i32 %off) nounwind {
|
||||
entry:
|
||||
;CHECK: vbroadcastss
|
||||
;CHECK: vpadd
|
||||
%A = getelementptr i8, <4 x i8*> %param, i32 %off
|
||||
ret <4 x i8*> %A
|
||||
;CHECK: ret
|
||||
}
|
||||
|
||||
;CHECK-LABEL: AGEP8:
|
||||
define <4 x i16*> @AGEP8(i16* %param, <4 x i32> %off) nounwind {
|
||||
entry:
|
||||
; Multiply offset by two (add it to itself).
|
||||
;CHECK: vpadd
|
||||
; add the base to the offset
|
||||
;CHECK: vbroadcastss
|
||||
;CHECK-NEXT: vpadd
|
||||
%A = getelementptr i16, i16* %param, <4 x i32> %off
|
||||
ret <4 x i16*> %A
|
||||
;CHECK: ret
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user