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

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

Fix so that AVX builder does not use an AVX2 pmovmskb

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