簡體   English   中英

Clang / GCC編譯器內在函數沒有相應的編譯器標志

[英]Clang/GCC Compiler Intrinsics without corresponding compiler flag

我知道有這種類似的問題,但在編譯不同的文件以不同的標志在這里是不能接受的解決方案,因為它會變得復雜代碼庫真正的快。 回答“不,這是不可能的”將會做到。


在任何版本的Clang OR GCC中,是否可以為SSE ​​2/3 / 3S / 4.1編譯內在函數,同時只允許編譯器使用SSE指令集進行優化?

編輯 :例如,我希望編譯器將_mm_load_si128()movdqa ,但編譯器不能在movdqa內在函數之外的任何其他位置發出此指令,類似於MSVC編譯器的工作方式。

EDIT2 :我有動態調度程序和幾個版本的單個函數,使用內在函數編寫不同的指令集。 使用多個文件會使維護更加困難,因為相同版本的代碼將跨越多個文件,並且有很多這種類型的函數。

EDIT3 :請求的示例源代碼: https//github.com/AviSynth/AviSynthPlus/blob/master/avs_core/filters/resample.cpp或該文件夾中的大多數文件。

這是一種使用gcc的方法,可能是可以接受的。 所有源代碼都進入單個源文件。 單個源文件分為幾個部分。 一節根據使用的命令行選項生成代碼。 main()和處理器功能檢測等功能在本節中介紹。 另一部分根據目標覆蓋編譯指示生成代碼。 可以使用目標覆蓋值支持的內部函數。 只有在處理器功能檢測確認存在所需的處理器功能后,才應調用本節中的功能。 此示例具有AVX2代碼的單個覆蓋部分。 編寫針對多個目標優化的函數時,可以使用多個覆蓋部分。

// temporarily switch target so that all x64 intrinsic functions will be available
#pragma GCC push_options
#pragma GCC target ("arch=core-avx2")
#include <intrin.h>
// restore the target selection
#pragma GCC pop_options

//----------------------------------------------------------------------------
// the following functions will be compiled using default code generation
//----------------------------------------------------------------------------

int dummy1 (int a) {return a;}

//----------------------------------------------------------------------------
// the following functions will be compiled using core-avx2 code generation
// all x64 intrinc functions are available
#pragma GCC push_options
#pragma GCC target ("arch=core-avx2")
//----------------------------------------------------------------------------

static __m256i bitShiftLeft256ymm (__m256i *data, int count)
   {
   __m256i innerCarry, carryOut, rotate;

   innerCarry = _mm256_srli_epi64 (*data, 64 - count);                        // carry outs in bit 0 of each qword
   rotate     = _mm256_permute4x64_epi64 (innerCarry, 0x93);                  // rotate ymm left 64 bits
   innerCarry = _mm256_blend_epi32 (_mm256_setzero_si256 (), rotate, 0xFC);   // clear lower qword
   *data    = _mm256_slli_epi64 (*data, count);                               // shift all qwords left
   *data    = _mm256_or_si256 (*data, innerCarry);                            // propagate carrys from low qwords
   carryOut   = _mm256_xor_si256 (innerCarry, rotate);                        // clear all except lower qword
   return carryOut;
   }

//----------------------------------------------------------------------------
// the following functions will be compiled using default code generation
#pragma GCC pop_options
//----------------------------------------------------------------------------

int main (void)
    {
    return 0;
    }

//----------------------------------------------------------------------------

我正在恢復這個線程,因為我剛剛遇到了同樣的問題,最近在Visual Studio中添加了LLVM / clang-cl支持。 我需要在運行時檢查(例如)AVX支持的代碼。 使用MSVC編譯器,我可以使用:

bool hasAVX(void) {
    __try { // VMASKMOVPD = AVX instruction
        __declspec(align(32)) double d[4] = { 4.4, 3.3, 2.2, 1.1 };
        __m256d dData = _mm256_setr_pd(8.888, 7.777, 6.666, 5.555);
        _mm256_store_pd(d, dData);
    }
    __except (EXCEPTION_EXECUTE_HANDLER) { return false; }
    return true;
}

它構建時沒有任何特殊的架構標志(不需要/arch:AVX)。但是,使用clang-cl,如果沒有-mavx構建,“__ m256d”和“mm256_setr_pd”都沒有定義,即使我用#include #include (全局)頭文件中的pragma clang屬性

#pragma clang attribute push (__attribute__((target("arch=avx"))), apply_to=function)
#include <intrin.h>
#pragma clang attribute pop

有線索嗎?

阿德里安

PS:我不能將此作為評論添加,因為我的“聲譽”低於50!

除了編譯器本身的開關之外,無法控制用於編譯器的指令集。 換句話說,沒有編譯指示或其他功能,只有整個編譯器標志。

這意味着實現你想要的唯一可行的解​​決方案是使用-msseX並將你的源分成多個文件(當然,你總是可以使用各種聰明的#include等來保持一個單獨的文本文件作為主要來源,並且只是在多個地方包含相同的文件)

當然,編譯器的源代碼是可用的。 我相信GCC和Clang / LLVM的維護人員會很樂意接受改進的補丁。 但請記住,從“解析源”到“發出指令”的路徑是漫長而復雜的。 如果我們這樣做會發生什么:

#pragma use_sse=1
void func()
{
   ... some code goes here ... 
}

#pragma use_sse=3
void func2()
{
  ...
  func();
  ...
}

現在,func足夠短,可以內聯,編譯器應該內聯嗎? 如果是這樣,它應該使用func()的sse1或sse3指令。

我知道你可能不關心那種困難,但是Clang和GCC的維護者確實必須以某種方式處理這個問題。

編輯:在聲明SSE內在函數(以及許多其他內在函數)的頭文件中,典型函數看起來像這樣:

extern __inline __m128 __attribute__((__gnu_inline__, __always_inline__, __artificial__))
_mm_add_ss (__m128 __A, __m128 __B)
{
  return (__m128) __builtin_ia32_addss ((__v4sf)__A, (__v4sf)__B);
}

只有在啟用了-msse選項后,才能在編譯器中使用builtin_ia32_addss。 因此,如果您說服編譯器仍然允許您在使用-mno-sse時使用_mm_add_ss(),則會出現“__builtin_ia32_addss未在此范圍內聲明”的錯誤(我剛試過)。

改變這種特殊行為可能不是很難 - 可能只有少數幾個地方代碼“引入內置函數”。 但是,我不相信代碼中還有其他問題,稍后會在編譯器中實際發出指令。

我已經在基於Clang的編譯器中使用“內置函數”做了一些工作,不幸的是,從“解析器”到“代碼生成”涉及到內置函數所涉及的幾個步驟。

EDIT2:

與GCC相比,為Clang解決這個問題更復雜,因為編譯器本身已經理解了SSE指令,所以它只是在頭文件中有這個:

static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_add_ps(__m128 __a, __m128 __b)
{
  return __a + __b;
}

然后編譯器知道要添加幾個__m128,它需要產生正確的SSE指令。 我剛剛下載了Clang(我在家里,我對Clang的工作正在進行中,與SSE完全沒有關系,只是內置函數 - 而且我沒有真正對Clang做過很多改動,但它足以大致了解內置函數的工作方式)。

但是,從您的角度來看,它不是內置函數的事實會使它變得更糟,因為operator+轉換要復雜得多。 我很確定編譯器只是把它變成了“添加這兩件事”,然后將它傳遞給LLVM以進行進一步的工作 - LLVM將是了解SSE指令等的部分。但是為了你的目的,這會使情況更糟,因為這是一個“內在函數”的事實現在幾乎丟失了,編譯器只是處理它就像你寫了一個+ b一樣,a和b的副作用是128位長的類型。 這使得處理生成“正確的指令”並將“所有其他”指令保持在不同的SSE級別變得更加復雜。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM