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

Last change on this file since 6104 was 6094, checked in by cameron, 14 months ago

mvmd_compress 32 for SSE2

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