问题描述
以下冗长的C程序生成一个简单的LLVM模块,其中包含仅调用llvm.x86.sse41.round.ps
的函数.它发出位代码文件,然后运行LLVM生成的代码.我的问题是如何找到主机的目标三元组和指令扩展(例如SSE或AVX),以及如何将此信息添加到LLVM模块,或者如何将其告知LLVM执行引擎.这是我的工作:
The following lengthy C program generates a simple LLVM module containing a function that merely calls llvm.x86.sse41.round.ps
. It emits the bitcode file and then runs the code generated by LLVM. My question is how do I find out target triple and instruction extensions like SSE or AVX of the host machine and how do I add this information to the LLVM module or how do I otherwise tell it to the LLVM execution engine. Here is, what I do:
$ cat ctest/avx-instruction-selection.c
#include <llvm-c/Core.h>
#include <llvm-c/Target.h>
#include <llvm-c/ExecutionEngine.h>
#include <llvm-c/BitWriter.h>
#include <llvm-c/Transforms/Scalar.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#if 1
const int vectorSize = 4;
const char* roundName = "llvm.x86.sse41.round.ps";
#else
const int vectorSize = 8;
const char* roundName = "llvm.x86.avx.round.ps.256";
#endif
int main ()
{
LLVMModuleRef module;
LLVMExecutionEngineRef execEngine;
LLVMTargetDataRef targetData;
LLVMTypeRef floatType, vectorType, ptrType, voidType, funcType, roundType, int32Type;
LLVMValueRef func, roundFunc;
LLVMValueRef param, loaded, const1, callRound;
LLVMBuilderRef builder;
LLVMBasicBlockRef block;
const int false = 0;
LLVMInitializeX86TargetInfo();
LLVMInitializeX86Target();
LLVMInitializeX86TargetMC();
module = LLVMModuleCreateWithName("_module");
LLVMSetTarget(module, "x86_64-unknown-linux-gnu");
floatType = LLVMFloatType();
vectorType = LLVMVectorType(floatType, vectorSize);
ptrType = LLVMPointerType(vectorType, 0);
voidType = LLVMVoidType();
LLVMTypeRef roundParams[] = { ptrType };
roundType = LLVMFunctionType(voidType, roundParams, 1, false);
func = LLVMAddFunction(module, "round", roundType);
LLVMSetLinkage(func, LLVMExternalLinkage);
builder = LLVMCreateBuilder();
block = LLVMAppendBasicBlock(func, "_L1");
LLVMPositionBuilderAtEnd(builder, block);
param = LLVMGetParam(func, 0);
loaded = LLVMBuildLoad(builder, param, "");
int32Type = LLVMIntType(32);
LLVMTypeRef funcParams[] = { vectorType, int32Type } ;
funcType = LLVMFunctionType(vectorType, funcParams, 2, false);
roundFunc = LLVMAddFunction(module, roundName, funcType);
LLVMSetLinkage(roundFunc, LLVMExternalLinkage);
const1 = LLVMConstInt(int32Type, 1, false);
LLVMValueRef callParams [] = { loaded, const1 } ;
callRound = LLVMBuildCall(builder, roundFunc, callParams, 2, "");
LLVMSetInstructionCallConv(callRound, 0);
LLVMAddInstrAttribute(callRound, 0, 0);
LLVMBuildStore(builder, callRound, param);
LLVMBuildRetVoid(builder);
LLVMWriteBitcodeToFile(module, "round-avx.bc");
char *errorMsg;
LLVMCreateExecutionEngineForModule(&execEngine, module, &errorMsg);
targetData = LLVMGetExecutionEngineTargetData(execEngine);
size_t vectorSize0 = LLVMStoreSizeOfType(targetData, vectorType);
size_t vectorAlign = LLVMABIAlignmentOfType(targetData, vectorType);
float vector[vectorSize];
printf("%lx, size %lx, align %lx\n", (size_t)vector, vectorSize0, vectorAlign);
LLVMGenericValueRef genericVector = LLVMCreateGenericValueOfPointer(vector);
LLVMGenericValueRef runParams[] = { genericVector } ;
LLVMRunFunction(execEngine, func, 1, runParams);
return 0;
}
$ gcc -Wall -o ctest/avx-instruction-selection ctest/avx-instruction-selection.c `/usr/lib/llvm-3.4/bin/llvm-config --cflags --ldflags` -lLLVM-3.4
$ ctest/avx-instruction-selection
7fff590431c0, size 10, align 10
$ ls round-avx.bc
round-avx.bc
$ llvm-dis -o - round-avx.bc
; ModuleID = 'round-avx.bc'
target triple = "x86_64-unknown-linux-gnu"
define void @round(<4 x float>*) {
_L1:
%1 = load <4 x float>* %0
%2 = call <4 x float> @llvm.x86.sse41.round.ps(<4 x float> %1, i32 1)
store <4 x float> %2, <4 x float>* %0
ret void
}
; Function Attrs: nounwind readnone
declare <4 x float> @llvm.x86.sse41.round.ps(<4 x float>, i32) #0
attributes #0 = { nounwind readnone }
$ gcc -Wall -o ctest/avx-instruction-selection ctest/avx-instruction-selection.c `/usr/lib/llvm-3.5/bin/llvm-config --cflags --ldflags` -lLLVM-3.5
$ ctest/avx-instruction-selection
7ffed6170350, size 10, align 10
LLVM ERROR: Cannot select: intrinsic %llvm.x86.sse41.round.ps
$ gcc -Wall -o ctest/avx-instruction-selection ctest/avx-instruction-selection.c `/usr/lib/llvm-3.6/bin/llvm-config --cflags --ldflags` -lLLVM-3.6
$ ctest/avx-instruction-selection
7ffeae91eb40, size 10, align 10
LLVM ERROR: Target does not support MC emission!
$ gcc -Wall -o ctest/avx-instruction-selection ctest/avx-instruction-selection.c `/usr/lib/llvm-3.7/bin/llvm-config --cflags --ldflags` -lLLVM-3.7
$ ctest/avx-instruction-selection
7fffb6464ea0, size 10, align 10
LLVM ERROR: Target does not support MC emission!
$ gcc -Wall -o ctest/avx-instruction-selection ctest/avx-instruction-selection.c `/usr/lib/llvm-3.8/bin/llvm-config --cflags --ldflags` -lLLVM-3.8
$ ctest/avx-instruction-selection
7ffd5e233000, size 10, align 10
LLVM ERROR: Target does not support MC emission!
总结:使用LLVM-3.4时,该示例起作用,使用LLVM-3.5时,找不到内在函数round.ps
,并且LLVM-3.6和更高版本中讲了一些我不理解的MC发射.
Summarized: With LLVM-3.4 the example works, with LLVM-3.5 the intrinsic function round.ps
cannot be found and LLVM-3.6 and later say something about MC emissions that I do not understand.
据我了解,LLVM-3.5找不到round.ps
内在函数,并且我猜找不到它,因为我没有告诉它现有的SSE扩展.运行llc
时,我可以添加选项-mattr=sse4.1
,但是如何将其告知执行引擎?
As I understand, LLVM-3.5 does not find the round.ps
intrinsic and I guess that it cannot find it because I have not told it about the existing SSE extension. When running llc
I can add the option -mattr=sse4.1
but how can I tell it to the execution engine?
第二个问题:如何通过LLVM-C API找出可用的指令扩展,例如主机的SSE?在x86上,我可以调用CPUID指令,但是有一种方法可以在所有平台上统一工作,并且LLVM可以帮助检测扩展吗?
Second question: How can I find out about the available instruction extensions like SSE of the host machine via the LLVM-C API? On x86 I can call the CPUID instruction but is there a way that works uniformly on all platforms and can LLVM assist detection of extensions?
第三个问题:我已经将目标三元组硬编码为C代码.如何通过LLVM-C API找出主机目标三元组?
Third question: I have hard-coded the target triple into the C code. How can I find out the host target-triple via the LLVM-C API?
最后一个问题:这个MC发射错误怎么办?
Last question: What about this MC emission error?
推荐答案
尝试了很多之后,我认为答案如下:
After trying around a lot I think the answer is as follows:
替换行
LLVMInitializeX86TargetInfo();
LLVMInitializeX86Target();
LLVMInitializeX86TargetMC();
作者
LLVMInitializeNativeTarget();
LLVMInitializeNativeAsmPrinter();
LLVMInitializeNativeAsmParser();
通过调用自定义函数LLVMCreateExecutionEngineForModuleCPU
代替LLVMCreateExecutionEngineForModule
的调用.它是LLVMCreateExecutionEngineForModule
的原始实现以及对setMCPU
的调用.
Replace the call of LLVMCreateExecutionEngineForModule
by a call to the custom function LLVMCreateExecutionEngineForModuleCPU
. It is the original implementation of LLVMCreateExecutionEngineForModule
plus a call of setMCPU
.
#define LLVM_VERSION (LLVM_VERSION_MAJOR * 100 + LLVM_VERSION_MINOR)
LLVMBool LLVMCreateExecutionEngineForModuleCPU
(LLVMExecutionEngineRef *OutEE,
LLVMModuleRef M,
char **OutError) {
std::string Error;
#if LLVM_VERSION < 306
EngineBuilder builder(unwrap(M));
#else
EngineBuilder builder(std::unique_ptr<Module>(unwrap(M)));
#endif
builder.setEngineKind(EngineKind::Either)
.setMCPU(sys::getHostCPUName().data())
.setErrorStr(&Error);
if (ExecutionEngine *EE = builder.create()){
*OutEE = wrap(EE);
return 0;
}
*OutError = strdup(Error.c_str());
return 1;
}
我还应该添加
float vector[vectorSize] __attribute__((aligned(32)));
为了使阵列与AVX向量对齐.
in order to align the array for AVX vectors.
根据线程使用AVX内在函数使JIT崩溃的答案 LLVMRunFunction
仅限于类似main
的原型(显然仅在MCJIT中).因此,我们还应将LLVMRunFunction
内容替换为
According to an answer in the thread crash JIT with AVX intrinsics LLVMRunFunction
is restricted to main
-like prototypes (apparently only in MCJIT). Thus we should also replace the LLVMRunFunction
stuff by
void (*funcPtr) (float *);
funcPtr = LLVMGetPointerToGlobal(execEngine, func);
funcPtr(vector);
这篇关于在LLVM-C API中确定和设置主机目标三元组和指令扩展的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!