2016-07-26 59 views
1

我正在寫一個LLVM通道,將原始程序中浮點變量的類型改爲long double。一個玩具測試程序是:如何通過編寫LLVM通道將原始程序中浮點變量的類型改爲long double?

int main(){ 
    double i = 0.0000000000000001; 
    if(i + 1 > 1) 
     printf("correct answer"); 
    else 
     printf("wrong answer"); 
    return 0; 
} 

我的通行證需要改變我的類型爲「長雙」。 原始程序的IR與轉換程序之間有五個不同的地方。

< %i = alloca x86_fp80, align 16 
--- 
> %i = alloca double, align 8 

< store x86_fp80 0xK3FC9E69594BEC44DE000, x86_fp80* %i, align 16 
< %0 = load x86_fp80, x86_fp80* %i, align 16 
< %add = fadd x86_fp80 %0, 0xK3FFF8000000000000000 
< %cmp = fcmp ogt x86_fp80 %add, 0xK3FFF8000000000000000 
--- 
> store double 1.000000e-16, double* %i, align 8 
> %0 = load double, double* %i, align 8 
> %add = fadd double %0, 1.000000e+00 
> %cmp = fcmp ogt double %add, 1.000000e+00 

我的通行證做上述轉換的要點如下:

virtual bool runOnFunction(Function &F) { 
    std::string svariable ("i"); 
    const ValueSymbolTable& symbolTable = F.getValueSymbolTable(); 
    Value* target = symbolTable.lookup(svariable); 
    AllocaInst* old_target = dyn_cast<AllocaInst>(target); 
    errs() <<"old_target: " << *target << "\n"; 
    errs() <<"num of old_target_uses:" << old_target->getNumUses() <<"\n"; 

    //get the type of long double and construct new AllocaInst 
    LLVMContext &context = F.getContext(); 
    Type* type = Type::getX86_FP80Ty(context); 
    unsigned alignment = 16; 
    AllocaInst* new_target = new AllocaInst(type, 0, alignment, "new", old_target); 
    new_target->takeName(old_target); 

    // iterating through instructions using old AllocaInst 
    Value::use_iterator it = old_target->use_begin(); 
    for(; it != old_target->use_end(); it++) { 
      Value * temp = it->get(); 
      errs() <<"temp:" << *temp <<"\n"; 
      //transform() is under construction 
      transform(it, new_target, type, alignment); 

    } 
    old_target->eraseFromParent(); 
    return false; 
} 

應該有我如在原計劃的IR翻番相關四條指令:

> store double 1.000000e-16, double* %i, align 8 
> %0 = load double, double* %i, align 8 
> %add = fadd double %0, 1.000000e+00 
> %cmp = fcmp ogt double %add, 1.000000e+00 

但並不如上述預期通的輸出:

old_target: %i = alloca double, align 8 
num of old_target_uses:2 
temp: %0 = alloca double, align 8 
temp: %0 = alloca double, align 8 

所以我的第一個問題是爲什麼getNumUses()和use_iterator沒有返回正確答案,我在通行證的輪廓中是否以錯誤的方式使用它們?我的第二個問題是在我的transform()函數中,我需要枚舉LoadInst,StoreInst,BinaryOperation等各種指令,並用新類型重新構造它們,對不對?

預先:)

+0

你爲什麼選擇在LLVM級別而不是在(例如使用鏘)源代碼級攻擊這個問題? – eush77

+0

非常感謝您的意見。在LLVM IR級別攻擊它,我將能夠方便地支持任何編譯(或將編譯)到LLVM的源語言,包括C/C++,Fortran,R等。 – Sue

回答

1

關於第一個問題非常感謝,每個Use目的是基本上處於Value(主要是指令或恆定)鏈接到其Users(指令或常數中的一個的數據流圖中的邊)。這兩個值可分別通過Use::getUse::getUser訪問。

Value::use_iterator it = old_target->use_begin(); 
for(; it != old_target->use_end(); it++) { 
     Value * temp = it->get(); 
} 

在這裏,當你重複上old_target使用和分配temp每次使用的使用值,什麼被分配實際上old_target本身。我相信你之後是it->getUser,這將是一個用戶,每次都不一樣。

注意getNumUses()實際上是正確的,因爲在你的榜樣%i使用了兩次,第一次在store,然後在load

我的第二個問題是在我的變換()函數,我需要枚舉 各種指令,如LoadInst,StoreInst,BinaryOperation 和重建它們與新的類型,對不對?

至於實際替換類型,我認爲這大致是這裏所要求的。請注意,通常用這種方式替換類型可能會導致錯誤的結果,因此您可能需要檢查是否沒有指令(如bitcastptrtoint)首先對這些變量進行操作。我建議你僅支持源代碼爲allocas的源代碼,以及從一開始就對這些源代碼進行操作的有限指令子集,並從此處擴展子集。

然後,將改變每個用戶的方式以適應其操作數從double/double*改變類型x86_fp80/x86_fp80*之一。如果它的結果​​類型發生變化,你也需要傳播它。爲此,您可能會發現工作列表模式很有用 - 這就是LLVM自身的組織數量(example)。

通常更新IR的方法是Value::replaceAllUsesWith。但是,在您的情況下,類型也很可能會發生變化,這會導致此函數失敗,並顯示錯誤消息。因此,您需要手動更改IR和類型,可能需要使用User::setOperandValue::mutateType的某些組合,並在需要時創建新指令。

例如:

  • loadfaddfcmp和類似的指示,更換操作數(一個或多個),並將結果添加到工作列表;
  • store指令:
    • 如果第一操作數的類型已經改變,中止處理,除非該第二操作數的類型也改變;
    • 如果第二個操作數的類型已更改,但第一個操作數的類型未更改,請將第一個操作數轉換爲x86_fp80(使用fpext);
    • 無論如何,將結果添加到工作清單;
  • callinvoke說明,投x86_fp80操作數doublefptrunc(或中止,如果要禁止這樣的向下轉換);
  • 對於bitcastptrtoint指令,中止處理;
  • ...
+0

非常感謝您的回答。是非常有幫助的。它應該是getUser(),正如你所說的那樣。非常感謝有關transform()的有用建議,這讓我更清楚如何現在編寫它。 – Sue

相關問題