source: icGREP/icgrep-devel/icgrep/IR_Gen/idisa_sse_builder.cpp @ 6113

Last change on this file since 6113 was 6113, checked in by cameron, 16 months ago

hsimd_signmask support for various BlockSizes?

File size: 13.3 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_sse_builder.h"
8#include <llvm/IR/Intrinsics.h>
9#include <llvm/IR/Module.h>
10
11using namespace llvm;
12
13namespace IDISA {
14
15std::string IDISA_SSE_Builder::getBuilderUniqueName() { return mBitBlockWidth != 128 ? "SSE_" + std::to_string(mBitBlockWidth) : "SSE";}
16std::string IDISA_SSE2_Builder::getBuilderUniqueName() { return mBitBlockWidth != 128 ? "SSE2_" + std::to_string(mBitBlockWidth) : "SSE2";}
17std::string IDISA_SSSE3_Builder::getBuilderUniqueName() { return mBitBlockWidth != 128 ? "SSSE3_" + std::to_string(mBitBlockWidth) : "SSSE3";}
18
19Value * IDISA_SSE2_Builder::hsimd_packh(unsigned fw, Value * a, Value * b) {   
20    if ((fw == 16) && (getVectorBitWidth(a) == SSE_width)) {
21        Value * packuswb_func = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_sse2_packuswb_128);
22        return CreateCall(packuswb_func, {simd_srli(16, a, 8), simd_srli(16, b, 8)});
23    }
24    // Otherwise use default logic.
25    return IDISA_SSE_Builder::hsimd_packh(fw, a, b);
26}
27
28Value * IDISA_SSE2_Builder::hsimd_packl(unsigned fw, Value * a, Value * b) {
29    if ((fw == 16) && (getVectorBitWidth(a) == SSE_width)) {
30        Value * packuswb_func = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_sse2_packuswb_128);
31        Value * mask = simd_lomask(16);
32        return CreateCall(packuswb_func, {fwCast(16, simd_and(a, mask)), fwCast(16, simd_and(b, mask))});
33    }
34    // Otherwise use default logic.
35    return IDISA_Builder::hsimd_packl(fw, a, b);
36}
37
38Value * IDISA_SSE2_Builder::hsimd_signmask(unsigned fw, Value * a) {
39    // SSE2 special case using Intrinsic::x86_sse2_movmsk_pd (fw=32 only)
40    if (getVectorBitWidth(a) == SSE_width) {
41        if (fw == 64) {
42            Value * signmask_f64func = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_sse2_movmsk_pd);
43            Type * bitBlock_f64type = VectorType::get(getDoubleTy(), mBitBlockWidth/64);
44            Value * a_as_pd = CreateBitCast(a, bitBlock_f64type);
45            return CreateCall(signmask_f64func, a_as_pd);
46        }
47        if (fw == 8) {
48            Value * pmovmskb_func = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_sse2_pmovmskb_128);
49            return CreateCall(pmovmskb_func, fwCast(8, a));
50        }
51    }
52    // Otherwise use default SSE logic.
53    return IDISA_SSE_Builder::hsimd_signmask(fw, a);
54}
55
56Value * IDISA_SSE_Builder::hsimd_signmask(const unsigned fw, Value * a) {
57    const unsigned SSE_blocks = getVectorBitWidth(a)/SSE_width;
58    if (SSE_blocks > 1) {
59        Value * a_lo = CreateHalfVectorLow(a);
60        Value * a_hi = CreateHalfVectorHigh(a);
61        if ((fw == 8 * SSE_blocks) || (fw >= 32 * SSE_blocks)) {
62            return hsimd_signmask(fw/2, hsimd_packh(fw, a_hi, a_lo));
63        }
64        unsigned maskWidth = getVectorBitWidth(a)/fw;
65        Type * maskTy = getIntNTy(maskWidth);
66        Value * mask_lo = CreateZExtOrTrunc(hsimd_signmask(fw, a_lo), maskTy);
67        Value * mask_hi = CreateZExtOrTrunc(hsimd_signmask(fw, a_hi), maskTy);
68        return CreateOr(CreateShl(mask_hi, maskWidth/2), mask_lo);
69    }
70    // SSE special cases using Intrinsic::x86_sse_movmsk_ps (fw=32 only)
71    if (fw == 32) {
72        Value * signmask_f32func = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_sse_movmsk_ps);
73        Type * bitBlock_f32type = VectorType::get(getFloatTy(), mBitBlockWidth/32);
74        Value * a_as_ps = CreateBitCast(a, bitBlock_f32type);
75        if (getVectorBitWidth(a) == SSE_width) {
76            return CreateCall(signmask_f32func, a_as_ps);
77        }
78    }
79    // Otherwise use default logic.
80    return IDISA_Builder::hsimd_signmask(fw, a);
81}
82
83#define SHIFT_FIELDWIDTH 64
84//#define LEAVE_CARRY_UNNORMALIZED
85
86// full shift producing {shiftout, shifted}
87std::pair<Value *, Value *> IDISA_SSE2_Builder::bitblock_advance(Value * a, Value * shiftin, unsigned shift) {
88    Value * shifted = nullptr;
89    Value * shiftout = nullptr;
90    Type * shiftTy = shiftin->getType();
91    if (LLVM_UNLIKELY(shift == 0)) {
92        return std::pair<Value *, Value *>(Constant::getNullValue(shiftTy), a);
93    }
94    Value * si = shiftin;
95    if (shiftTy != mBitBlockType) {
96        si = bitCast(CreateZExt(shiftin, getIntNTy(mBitBlockWidth)));
97    }
98    if (LLVM_UNLIKELY(shift == mBitBlockWidth)) {
99        return std::pair<Value *, Value *>(CreateBitCast(a, shiftTy), si);
100    }
101#ifndef LEAVE_CARRY_UNNORMALIZED
102    if (LLVM_UNLIKELY((shift % 8) == 0)) { // Use a single whole-byte shift, if possible.
103        shifted = bitCast(simd_or(mvmd_slli(8, a, shift / 8), si));
104        shiftout = bitCast(mvmd_srli(8, a, (mBitBlockWidth - shift) / 8));
105        return std::pair<Value *, Value *>(shiftout, shifted);
106    }
107    Value * shiftback = simd_srli(SHIFT_FIELDWIDTH, a, SHIFT_FIELDWIDTH - (shift % SHIFT_FIELDWIDTH));
108    Value * shiftfwd = simd_slli(SHIFT_FIELDWIDTH, a, shift % SHIFT_FIELDWIDTH);
109    if (LLVM_LIKELY(shift < SHIFT_FIELDWIDTH)) {
110        shiftout = mvmd_srli(SHIFT_FIELDWIDTH, shiftback, mBitBlockWidth/SHIFT_FIELDWIDTH - 1);
111        shifted = simd_or(simd_or(shiftfwd, si), mvmd_slli(SHIFT_FIELDWIDTH, shiftback, 1));
112    }
113    else {
114        shiftout = simd_or(shiftback, mvmd_srli(SHIFT_FIELDWIDTH, shiftfwd, 1));
115        shifted = simd_or(si, mvmd_slli(SHIFT_FIELDWIDTH, shiftfwd, (mBitBlockWidth - shift) / SHIFT_FIELDWIDTH));
116        if (shift < mBitBlockWidth - SHIFT_FIELDWIDTH) {
117            shiftout = mvmd_srli(SHIFT_FIELDWIDTH, shiftout, (mBitBlockWidth - shift) / SHIFT_FIELDWIDTH);
118            shifted = simd_or(shifted, mvmd_slli(SHIFT_FIELDWIDTH, shiftback, shift/SHIFT_FIELDWIDTH + 1));
119        }
120    }
121#endif
122#ifdef LEAVE_CARRY_UNNORMALIZED
123    shiftout = a;
124    if (LLVM_UNLIKELY((shift % 8) == 0)) { // Use a single whole-byte shift, if possible.
125        shifted = mvmd_dslli(8, a, shiftin, (mBitBlockWidth - shift) / 8);
126    }
127    else if (LLVM_LIKELY(shift < SHIFT_FIELDWIDTH)) {
128        Value * ahead = mvmd_dslli(SHIFT_FIELDWIDTH, a, shiftin, mBitBlockWidth / SHIFT_FIELDWIDTH - 1);
129        shifted = simd_or(simd_srli(SHIFT_FIELDWIDTH, ahead, SHIFT_FIELDWIDTH - shift), simd_slli(SHIFT_FIELDWIDTH, a, shift));
130    }
131    else {
132        throw std::runtime_error("Unsupported shift.");
133    }
134#endif
135    if (shiftTy != mBitBlockType) {
136        shiftout = CreateBitCast(shiftout, shiftTy);
137    }
138    //CallPrintRegister("shifted", shifted);
139    //CallPrintRegister("shiftout", shiftout);
140    return std::pair<Value *, Value *>(shiftout, shifted);
141}
142   
143Value * IDISA_SSE2_Builder::mvmd_shuffle(unsigned fw, Value * a, Value * index_vector) {
144    if ((mBitBlockWidth == 128) && (fw == 64)) {
145        // First create a vector with exchanged values of the 2 fields.
146        Constant * idx[2] = {ConstantInt::get(getInt32Ty(), 1), ConstantInt::get(getInt32Ty(), 0)};
147        Value * exchanged = CreateShuffleVector(a, UndefValue::get(fwVectorType(fw)), ConstantVector::get({idx, 2}));
148        // bits that change if the value in a needs to be exchanged.
149        Value * changed = simd_xor(a, exchanged);
150        // Now create a mask to select between original and exchanged values.
151        Constant * xchg[2] = {ConstantInt::get(getInt64Ty(), 1), ConstantInt::get(getInt64Ty(), 0)};
152        Value * xchg_vec = ConstantVector::get({xchg, 2});
153        Constant * oneSplat = ConstantVector::getSplat(2, ConstantInt::get(getInt64Ty(), 1));
154        Value * exchange_mask = simd_eq(fw, simd_and(index_vector, oneSplat), xchg_vec);
155        Value * rslt = simd_xor(simd_and(changed, exchange_mask), a);
156        return rslt;
157    }
158    return IDISA_SSE_Builder::mvmd_shuffle(fw, a, index_vector);
159}
160
161Value * IDISA_SSE_Builder::mvmd_compress(unsigned fw, Value * a, Value * selector) {
162    if ((mBitBlockWidth == 128) && (fw == 64)) {
163        Constant * keep[2] = {ConstantInt::get(getInt64Ty(), 1), ConstantInt::get(getInt64Ty(), 3)};
164        Constant * keep_mask = ConstantVector::get({keep, 2});
165        Constant * shift[2] = {ConstantInt::get(getInt64Ty(), 2), ConstantInt::get(getInt64Ty(), 0)};
166        Constant * shifted_mask = ConstantVector::get({shift, 2});
167        Value * a_srli1 = mvmd_srli(64, a, 1);
168        Value * bdcst = simd_fill(64, CreateZExt(selector, getInt64Ty()));
169        Value * kept = simd_and(simd_eq(64, simd_and(keep_mask, bdcst), keep_mask), a);
170        Value * shifted = simd_and(a_srli1, simd_eq(64, shifted_mask, bdcst));
171        return simd_or(kept, shifted);
172    }
173    if ((mBitBlockWidth == 128) && (fw == 32)) {
174        Value * bdcst = simd_fill(32, CreateZExtOrTrunc(selector, getInt32Ty()));
175        Constant * fieldBit[4] =
176        {ConstantInt::get(getInt32Ty(), 1), ConstantInt::get(getInt32Ty(), 2),
177            ConstantInt::get(getInt32Ty(), 4), ConstantInt::get(getInt32Ty(), 8)};
178        Constant * fieldMask = ConstantVector::get({fieldBit, 4});
179        Value * a_selected = simd_and(simd_eq(32, fieldMask, simd_and(fieldMask, bdcst)), a);
180        Constant * rotateInwards[4] =
181        {ConstantInt::get(getInt32Ty(), 1), ConstantInt::get(getInt32Ty(), 0),
182            ConstantInt::get(getInt32Ty(), 3), ConstantInt::get(getInt32Ty(), 2)};
183        Constant * rotateVector = ConstantVector::get({rotateInwards, 4});
184        Value * rotated = CreateShuffleVector(fwCast(32, a_selected), UndefValue::get(fwVectorType(fw)), rotateVector);
185        Constant * rotate_bit[2] = {ConstantInt::get(getInt64Ty(), 2), ConstantInt::get(getInt64Ty(), 4)};
186        Constant * rotate_mask = ConstantVector::get({rotate_bit, 2});
187        Value * rotateControl = simd_eq(64, fwCast(64, simd_and(bdcst, rotate_mask)), allZeroes());
188        Value * centralResult = simd_if(1, rotateControl, rotated, a_selected);
189        Value * delete_marks_lo = CreateAnd(CreateNot(selector), ConstantInt::get(selector->getType(), 3));
190        Value * delCount_lo = CreateSub(delete_marks_lo, CreateLShr(delete_marks_lo, 1));
191        return mvmd_srl(32, centralResult, delCount_lo);
192    }
193    return IDISA_Builder::mvmd_compress(fw, a, selector);
194}
195
196Value * IDISA_SSSE3_Builder::esimd_mergeh(unsigned fw, Value * a, Value * b) {
197    if ((fw == 1) || (fw == 2)) {
198        Constant * interleave_table = bit_interleave_byteshuffle_table(fw);
199        // Merge the bytes.
200        Value * byte_merge = esimd_mergeh(8, a, b);
201        Value * low_bits = mvmd_shuffle(8, interleave_table, fwCast(8, simd_and(byte_merge, simd_lomask(8))));
202        Value * high_bits = simd_slli(16, mvmd_shuffle(8, interleave_table, fwCast(8, simd_srli(8, byte_merge, 4))), fw);
203        // For each 16-bit field, interleave the low bits of the two bytes.
204        low_bits = simd_or(simd_select_lo(16, low_bits), simd_srli(16, low_bits, 8-fw));
205        // For each 16-bit field, interleave the high bits of the two bytes.
206        high_bits = simd_or(simd_select_hi(16, high_bits), simd_slli(16, high_bits, 8-fw));
207        return simd_or(low_bits, high_bits);
208    }
209    // Otherwise use default SSE logic.
210    return IDISA_SSE2_Builder::esimd_mergeh(fw, a, b);
211}
212
213Value * IDISA_SSSE3_Builder::esimd_mergel(unsigned fw, Value * a, Value * b) {
214    if ((fw == 1) || (fw == 2)) {
215        Constant * interleave_table = bit_interleave_byteshuffle_table(fw);
216        // Merge the bytes.
217        Value * byte_merge = esimd_mergel(8, a, b);
218        Value * low_bits = mvmd_shuffle(8, interleave_table, fwCast(8, simd_and(byte_merge, simd_lomask(8))));
219        Value * high_bits = simd_slli(16, mvmd_shuffle(8, interleave_table, fwCast(8, simd_srli(8, byte_merge, 4))), fw);
220        // For each 16-bit field, interleave the low bits of the two bytes.
221        low_bits = simd_or(simd_select_lo(16, low_bits), simd_srli(16, low_bits, 8-fw));
222        // For each 16-bit field, interleave the high bits of the two bytes.
223        high_bits = simd_or(simd_select_hi(16, high_bits), simd_slli(16, high_bits, 8-fw));
224        return simd_or(low_bits, high_bits);
225    }
226    // Otherwise use default SSE2 logic.
227    return IDISA_SSE2_Builder::esimd_mergel(fw, a, b);
228}
229
230llvm::Value * IDISA_SSSE3_Builder::mvmd_shuffle(unsigned fw, llvm::Value * data_table, llvm::Value * index_vector) {
231    if (mBitBlockWidth == 128 && fw > 8) {
232        // Create a table for shuffling with smaller field widths.
233        const unsigned fieldCount = mBitBlockWidth/fw;
234        Constant * idxMask = ConstantVector::getSplat(fieldCount, ConstantInt::get(getIntNTy(fw), fieldCount-1));
235        Value * idx = simd_and(index_vector, idxMask);
236        unsigned half_fw = fw/2;
237        unsigned field_count = mBitBlockWidth/half_fw;
238        // Build a ConstantVector of alternating 0 and 1 values.
239        Constant * Idxs[field_count];
240        for (unsigned int i = 0; i < field_count; i++) {
241            Idxs[i] = ConstantInt::get(getIntNTy(fw/2), i & 1);
242        }
243        Constant * splat01 = ConstantVector::get({Idxs, field_count});
244       
245        Value * half_fw_indexes = simd_or(idx, mvmd_slli(half_fw, idx, 1));
246        half_fw_indexes = simd_add(fw, simd_add(fw, half_fw_indexes, half_fw_indexes), splat01);
247        Value * rslt = mvmd_shuffle(half_fw, data_table, half_fw_indexes);
248        return rslt;
249    }
250    if (mBitBlockWidth == 128 && fw == 8) {
251        Value * shuf8Func = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_ssse3_pshuf_b_128);
252        return CreateCall(shuf8Func, {fwCast(8, data_table), fwCast(8, simd_and(index_vector, simd_lomask(8)))});
253    }
254    return IDISA_SSE2_Builder::mvmd_shuffle(fw, data_table, index_vector);
255}
256
257
258}
Note: See TracBrowser for help on using the repository browser.