Skip to content

Commit

Permalink
[flang] Add PowerPC vec_convert, vec_ctf and vec_cvf intrinsic
Browse files Browse the repository at this point in the history
Co-authored-by: Paul Scoropan <1paulscoropan@gmail.com>

Differential Revision: https://reviews.llvm.org/D155235
  • Loading branch information
kkwli committed Jul 20, 2023
1 parent c6f66de commit 99dc393
Show file tree
Hide file tree
Showing 8 changed files with 2,910 additions and 5 deletions.
7 changes: 7 additions & 0 deletions flang/include/flang/Optimizer/Builder/PPCIntrinsicCall.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ enum class VecOp {
Cmpgt,
Cmple,
Cmplt,
Convert,
Ctf,
Cvf,
Mul,
Sl,
Sld,
Expand Down Expand Up @@ -112,6 +115,10 @@ struct PPCIntrinsicLibrary : IntrinsicLibrary {
fir::ExtendedValue genVecCmp(mlir::Type resultType,
llvm::ArrayRef<fir::ExtendedValue> args);

template <VecOp>
fir::ExtendedValue genVecConvert(mlir::Type resultType,
llvm::ArrayRef<fir::ExtendedValue> args);

template <VecOp>
fir::ExtendedValue genVecAnyCompare(mlir::Type resultType,
llvm::ArrayRef<fir::ExtendedValue> args);
Expand Down
145 changes: 145 additions & 0 deletions flang/lib/Optimizer/Builder/PPCIntrinsicCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@ static constexpr IntrinsicHandler ppcHandlers[]{
&PI::genVecCmp<VecOp::Cmplt>),
{{{"arg1", asValue}, {"arg2", asValue}}},
/*isElemental=*/true},
{"__ppc_vec_convert",
static_cast<IntrinsicLibrary::ExtendedGenerator>(
&PI::genVecConvert<VecOp::Convert>),
{{{"v", asValue}, {"mold", asValue}}},
/*isElemental=*/false},
{"__ppc_vec_ctf",
static_cast<IntrinsicLibrary::ExtendedGenerator>(
&PI::genVecConvert<VecOp::Ctf>),
{{{"arg1", asValue}, {"arg2", asValue}}},
/*isElemental=*/true},
{"__ppc_vec_cvf",
static_cast<IntrinsicLibrary::ExtendedGenerator>(
&PI::genVecConvert<VecOp::Cvf>),
{{{"arg1", asValue}}},
/*isElemental=*/true},
{"__ppc_vec_mul",
static_cast<IntrinsicLibrary::ExtendedGenerator>(
&PI::genVecAddAndMulSubXor<VecOp::Mul>),
Expand Down Expand Up @@ -681,6 +696,136 @@ PPCIntrinsicLibrary::genVecCmp(mlir::Type resultType,
return res;
}

static inline mlir::Value swapVectorWordPairs(fir::FirOpBuilder &builder,
mlir::Location loc,
mlir::Value arg) {
auto ty = arg.getType();
auto context{builder.getContext()};
auto vtype{mlir::VectorType::get(16, mlir::IntegerType::get(context, 8))};

if (ty != vtype)
arg = builder.create<mlir::LLVM::BitcastOp>(loc, vtype, arg).getResult();

llvm::SmallVector<int64_t, 16> mask{4, 5, 6, 7, 0, 1, 2, 3,
12, 13, 14, 15, 8, 9, 10, 11};
arg = builder.create<mlir::vector::ShuffleOp>(loc, arg, arg, mask);
if (ty != vtype)
arg = builder.create<mlir::LLVM::BitcastOp>(loc, ty, arg);
return arg;
}

// VEC_CONVERT, VEC_CTF, VEC_CVF
template <VecOp vop>
fir::ExtendedValue
PPCIntrinsicLibrary::genVecConvert(mlir::Type resultType,
llvm::ArrayRef<fir::ExtendedValue> args) {
auto context{builder.getContext()};
auto argBases{getBasesForArgs(args)};
auto vecTyInfo{getVecTypeFromFir(argBases[0])};
auto mlirTy{vecTyInfo.toMlirVectorType(context)};
auto vArg1{builder.createConvert(loc, mlirTy, argBases[0])};
const auto i32Ty{mlir::IntegerType::get(context, 32)};

switch (vop) {
case VecOp::Ctf: {
assert(args.size() == 2);
auto convArg{builder.createConvert(loc, i32Ty, argBases[1])};
auto eTy{vecTyInfo.eleTy.dyn_cast<mlir::IntegerType>()};
assert(eTy && "Unsupported vector type");
const auto isUnsigned{eTy.isUnsignedInteger()};
const auto width{eTy.getWidth()};

if (width == 32) {
auto ftype{(isUnsigned)
? genFuncType<Ty::RealVector<4>, Ty::UnsignedVector<4>,
Ty::Integer<4>>(context, builder)
: genFuncType<Ty::RealVector<4>, Ty::IntegerVector<4>,
Ty::Integer<4>>(context, builder)};
const llvm::StringRef fname{(isUnsigned) ? "llvm.ppc.altivec.vcfux"
: "llvm.ppc.altivec.vcfsx"};
auto funcOp{builder.addNamedFunction(loc, fname, ftype)};
mlir::Value newArgs[] = {argBases[0], convArg};
auto callOp{builder.create<fir::CallOp>(loc, funcOp, newArgs)};

return callOp.getResult(0);
} else if (width == 64) {
auto fTy{mlir::FloatType::getF64(context)};
auto ty{mlir::VectorType::get(2, fTy)};

// vec_vtf(arg1, arg2) = fmul(1.0 / (1 << arg2), llvm.sitofp(arg1))
auto convOp{(isUnsigned)
? builder.create<mlir::LLVM::UIToFPOp>(loc, ty, vArg1)
: builder.create<mlir::LLVM::SIToFPOp>(loc, ty, vArg1)};

// construct vector<1./(1<<arg1), 1.0/(1<<arg1)>
auto constInt{
mlir::dyn_cast<mlir::arith::ConstantOp>(argBases[1].getDefiningOp())
.getValue()
.dyn_cast_or_null<mlir::IntegerAttr>()};
assert(constInt && "expected integer constant argument");
double f{1.0 / (1 << constInt.getInt())};
llvm::SmallVector<double> vals{f, f};
auto constOp{builder.create<mlir::arith::ConstantOp>(
loc, ty, builder.getF64VectorAttr(vals))};

auto mulOp{builder.create<mlir::LLVM::FMulOp>(
loc, ty, convOp->getResult(0), constOp)};

return builder.createConvert(loc, fir::VectorType::get(2, fTy), mulOp);
}
llvm_unreachable("invalid element integer kind");
}
case VecOp::Convert: {
assert(args.size() == 2);
// resultType has mold type (if scalar) or element type (if array)
auto resTyInfo{getVecTypeFromFirType(resultType)};
auto moldTy{resTyInfo.toMlirVectorType(context)};
auto firTy{resTyInfo.toFirVectorType()};

// vec_convert(v, mold) = bitcast v to "type of mold"
auto conv{builder.create<mlir::LLVM::BitcastOp>(loc, moldTy, vArg1)};

return builder.createConvert(loc, firTy, conv);
}
case VecOp::Cvf: {
assert(args.size() == 1);

mlir::Value newArgs[]{vArg1};
if (vecTyInfo.isFloat32()) {
// TODO: Handle element ordering
newArgs[0] = swapVectorWordPairs(builder, loc, newArgs[0]);

const llvm::StringRef fname{"llvm.ppc.vsx.xvcvspdp"};
auto ftype{
genFuncType<Ty::RealVector<8>, Ty::RealVector<4>>(context, builder)};
auto funcOp{builder.addNamedFunction(loc, fname, ftype)};
auto callOp{builder.create<fir::CallOp>(loc, funcOp, newArgs)};

return callOp.getResult(0);
} else if (vecTyInfo.isFloat64()) {
const llvm::StringRef fname{"llvm.ppc.vsx.xvcvdpsp"};
auto ftype{
genFuncType<Ty::RealVector<4>, Ty::RealVector<8>>(context, builder)};
auto funcOp{builder.addNamedFunction(loc, fname, ftype)};
newArgs[0] =
builder.create<fir::CallOp>(loc, funcOp, newArgs).getResult(0);
auto fvf32Ty{newArgs[0].getType()};
auto f32type{mlir::FloatType::getF32(context)};
auto mvf32Ty{mlir::VectorType::get(4, f32type)};
newArgs[0] = builder.createConvert(loc, mvf32Ty, newArgs[0]);

// TODO: Handle element ordering
newArgs[0] = swapVectorWordPairs(builder, loc, newArgs[0]);

return builder.createConvert(loc, fvf32Ty, newArgs[0]);
}
llvm_unreachable("invalid element integer kind");
}
default:
llvm_unreachable("Invalid vector operation for generator");
}
}

// VEC_SL, VEC_SLD, VEC_SLDW, VEC_SLL, VEC_SLO, VEC_SR, VEC_SRL, VEC_SRO
template <VecOp vop>
fir::ExtendedValue
Expand Down
3 changes: 3 additions & 0 deletions flang/lib/Semantics/check-call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1394,6 +1394,9 @@ bool CheckPPCIntrinsic(const Symbol &generic, const Symbol &specific,
if (specific.name().ToString().compare(0, 15, "__ppc_vec_sldw_") == 0) {
return CheckArgumentIsConstantExprInRange(actuals, 2, 0, 3, messages);
}
if (specific.name().ToString().compare(0, 14, "__ppc_vec_ctf_") == 0) {
return CheckArgumentIsConstantExprInRange(actuals, 1, 0, 31, messages);
}
return false;
}

Expand Down
150 changes: 146 additions & 4 deletions flang/module/__ppc_intrinsics.f90
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ end function func_r8r8r8r8
!--------------------
! Vector intrinsic
!--------------------
!! ================ 1 argument function interface ================
! vector(r) function f(vector(r))
#define ELEM_FUNC_VRVR_2(VKIND1, VKIND2) \
elemental vector(real(VKIND1)) function elem_func_vr##VKIND1##vr##VKIND2(arg1); \
vector(real(VKIND2)), intent(in) :: arg1; \
end function ;

ELEM_FUNC_VRVR_2(4,8) ELEM_FUNC_VRVR_2(8,4)

#undef ELEM_FUNC_VRVR_2

!! ================ 2 arguments function interface ================
! vector(i) function f(vector(i), vector(i))
#define ELEM_FUNC_VIVIVI(VKIND) \
Expand Down Expand Up @@ -88,6 +99,56 @@ elemental integer(RKIND) function elem_func_i##RKIND##vr##VKIND##vr##VKIND(arg1,
vector(real(VKIND)), intent(in) :: arg1, arg2; \
end function ;

! vector(r) function f(vector(i), i)
#define ELEM_FUNC_VRVII(VKIND) \
elemental vector(real(VKIND)) function elem_func_vr##VKIND##vi##VKIND##i(arg1, arg2); \
vector(integer(VKIND)), intent(in) :: arg1; \
integer(8), intent(in) :: arg2; \
!dir$ ignore_tkr(k) arg2; \
end function ;

! vector(r) function f(vector(u), i)
#define ELEM_FUNC_VRVUI(VKIND) \
elemental vector(real(VKIND)) function elem_func_vr##VKIND##vu##VKIND##i(arg1, arg2); \
vector(unsigned(VKIND)), intent(in) :: arg1; \
integer(8), intent(in) :: arg2; \
!dir$ ignore_tkr(k) arg2; \
end function ;

! The following macros are specific for the vec_convert(v, mold) intrinsics as
! the argument keywords are different from the other vector intrinsics.
!
! vector(i) function f(vector(i), vector(i))
#define FUNC_VEC_CONVERT_VIVIVI(VKIND) \
pure vector(integer(VKIND)) function func_vec_convert_vi##VKIND##vi##vi##VKIND(v, mold); \
vector(integer(8)), intent(in) :: v; \
!dir$ ignore_tkr(tk) v; \
vector(integer(VKIND)), intent(in) :: mold; \
!dir$ ignore_tkr(r) mold; \
end function ;

! vector(u) function f(vector(i), vector(u))
#define FUNC_VEC_CONVERT_VUVIVU(VKIND) \
pure vector(unsigned(VKIND)) function func_vec_convert_vu##VKIND##vi##vu##VKIND(v, mold); \
vector(integer(8)), intent(in) :: v; \
!dir$ ignore_tkr(tk) v; \
vector(unsigned(VKIND)), intent(in) :: mold; \
!dir$ ignore_tkr(r) mold; \
end function ;

! vector(r) function f(vector(i), vector(r))
#define FUNC_VEC_CONVERT_VRVIVR(VKIND) \
pure vector(real(VKIND)) function func_vec_convert_vr##VKIND##vi##vr##VKIND(v, mold); \
vector(integer(8)), intent(in) :: v; \
!dir$ ignore_tkr(tk) v; \
vector(real(VKIND)), intent(in) :: mold; \
!dir$ ignore_tkr(r) mold; \
end function ;

FUNC_VEC_CONVERT_VIVIVI(1) FUNC_VEC_CONVERT_VIVIVI(2) FUNC_VEC_CONVERT_VIVIVI(4) FUNC_VEC_CONVERT_VIVIVI(8)
FUNC_VEC_CONVERT_VUVIVU(1) FUNC_VEC_CONVERT_VUVIVU(2) FUNC_VEC_CONVERT_VUVIVU(4) FUNC_VEC_CONVERT_VUVIVU(8)
FUNC_VEC_CONVERT_VRVIVR(4) FUNC_VEC_CONVERT_VRVIVR(8)

ELEM_FUNC_VIVIVI(1) ELEM_FUNC_VIVIVI(2) ELEM_FUNC_VIVIVI(4) ELEM_FUNC_VIVIVI(8)
ELEM_FUNC_VUVIVI(1) ELEM_FUNC_VUVIVI(2) ELEM_FUNC_VUVIVI(4) ELEM_FUNC_VUVIVI(8)
ELEM_FUNC_VUVUVU(1) ELEM_FUNC_VUVUVU(2) ELEM_FUNC_VUVUVU(4) ELEM_FUNC_VUVUVU(8)
Expand All @@ -104,7 +165,14 @@ elemental integer(RKIND) function elem_func_i##RKIND##vr##VKIND##vr##VKIND(arg1,
ELEM_FUNC_IVIVI(4,1) ELEM_FUNC_IVIVI(4,2) ELEM_FUNC_IVIVI(4,4) ELEM_FUNC_IVIVI(4,8)
ELEM_FUNC_IVUVU(4,1) ELEM_FUNC_IVUVU(4,2) ELEM_FUNC_IVUVU(4,4) ELEM_FUNC_IVUVU(4,8)
ELEM_FUNC_IVRVR(4,4) ELEM_FUNC_IVRVR(4,8)

ELEM_FUNC_VRVII(4) ELEM_FUNC_VRVII(8)
ELEM_FUNC_VRVUI(4) ELEM_FUNC_VRVUI(8)

#undef FUNC_VEC_CONVERT_VRVIVR
#undef FUNC_VEC_CONVERT_VUVIVU
#undef FUNC_VEC_CONVERT_VIVIVI
#undef ELEM_FUNC_VRVUI
#undef ELEM_FUNC_VRVII
#undef ELEM_FUNC_IVIVI
#undef ELEM_FUNC_IVUVU
#undef ELEM_FUNC_VIVIVU_2
Expand Down Expand Up @@ -316,6 +384,24 @@ end function func_r8r8i
end interface mtfsfi
public :: mtfsfi

!-------------------------
! vector function(vector)
!-------------------------
#define VR_VR_2(NAME, VKIND1, VKIND2) __ppc_##NAME##_vr##VKIND1##vr##VKIND2

#define VEC_VR_VR_2(NAME, VKIND1, VKIND2) \
procedure(elem_func_vr##VKIND1##vr##VKIND2) :: VR_VR_2(NAME, VKIND1, VKIND2);

! vec_cvf
VEC_VR_VR_2(vec_cvf,4,8) VEC_VR_VR_2(vec_cvf,8,4)
interface vec_cvf
procedure :: VR_VR_2(vec_cvf,4,8), VR_VR_2(vec_cvf,8,4)
end interface vec_cvf
public :: vec_cvf

#undef VEC_VR_VR_2
#undef VR_VR_2

!---------------------------------
! vector function(vector, vector)
!---------------------------------
Expand Down Expand Up @@ -412,6 +498,36 @@ end function func_r8r8i
end interface vec_cmplt
public :: vec_cmplt

! vec_convert
! Argument 'v' has the `ignore_tkr` directive
#define CONVERT_VI_VI_VI(VKIND) __ppc_vec_convert_vi##VKIND##vi##vi##VKIND
#define CONVERT_VU_VI_VU(VKIND) __ppc_vec_convert_vu##VKIND##vi##vu##VKIND
#define CONVERT_VR_VI_VR(VKIND) __ppc_vec_convert_vr##VKIND##vi##vr##VKIND

#define VEC_CONVERT_VI_VI_VI(VKIND) \
procedure(func_vec_convert_vi##VKIND##vi##vi##VKIND) :: CONVERT_VI_VI_VI(VKIND);
#define VEC_CONVERT_VU_VI_VU(VKIND) \
procedure(func_vec_convert_vu##VKIND##vi##vu##VKIND) :: CONVERT_VU_VI_VU(VKIND);
#define VEC_CONVERT_VR_VI_VR(VKIND) \
procedure(func_vec_convert_vr##VKIND##vi##vr##VKIND) :: CONVERT_VR_VI_VR(VKIND);

VEC_CONVERT_VI_VI_VI(1) VEC_CONVERT_VI_VI_VI(2) VEC_CONVERT_VI_VI_VI(4) VEC_CONVERT_VI_VI_VI(8)
VEC_CONVERT_VU_VI_VU(1) VEC_CONVERT_VU_VI_VU(2) VEC_CONVERT_VU_VI_VU(4) VEC_CONVERT_VU_VI_VU(8)
VEC_CONVERT_VR_VI_VR(4) VEC_CONVERT_VR_VI_VR(8)
interface vec_convert
procedure :: CONVERT_VI_VI_VI(1), CONVERT_VI_VI_VI(2), CONVERT_VI_VI_VI(4), CONVERT_VI_VI_VI(8)
procedure :: CONVERT_VU_VI_VU(1), CONVERT_VU_VI_VU(2), CONVERT_VU_VI_VU(4), CONVERT_VU_VI_VU(8)
procedure :: CONVERT_VR_VI_VR(4), CONVERT_VR_VI_VR(8)
end interface vec_convert
public :: vec_convert

#undef VEC_CONVERT_VR_VI_VR
#undef VEC_CONVERT_VU_VI_VU
#undef VEC_CONVERT_VI_VI_VI
#undef CONVERT_VR_VI_VR
#undef CONVERT_VU_VI_VU
#undef CONVERT_VI_VI_VI

! vec_max
VEC_VI_VI_VI(vec_max,1) VEC_VI_VI_VI(vec_max,2) VEC_VI_VI_VI(vec_max,4) VEC_VI_VI_VI(vec_max,8)
VEC_VU_VU_VU(vec_max,1) VEC_VU_VU_VU(vec_max,2) VEC_VU_VU_VU(vec_max,4) VEC_VU_VU_VU(vec_max,8)
Expand Down Expand Up @@ -552,19 +668,18 @@ end function func_r8r8i
#undef VEC_VU_VR_VR
#undef VEC_VR_VR_VR
#undef VEC_VU_VU_VU
#undef VEC_V_VR_VU
#undef VEC_VU_VU_VU_2
#undef VEC_VI_VI_VI
#undef VEC_VU_VI_VI
#undef VEC_VI_VI_VU
#undef VEC_VI_VI_VU_2
#undef VU_VR_VR
#undef VR_VR_VU_2
#undef VR_VR_VR
#undef VU_VU_VU
#undef VU_VU_VU_2
#undef VI_VI_VU_2
#undef VI_VI_VU
#undef VR_VR_VU_2
#undef VI_VI_VU_2
#undef VU_VI_VI
#undef VI_VI_VI

Expand Down Expand Up @@ -669,4 +784,31 @@ end function func_r8r8i
#undef VU_VU_VU_I
#undef VI_VI_VI_I

!----------------------------------
! vector function(vector, integer)
!----------------------------------
! 'i0' stands for the integer argument being ignored via
! the `ignore_tkr' directive.
#define VR_VI_I(NAME, VKIND) __ppc_##NAME##_vr##VKIND##vi##VKIND##i0
#define VR_VU_I(NAME, VKIND) __ppc_##NAME##_vr##VKIND##vu##VKIND##i0

#define VEC_VR_VI_I(NAME, VKIND) \
procedure(elem_func_vr##VKIND##vi##VKIND##i) :: VR_VI_I(NAME, VKIND);
#define VEC_VR_VU_I(NAME, VKIND) \
procedure(elem_func_vr##VKIND##vu##VKIND##i) :: VR_VU_I(NAME, VKIND);

! vec_ctf
VEC_VR_VI_I(vec_ctf,4) VEC_VR_VI_I(vec_ctf,8)
VEC_VR_VU_I(vec_ctf,4) VEC_VR_VU_I(vec_ctf,8)
interface vec_ctf
procedure :: VR_VI_I(vec_ctf,4), VR_VI_I(vec_ctf,8)
procedure :: VR_VU_I(vec_ctf,4), VR_VU_I(vec_ctf,8)
end interface vec_ctf
public :: vec_ctf

#undef VEC_VR_VU_I
#undef VEC_VR_VI_I
#undef VR_VU_I
#undef VR_VI_I

end module __ppc_intrinsics

0 comments on commit 99dc393

Please sign in to comment.