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

Last change on this file since 6087 was 6087, checked in by cameron, 10 months ago

IDISA builder updates for esimd_mergel/h

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