source: icGREP/icgrep-devel/icgrep/IR_Gen/idisa_avx_builder.cpp @ 5843

Last change on this file since 5843 was 5841, checked in by cameron, 19 months ago

LLVM_VERSION_CODE macro, CC-multiplex option, performance bug fixes

File size: 12.5 KB
RevLine 
[4892]1/*
[5841]2 *  Copyright (c) 2018 International Characters.
[4892]3 *  This software is licensed to the public under the Open Software License 3.0.
4 *  icgrep is a trademark of International Characters.
5 */
6
7#include "idisa_avx_builder.h"
[5841]8#include <toolchain/toolchain.h>
[4892]9
[5464]10using namespace llvm;
11
[4892]12namespace IDISA {
[5374]13   
[5436]14std::string IDISA_AVX_Builder::getBuilderUniqueName() {
15    return mBitBlockWidth != 256 ? "AVX_" + std::to_string(mBitBlockWidth) : "AVX";
16}
[4892]17
[4903]18Value * IDISA_AVX_Builder::hsimd_signmask(unsigned fw, Value * a) {
[4996]19    // AVX2 special cases
[4956]20    if (mBitBlockWidth == 256) {
21        if (fw == 64) {
[5440]22            Value * signmask_f64func = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_avx_movmsk_pd_256);
[4956]23            Type * bitBlock_f64type = VectorType::get(getDoubleTy(), mBitBlockWidth/64);
24            Value * a_as_pd = CreateBitCast(a, bitBlock_f64type);
[5309]25            return CreateCall(signmask_f64func, a_as_pd);
26        } else if (fw == 32) {
[5440]27            Value * signmask_f32func = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_avx_movmsk_ps_256);
[4956]28            Type * bitBlock_f32type = VectorType::get(getFloatTy(), mBitBlockWidth/32);
29            Value * a_as_ps = CreateBitCast(a, bitBlock_f32type);
[5309]30            return CreateCall(signmask_f32func, a_as_ps);
[5828]31        } else if (fw == 8) {
32            Value * signmask_f8func = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_avx2_pmovmskb);
33            Type * bitBlock_i8type = VectorType::get(getInt8Ty(), mBitBlockWidth/8);
34            Value * a_as_ps = CreateBitCast(a, bitBlock_i8type);
35            return CreateCall(signmask_f8func, a_as_ps);
[4956]36        }
[5309]37    } else if (mBitBlockWidth == 512) {
[4956]38        if (fw == 64) {
[5309]39            Type * bitBlock_f32type = VectorType::get(getFloatTy(), mBitBlockWidth / 32);
[4956]40            Value * a_as_ps = CreateBitCast(a, bitBlock_f32type);
[5309]41            Constant * indicies[8];
[4956]42            for (unsigned i = 0; i < 8; i++) {
[5309]43                indicies[i] = getInt32(2 * i + 1);
[4956]44            }
[5309]45            Value * packh = CreateShuffleVector(a_as_ps, UndefValue::get(bitBlock_f32type), ConstantVector::get({indicies, 8}));
[4956]46            Type * halfBlock_f32type = VectorType::get(getFloatTy(), mBitBlockWidth/64);
47            Value * pack_as_ps = CreateBitCast(packh, halfBlock_f32type);
[5440]48            Value * signmask_f32func = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_avx_movmsk_ps_256);
[5309]49            return CreateCall(signmask_f32func, pack_as_ps);
[4956]50        }
[4892]51    }
[4996]52    // Otherwise use default SSE logic.
53    return IDISA_SSE_Builder::hsimd_signmask(fw, a);
[4892]54}
[5436]55
56std::string IDISA_AVX2_Builder::getBuilderUniqueName() {
57    return mBitBlockWidth != 256 ? "AVX2_" + std::to_string(mBitBlockWidth) : "AVX2";
58}
[4892]59   
[4955]60Value * IDISA_AVX2_Builder::hsimd_packh(unsigned fw, Value * a, Value * b) {
[5839]61    if ((fw > 8) && (fw <= 64)) {
[5309]62        Value * aVec = fwCast(fw / 2, a);
63        Value * bVec = fwCast(fw / 2, b);
64        const auto field_count = 2 * mBitBlockWidth / fw;
65        Constant * Idxs[field_count];
66        const auto H = (field_count / 2);
67        const auto Q = (field_count / 4);
68        for (unsigned i = 0; i < Q; i++) {
69            Idxs[i] = getInt32(2 * i);
70            Idxs[i + Q] = getInt32((2 * i) + 1);
71            Idxs[i + H] = getInt32((2 * i) + H);
72            Idxs[i + H + Q] = getInt32((2 * i) + 1 + H);
[4997]73        }
[5309]74        Value * shufa = CreateShuffleVector(aVec, aVec, ConstantVector::get({Idxs, field_count}));
75        Value * shufb = CreateShuffleVector(bVec, bVec, ConstantVector::get({Idxs, field_count}));
76        return hsimd_packh(mBitBlockWidth / 2, shufa, shufb);
[4955]77    }
[4996]78    // Otherwise use default SSE logic.
79    return IDISA_SSE_Builder::hsimd_packh(fw, a, b);
[4892]80}
[4955]81
82Value * IDISA_AVX2_Builder::hsimd_packl(unsigned fw, Value * a, Value * b) {
[5839]83    if ((fw > 8) && (fw <= 64)) {
[5309]84        Value * aVec = fwCast(fw / 2, a);
85        Value * bVec = fwCast(fw / 2, b);
86        const auto field_count = 2 * mBitBlockWidth / fw;
87        Constant * Idxs[field_count];
88        const auto H = (field_count / 2);
89        const auto Q = (field_count / 4);
90        for (unsigned i = 0; i < Q; i++) {
91            Idxs[i] = getInt32(2 * i);
92            Idxs[i + Q] = getInt32((2 * i) + 1);
93            Idxs[i + H] = getInt32((2 * i) + H);
94            Idxs[i + H + Q] = getInt32((2 * i) + H + 1);
[4997]95        }
[5309]96        Value * shufa = CreateShuffleVector(aVec, aVec, ConstantVector::get({Idxs, field_count}));
97        Value * shufb = CreateShuffleVector(bVec, bVec, ConstantVector::get({Idxs, field_count}));
98        return hsimd_packl(mBitBlockWidth / 2, shufa, shufb);
[4955]99    }
[4996]100    // Otherwise use default SSE logic.
101    return IDISA_SSE_Builder::hsimd_packl(fw, a, b);
[4955]102}
[4957]103   
104Value * IDISA_AVX2_Builder::esimd_mergeh(unsigned fw, Value * a, Value * b) {
[5841]105#if LLVM_VERSION_INTEGER < LLVM_VERSION_CODE(6, 0, 0)
[4957]106    if ((fw == 128) && (mBitBlockWidth == 256)) {
[5440]107        Value * vperm2i128func = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_avx2_vperm2i128);
[5014]108        return CreateCall(vperm2i128func, {fwCast(64, a), fwCast(64, b), getInt8(0x31)});
[4957]109    }
[5759]110#endif
[4996]111    // Otherwise use default SSE logic.
112    return IDISA_SSE_Builder::esimd_mergeh(fw, a, b);
[4955]113}
[4957]114
115Value * IDISA_AVX2_Builder::esimd_mergel(unsigned fw, Value * a, Value * b) {
[5841]116#if LLVM_VERSION_INTEGER < LLVM_VERSION_CODE(6, 0, 0)
[4957]117    if ((fw == 128) && (mBitBlockWidth == 256)) {
[5440]118        Value * vperm2i128func = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_avx2_vperm2i128);
[5014]119        return CreateCall(vperm2i128func, {fwCast(64, a), fwCast(64, b), getInt8(0x20)});
[4957]120    }
[5759]121#endif
[4996]122    // Otherwise use default SSE logic.
123    return IDISA_SSE_Builder::esimd_mergel(fw, a, b);
[4957]124}
125
126Value * IDISA_AVX2_Builder::hsimd_packl_in_lanes(unsigned lanes, unsigned fw, Value * a, Value * b) {
127    if ((fw == 16)  && (lanes == 2)) {
[5440]128        Value * vpackuswbfunc = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_avx2_packuswb);
[4957]129        Value * a_low = fwCast(16, simd_and(a, simd_lomask(fw)));
130        Value * b_low = fwCast(16, simd_and(b, simd_lomask(fw)));
[5309]131        return CreateCall(vpackuswbfunc, {a_low, b_low});
[4957]132    }
[4996]133    // Otherwise use default SSE logic.
134    return IDISA_SSE_Builder::hsimd_packl_in_lanes(lanes, fw, a, b);
[4957]135}
136
137Value * IDISA_AVX2_Builder::hsimd_packh_in_lanes(unsigned lanes, unsigned fw, Value * a, Value * b) {
138    if ((fw == 16)  && (lanes == 2)) {
[5440]139        Value * vpackuswbfunc = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_avx2_packuswb);
[4957]140        Value * a_low = simd_srli(fw, a, fw/2);
141        Value * b_low = simd_srli(fw, b, fw/2);
[5309]142        return CreateCall(vpackuswbfunc, {a_low, b_low});
[4957]143    }
[4996]144    // Otherwise use default SSE logic.
145    return IDISA_SSE_Builder::hsimd_packh_in_lanes(lanes, fw, a, b);
[4957]146}
147   
[5115]148std::pair<Value *, Value *> IDISA_AVX2_Builder::bitblock_add_with_carry(Value * e1, Value * e2, Value * carryin) {
149    // using LONG_ADD
150    Type * carryTy = carryin->getType();
151    if (carryTy == mBitBlockType) {
152        carryin = mvmd_extract(32, carryin, 0);
153    }
154    Value * carrygen = simd_and(e1, e2);
155    Value * carryprop = simd_or(e1, e2);
156    Value * digitsum = simd_add(64, e1, e2);
157    Value * digitcarry = simd_or(carrygen, simd_and(carryprop, CreateNot(digitsum)));
158    Value * carryMask = hsimd_signmask(64, digitcarry);
159    Value * carryMask2 = CreateOr(CreateAdd(carryMask, carryMask), carryin);
160    Value * bubble = simd_eq(64, digitsum, allOnes());
161    Value * bubbleMask = hsimd_signmask(64, bubble);
162    Value * incrementMask = CreateXor(CreateAdd(bubbleMask, carryMask2), bubbleMask);
163    Value * increments = esimd_bitspread(64,incrementMask);
164    Value * sum = simd_add(64, digitsum, increments);
165    Value * carry_out = CreateLShr(incrementMask, mBitBlockWidth / 64);
166    if (carryTy == mBitBlockType) {
167        carry_out = bitCast(CreateZExt(carry_out, getIntNTy(mBitBlockWidth)));
168    }
[5309]169    return std::pair<Value *, Value *>{carry_out, bitCast(sum)};
[4957]170}
[5115]171   
[5713]172std::pair<Value *, Value *> IDISA_AVX2_Builder::bitblock_indexed_advance(Value * strm, Value * index_strm, Value * shiftIn, unsigned shiftAmount) {
[5832]173    Value * const popcount = Intrinsic::getDeclaration(getModule(), Intrinsic::ctpop, getSizeTy());
[5713]174    Value * PEXT_f = nullptr;
175    Value * PDEP_f = nullptr;
[5832]176    const unsigned bitWidth = getSizeTy()->getBitWidth();
[5713]177    if (bitWidth == 64) {
178        PEXT_f = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_bmi_pext_64);
179        PDEP_f = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_bmi_pdep_64);
[5832]180    } else if ((bitWidth == 32)  && (shiftAmount < 32)) {
[5713]181        PEXT_f = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_bmi_pext_32);
182        PDEP_f = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_bmi_pdep_32);
[5832]183    } else {
[5713]184        llvm::report_fatal_error("indexed_advance unsupported bit width");
185    }
186    Type * iBitBlock = getIntNTy(getBitBlockWidth());
187    Value * shiftVal = getSize(shiftAmount);
[5832]188    const auto n = getBitBlockWidth() / bitWidth;
189    VectorType * const vecTy = VectorType::get(getSizeTy(), n);
[5713]190    if (LLVM_LIKELY(shiftAmount < bitWidth)) {
191        Value * carry = mvmd_extract(bitWidth, shiftIn, 0);
[5832]192        Value * result = UndefValue::get(vecTy);
193        for (unsigned i = 0; i < n; i++) {
[5713]194            Value * s = mvmd_extract(bitWidth, strm, i);
195            Value * ix = mvmd_extract(bitWidth, index_strm, i);
[5832]196            Value * ix_popcnt = CreateCall(popcount, {ix});
[5713]197            Value * bits = CreateCall(PEXT_f, {s, ix});
198            Value * adv = CreateOr(CreateShl(bits, shiftAmount), carry);
199            // We have two cases depending on whether the popcount of the index pack is < shiftAmount or not.
200            Value * popcount_small = CreateICmpULT(ix_popcnt, shiftVal);
[5832]201            Value * carry_if_popcount_small =
[5713]202                CreateOr(CreateShl(bits, CreateSub(shiftVal, ix_popcnt)),
203                            CreateLShr(carry, ix_popcnt));
204            Value * carry_if_popcount_large = CreateLShr(bits, CreateSub(ix_popcnt, shiftVal));
205            carry = CreateSelect(popcount_small, carry_if_popcount_small, carry_if_popcount_large);
206            result = mvmd_insert(bitWidth, result, CreateCall(PDEP_f, {adv, ix}), i);
207        }
208        Value * carryOut = mvmd_insert(bitWidth, allZeroes(), carry, 0);
209        return std::pair<Value *, Value *>{bitCast(carryOut), bitCast(result)};
210    }
[5715]211    else if (shiftAmount <= mBitBlockWidth) {
[5713]212        // The shift amount is always greater than the popcount of the individual
213        // elements that we deal with.   This simplifies some of the logic.
[5715]214        Value * carry = CreateBitCast(shiftIn, iBitBlock);
[5832]215        Value * result = UndefValue::get(vecTy);
216        for (unsigned i = 0; i < n; i++) {
[5713]217            Value * s = mvmd_extract(bitWidth, strm, i);
218            Value * ix = mvmd_extract(bitWidth, index_strm, i);
[5832]219            Value * ix_popcnt = CreateCall(popcount, {ix});
[5713]220            Value * bits = CreateCall(PEXT_f, {s, ix});  // All these bits are shifted out (appended to carry).
221            result = mvmd_insert(bitWidth, result, CreateCall(PDEP_f, {mvmd_extract(bitWidth, carry, 0), ix}), i);
222            carry = CreateLShr(carry, CreateZExt(ix_popcnt, iBitBlock)); // Remove the carry bits consumed, make room for new bits.
223            carry = CreateOr(carry, CreateShl(CreateZExt(bits, iBitBlock), CreateZExt(CreateSub(shiftVal, ix_popcnt), iBitBlock)));
224        }
225        return std::pair<Value *, Value *>{bitCast(carry), bitCast(result)};
226    }
[5715]227    else {
228        // The shift amount is greater than the total popcount.   We will consume popcount
229        // bits from the shiftIn value only, and produce a carry out value of the selected bits.
230        // elements that we deal with.   This simplifies some of the logic.
231        Value * carry = CreateBitCast(shiftIn, iBitBlock);
[5832]232        Value * result = UndefValue::get(vecTy);
233        Value * carryOut = ConstantInt::getNullValue(iBitBlock);
[5715]234        Value * generated = getSize(0);
[5832]235        for (unsigned i = 0; i < n; i++) {
[5715]236            Value * s = mvmd_extract(bitWidth, strm, i);
237            Value * ix = mvmd_extract(bitWidth, index_strm, i);
[5832]238            Value * ix_popcnt = CreateCall(popcount, {ix});
[5715]239            Value * bits = CreateCall(PEXT_f, {s, ix});  // All these bits are shifted out (appended to carry).
240            result = mvmd_insert(bitWidth, result, CreateCall(PDEP_f, {mvmd_extract(bitWidth, carry, 0), ix}), i);
[5719]241            carry = CreateLShr(carry, CreateZExt(ix_popcnt, iBitBlock)); // Remove the carry bits consumed.
242            carryOut = CreateOr(carryOut, CreateShl(CreateZExt(bits, iBitBlock), CreateZExt(generated, iBitBlock)));
[5715]243            generated = CreateAdd(generated, ix_popcnt);
244        }
245        return std::pair<Value *, Value *>{bitCast(carryOut), bitCast(result)};
246    }
[5115]247}
[5713]248
249}
Note: See TracBrowser for help on using the repository browser.