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

Last change on this file since 6057 was 6057, checked in by cameron, 11 months ago

mvmd_shuffle<64> for SSE2

File size: 9.0 KB
Line 
1/*
2 *  Copyright (c) 2016 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";}
16
17Value * IDISA_SSE2_Builder::hsimd_packh(unsigned fw, Value * a, Value * b) {   
18    if ((fw == 16) && (mBitBlockWidth == 128)) {
19        Value * packuswb_func = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_sse2_packuswb_128);
20        return CreateCall(packuswb_func, {simd_srli(16, a, 8), simd_srli(16, b, 8)});
21    }
22    // Otherwise use default logic.
23    return IDISA_Builder::hsimd_packh(fw, a, b);
24}
25
26Value * IDISA_SSE2_Builder::hsimd_packl(unsigned fw, Value * a, Value * b) {
27    if ((fw == 16) && (mBitBlockWidth == 128)) {
28        Value * packuswb_func = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_sse2_packuswb_128);
29        Value * mask = simd_lomask(16);
30        return CreateCall(packuswb_func, {fwCast(16, simd_and(a, mask)), fwCast(16, simd_and(b, mask))});
31    }
32    // Otherwise use default logic.
33    return IDISA_Builder::hsimd_packl(fw, a, b);
34}
35
36Value * IDISA_SSE2_Builder::hsimd_signmask(unsigned fw, Value * a) {
37    // SSE2 special case using Intrinsic::x86_sse2_movmsk_pd (fw=32 only)
38    if (mBitBlockWidth == 128) {
39        if (fw == 64) {
40            Value * signmask_f64func = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_sse2_movmsk_pd);
41            Type * bitBlock_f64type = VectorType::get(getDoubleTy(), mBitBlockWidth/64);
42            Value * a_as_pd = CreateBitCast(a, bitBlock_f64type);
43            return CreateCall(signmask_f64func, a_as_pd);
44        }
45        if (fw == 8) {
46            Value * pmovmskb_func = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_sse2_pmovmskb_128);
47            return CreateCall(pmovmskb_func, fwCast(8, a));
48        }
49    }
50    const auto fieldCount = mBitBlockWidth / fw;
51    if ((fieldCount > 4) && (fieldCount <= 16)) {
52        Value * pmovmskb_func = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_sse2_pmovmskb_128);
53        int fieldBytes = fw / 8;
54        int hiByte = fieldBytes - 1;
55        Constant * Idxs[16];
56        for (unsigned i = 0; i < fieldCount; i++) {
57            Idxs[i] = getInt32(fieldBytes * i + hiByte);
58        }
59        for (unsigned i = fieldCount; i < 16; i++) {
60            Idxs[i] = getInt32(mBitBlockWidth / 8);
61        }
62        Value * packh = CreateShuffleVector(fwCast(8, a), fwCast(8, allZeroes()), ConstantVector::get({Idxs, 16}));
63        return CreateCall(pmovmskb_func, packh);
64    }
65    // Otherwise use default SSE logic.
66    return IDISA_SSE_Builder::hsimd_signmask(fw, a);
67}
68
69Value * IDISA_SSE_Builder::hsimd_signmask(const unsigned fw, Value * a) {
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 (mBitBlockWidth == 128) {
76            return CreateCall(signmask_f32func, a_as_ps);
77        }
78    } else if ((fw == 64) && (mBitBlockWidth == 256)) {
79        Type * bitBlock_f32type = VectorType::get(getFloatTy(), mBitBlockWidth/32);
80        Value * a_as_ps = CreateBitCast(a, bitBlock_f32type);
81        Constant * Idxs[4];
82        for (unsigned i = 0; i < 4; i++) {
83            Idxs[i] = getInt32(2 * i + 1);
84        }
85        Value * packh = CreateShuffleVector(a_as_ps, UndefValue::get(bitBlock_f32type), ConstantVector::get({Idxs, 4}));
86        Type * halfBlock_f32type = VectorType::get(getFloatTy(), mBitBlockWidth/64);
87        Value * pack_as_ps = CreateBitCast(packh, halfBlock_f32type);
88        Value * signmask_f32func = Intrinsic::getDeclaration(getModule(), Intrinsic::x86_sse_movmsk_ps);
89        Value * mask = CreateCall(signmask_f32func, pack_as_ps);
90        return mask;
91    }
92    // Otherwise use default logic.
93    return IDISA_Builder::hsimd_signmask(fw, a);
94}
95
96#define SHIFT_FIELDWIDTH 64
97//#define LEAVE_CARRY_UNNORMALIZED
98
99// full shift producing {shiftout, shifted}
100std::pair<Value *, Value *> IDISA_SSE2_Builder::bitblock_advance(Value * a, Value * shiftin, unsigned shift) {
101    Value * shifted = nullptr;
102    Value * shiftout = nullptr;
103    Type * shiftTy = shiftin->getType();
104    if (LLVM_UNLIKELY(shift == 0)) {
105        return std::pair<Value *, Value *>(Constant::getNullValue(shiftTy), a);
106    }
107    Value * si = shiftin;
108    if (shiftTy != mBitBlockType) {
109        si = bitCast(CreateZExt(shiftin, getIntNTy(mBitBlockWidth)));
110    }
111    if (LLVM_UNLIKELY(shift == mBitBlockWidth)) {
112        return std::pair<Value *, Value *>(CreateBitCast(a, shiftTy), si);
113    }
114#ifndef LEAVE_CARRY_UNNORMALIZED
115    if (LLVM_UNLIKELY((shift % 8) == 0)) { // Use a single whole-byte shift, if possible.
116        shifted = simd_or(mvmd_slli(8, a, shift / 8), si);
117        shiftout = mvmd_srli(8, a, (mBitBlockWidth - shift) / 8);
118        return std::pair<Value *, Value *>(shiftout, shifted);
119    }
120    Value * shiftback = simd_srli(SHIFT_FIELDWIDTH, a, SHIFT_FIELDWIDTH - (shift % SHIFT_FIELDWIDTH));
121    Value * shiftfwd = simd_slli(SHIFT_FIELDWIDTH, a, shift % SHIFT_FIELDWIDTH);
122    if (LLVM_LIKELY(shift < SHIFT_FIELDWIDTH)) {
123        shiftout = mvmd_srli(SHIFT_FIELDWIDTH, shiftback, mBitBlockWidth/SHIFT_FIELDWIDTH - 1);
124        shifted = simd_or(simd_or(shiftfwd, si), mvmd_slli(SHIFT_FIELDWIDTH, shiftback, 1));
125    }
126    else {
127        shiftout = simd_or(shiftback, mvmd_srli(SHIFT_FIELDWIDTH, shiftfwd, 1));
128        shifted = simd_or(si, mvmd_slli(SHIFT_FIELDWIDTH, shiftfwd, (mBitBlockWidth - shift) / SHIFT_FIELDWIDTH));
129        if (shift < mBitBlockWidth - SHIFT_FIELDWIDTH) {
130            shiftout = mvmd_srli(SHIFT_FIELDWIDTH, shiftout, (mBitBlockWidth - shift) / SHIFT_FIELDWIDTH);
131            shifted = simd_or(shifted, mvmd_slli(SHIFT_FIELDWIDTH, shiftback, shift/SHIFT_FIELDWIDTH + 1));
132        }
133    }
134#endif
135#ifdef LEAVE_CARRY_UNNORMALIZED
136    shiftout = a;
137    if (LLVM_UNLIKELY((shift % 8) == 0)) { // Use a single whole-byte shift, if possible.
138        shifted = mvmd_dslli(8, a, shiftin, (mBitBlockWidth - shift) / 8);
139    }
140    else if (LLVM_LIKELY(shift < SHIFT_FIELDWIDTH)) {
141        Value * ahead = mvmd_dslli(SHIFT_FIELDWIDTH, a, shiftin, mBitBlockWidth / SHIFT_FIELDWIDTH - 1);
142        shifted = simd_or(simd_srli(SHIFT_FIELDWIDTH, ahead, SHIFT_FIELDWIDTH - shift), simd_slli(SHIFT_FIELDWIDTH, a, shift));
143    }
144    else {
145        throw std::runtime_error("Unsupported shift.");
146    }
147#endif
148    if (shiftTy != mBitBlockType) {
149        shiftout = CreateBitCast(shiftout, shiftTy);
150    }
151    //CallPrintRegister("shifted", shifted);
152    //CallPrintRegister("shiftout", shiftout);
153    return std::pair<Value *, Value *>(shiftout, shifted);
154}
155   
156Value * IDISA_SSE2_Builder::mvmd_shuffle(unsigned fw, Value * a, Value * shuffle_table) {
157    if ((mBitBlockWidth == 128) && (fw == 64)) {
158        // First create a vector with exchanged values of the 2 fields.
159        Constant * idx[2] = {ConstantInt::get(getInt32Ty(), 1), ConstantInt::get(getInt32Ty(), 0)};
160        Value * exchanged = CreateShuffleVector(a, UndefValue::get(fwVectorType(fw)), ConstantVector::get({idx, 2}));
161        // bits that change if the value in a needs to be exchanged.
162        Value * changed = simd_xor(a, exchanged);
163        // Now create a mask to select between original and exchanged values.
164        Constant * xchg[2] = {ConstantInt::get(getInt64Ty(), 1), ConstantInt::get(getInt64Ty(), 0)};
165        Value * xchg_vec = ConstantVector::get({xchg, 2});
166        Constant * oneSplat = ConstantVector::getSplat(2, ConstantInt::get(getInt64Ty(), 1));
167        Value * exchange_mask = simd_eq(fw, simd_and(shuffle_table, oneSplat), xchg_vec);
168        Value * rslt = simd_xor(simd_and(changed, exchange_mask), a);
169        return rslt;
170    }
171    return IDISA_Builder::mvmd_shuffle(fw, a, shuffle_table);
172}
173
174Value * IDISA_SSE_Builder::mvmd_compress(unsigned fw, Value * a, Value * selector) {
175    if ((mBitBlockWidth == 128) && (fw == 64)) {
176        Constant * keep[2] = {ConstantInt::get(getInt64Ty(), 1), ConstantInt::get(getInt64Ty(), 3)};
177        Constant * keep_mask = ConstantVector::get({keep, 2});
178        Constant * shift[2] = {ConstantInt::get(getInt64Ty(), 2), ConstantInt::get(getInt64Ty(), 0)};
179        Constant * shifted_mask = ConstantVector::get({shift, 2});
180        Value * a_srli1 = mvmd_srli(64, a, 1);
181        Value * bdcst = simd_fill(64, CreateZExt(selector, getInt64Ty()));
182        Value * kept = simd_and(simd_eq(64, simd_and(keep_mask, bdcst), keep_mask), a);
183        Value * shifted = simd_and(a_srli1, simd_eq(64, shifted_mask, bdcst));
184        return simd_or(kept, shifted);
185    }
186    return IDISA_Builder::mvmd_compress(fw, a, selector);
187}
188
189
190}
Note: See TracBrowser for help on using the repository browser.