llvm生成rdrand指令

問題

在做一個project的時候需要使用llvm的pass對函數進行插樁,在每一個函數頭之前插入一條指令 rdrand %rax,在尋找llvm基本指令之後發現並沒有生成隨機數的指令,這時就想到了llvm中intrinsic函數中是否有關於rdrand指令的函數,在對llvm整個源碼進行掃描之後,發現有x86的rdrand的intrinsic函數:


  X86_INTRINSIC_DATA(rdrand_16, RDRAND, X86ISD::RDRAND, 0),
  X86_INTRINSIC_DATA(rdrand_32, RDRAND, X86ISD::RDRAND, 0),
  X86_INTRINSIC_DATA(rdrand_64, RDRAND, X86ISD::RDRAND, 0),

既然有對它們的定義,那是否有關於它們的使用呢,又經過一番查找,終於在一個測試文件中(/test/CodeGen/X86/rdrand.ll)找到相應的使用:

declare {i16, i32} @llvm.x86.rdrand.16()
declare {i32, i32} @llvm.x86.rdrand.32()
declare {i64, i32} @llvm.x86.rdrand.64()

define i32 @_rdrand16_step(i16* %random_val) {
  %call = call {i16, i32} @llvm.x86.rdrand.16()
  %randval = extractvalue {i16, i32} %call, 0
  store i16 %randval, i16* %random_val
  %isvalid = extractvalue {i16, i32} %call, 1
  ret i32 %isvalid
; CHECK-LABEL: _rdrand16_step:
; CHECK: rdrandw    %ax
; CHECK: movzwl %ax, %ecx
; CHECK: movl   $1, %eax
; CHECK: cmovael    %ecx, %eax
; CHECK: movw   %cx, (%r[[A0:di|cx]])
; CHECK: ret
}

define i32 @_rdrand32_step(i32* %random_val) {
  %call = call {i32, i32} @llvm.x86.rdrand.32()
  %randval = extractvalue {i32, i32} %call, 0
  store i32 %randval, i32* %random_val
  %isvalid = extractvalue {i32, i32} %call, 1
  ret i32 %isvalid
; CHECK-LABEL: _rdrand32_step:
; CHECK: rdrandl    %e[[T0:[a-z]+]]
; CHECK: movl   $1, %eax
; CHECK: cmovael    %e[[T0]], %eax
; CHECK: movl   %e[[T0]], (%r[[A0]])
; CHECK: ret
}

define i32 @_rdrand64_step(i64* %random_val) {
  %call = call {i64, i32} @llvm.x86.rdrand.64()
  %randval = extractvalue {i64, i32} %call, 0
  store i64 %randval, i64* %random_val
  %isvalid = extractvalue {i64, i32} %call, 1
  ret i32 %isvalid
; CHECK-LABEL: _rdrand64_step:
; CHECK: rdrandq    %r[[T1:[a-z]+]]
; CHECK: movl   $1, %eax
; CHECK: cmovael    %e[[T1]], %eax
; CHECK: movq   %r[[T1]], (%r[[A0]])
; CHECK: ret
}

這個系列函數是對rdrandx_step(address)系列函數的包裝,在這些函數中是生成隨機數,並將該隨機數保存到address參數中。發現在該系列函數中對@llvm.x86.rdrand.xx()系列函數的調用,可以發現,在該函數第一個語句是對@llvm.x86.rdrand.xx() intrinsic函數的調用,該函數返回一個StructType類型的結果,該結果一共有兩個成員組成,第一個成員是@llvm.x86.rdrand.xx()函數產生的隨機數,第二個參數表示該函數是否調用成功。第二個語句就是從結果中取出隨機數。弄清了該函數的邏輯後就可以通過llvm C API產生相應的IR指令。

llvm C API

下面是我通過llvm的C API產生的相應的IR指令的代碼:


AllocaInst* AI3 = B.CreateAlloca(PtrTy, nullptr, "RandomValue");

std::vector<Type *> arg_type;

Function *fun = Intrinsic::getDeclaration(F->getParent(), Intrinsic::x86_rdrand_64, arg_type);
CallInst* result = B.CreateCall(fun, {});
    //B.CreateRet(result);
    //result->dump();
if (dyn_cast<StructType>(result->getType())) {
        //errs() << "Hello\n";
    Value* randomValue = B.CreateExtractValue(result, (uint64_t)0);
    Value* randomValuePtr = B.CreateIntToPtr(randomValue, Type::getInt8PtrTy(B.getContext()));
    B.CreateStore(randomValuePtr, AI3, true);
}

注意事項

在生成了.ll文件後,需要用llc工具生成二進制文件,此時需要添加-mattr=rdrnd的屬性:

llc -filetype=obj input.ll -mattr=+rdrnd -o output.o

否則會出現LLVM ERROR: Cannot select: t74: i64,i32,ch = X86ISD::RDRAND t0錯誤

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章