日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

关于llvm kaleidoscope: 记一次Debug血泪之路

發(fā)布時間:2023/12/13 综合教程 44 生活家
生活随笔 收集整理的這篇文章主要介紹了 关于llvm kaleidoscope: 记一次Debug血泪之路 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

簡而言之,慎(bu)用(yong)全局變量

這次debug基本上花了我一周的時間,我基本上是晚上9點30下自習(xí)回然后調(diào)試到11點30,如此反復(fù)一周直到今天周五終于解決了,,以前都聽說前輩們 說盡量不要使用全局變量,我只當個笑話順而過,今天我可能走了前輩們的老路,我實在忍不住要告誡各位請慎用全局變量,如果不當笑話對待這點那這篇文章目的就達到了,后面可以省略了。

以下是可以被省略的正文。上學(xué)期到這學(xué)期始我林林總總寫過幾個編譯器前端,有l(wèi)exyacc自底向上自動生成的也有手寫詞法分析自頂向下的遞歸下降分析,但是還從來沒做過后端,一來是感覺自己差點火候二來也太懶感覺量大繁瑣。這學(xué)期開學(xué)偶然在知乎聽說llvm有成熟的代碼生成優(yōu)化以及到到目標機器的代碼生成,想來自己看了那么多theory還從來沒有實踐過真正的編譯器,說不遺憾肯定是假的,然后我翻了一遍llvm documentation發(fā)現(xiàn)有個極簡編譯器kaleidoscope的demo,于是準備擼起袖子實現(xiàn)一遍,官方demo是linux下的,我用visual
studio 2015實現(xiàn)了一遍,然后稍微把它面向?qū)ο罅艘环栴}就在這里的“稍微”,我只把詞法分析語法分析用面向?qū)ο筮M行表示,其余部分用demo里的靜態(tài)/全局變量/函數(shù)。直到LLVM IR代碼生成都是熟悉的味道熟悉的套路,但是到了chapter4添加了一個優(yōu)化器和JIT解釋器就遇到了九天神坑,首先一大堆LINK ERRORs,好在都在接受范圍內(nèi),編譯了一大堆依賴項后編譯通過了。當我輸入一個表達式"1+2"就出現(xiàn)了nullptr異常,然后我從startup開始很自然的進入parser.parserDriver();

int main() {
	llvm::InitializeNativeTarget();
	llvm::InitializeNativeTargetAsmPrinter();
	llvm::InitializeNativeTargetAsmParser();

	kaleidoscope::Parser parser;
 
	fprintf(stderr, ">> ");
	parser.getNextToken();

	theJIT = llvm::make_unique<llvm::orc::KaleidoscopeJIT>();
	initializeModuleAndPassManager();
	
	parser.parserDriver();
	return 0;
}

進入parserDriver后大概長這個樣子

/// top ::= definition | external | expression | ';'
void kaleidoscope::Parser::parserDriver() {
	while (true) {
		fprintf(stderr, ">> ");
		switch (currentToken) {
		case Token::TokenEOF:
			return;
		case ';': // ignore top-level semicolons.
			getNextToken();
			break;
		case Token::TokenDef:
			handleFunctionDefinition();
			break;
		case Token::TokenExtern:
			handleExtern();
			break;
		default:
			handleTopLevelExpression();
			break;
		}
	}
}

這里由于我輸入的是"1+2"在kaleidoscope的定義里面應(yīng)該屬于toplevelexpression,繼續(xù)跟蹤handleTopLevelExpression(),

void kaleidoscope::Parser::handleTopLevelExpression() {
	if (auto funcAST = parseTopLevelExpr()) {
		if (funcAST->codegen()) {

			//omit these codes...
		}
	}
	else {
		getNextToken();
	}
}

我也不清楚到底是解析表達式錯誤還是代碼生成,先進parserTopLevelExpr下了斷點看了一下函數(shù)正常返回,排除解析錯誤那接下來就是代碼生成

llvm::Function * FunctionAST::codegen() {
	auto & p = *(this->funcProto);
	funcPrototypeMap[this->funcProto->getFunctionName()] = std::move(this->funcProto);
	llvm::Function * theFunction = getSpecifiedFunction(p.getFunctionName());
        //omit...
}

逐語句跑了一遍發(fā)現(xiàn)是第三行引發(fā)的異常,斷點顯式getFunctionName沒有問題,那肯定就是函數(shù)問題了,繼續(xù)跟蹤getSpecifiedFunction

static llvm::Function * getSpecifiedFunction(std::string name) {
	// First, see if the function has already been added to the current module.
	if (auto *F = theModule->getFunction(name))
		return F;

	// If not, check whether we can codegen the declaration from some existing
	// prototype.
	auto FI = funcPrototypeMap.find(name);
	if (FI != funcPrototypeMap.end())
		return FI->second->codegen();

	// If no existing prototype exists, return null.
	return nullptr;
}

逐語句發(fā)現(xiàn)是對llvm::Module::getFunction的問題,getFunction在module的符號表查詢指定的函數(shù)如果不存在就返回null

Function *Module::getFunction(StringRef Name) const {
  return dyn_cast_or_null<Function>(getNamedValue(Name));
}

繼續(xù)getNamedValue()

GlobalValue *Module::getNamedValue(StringRef Name) const {
  return cast_or_null<GlobalValue>(getValueSymbolTable().lookup(Name));
}

問題就在這里了,異常顯示的this is nullptr也就是說getValueSymbolTable返回的是nullptr理所當然后面的lookup成員函數(shù)調(diào)用出錯,進入getValueSymbolTable

  /// Get the symbol table of global variable and function identifiers
  const ValueSymbolTable &getValueSymbolTable() const { return *ValSymTab; }

這是Module類的一個函數(shù),返回private ValueSymbolTable *ValSymTab;而ValSymTab在module初始化的時候會分配內(nèi)存

Module::Module(StringRef MID, LLVMContext &C)
    : Context(C), Materializer(), ModuleID(MID), SourceFileName(MID), DL("") {
  ValSymTab = new ValueSymbolTable();
  NamedMDSymTab = new StringMap<NamedMDNode *>();
  Context.addModule(this);
}

但是這里ValSymTab指向的內(nèi)存卻是沒有分配。我想應(yīng)該是堆不夠的問題,我相信我的電腦,沒有為什么,然后剩下的可能就是theModule變量出現(xiàn)了問題。縱觀整個解決方案,用到static std::unique_ptr<llvm::Module> theModule;也就initializeModuleAndPassManager(),getSpecifiedFunction(std::string name),我 先是構(gòu)跟蹤n了itializeModuleAndPassManager(),回到main,斷點顯式theJIT沒有問題(這里多說一句,在windows下KaleidoscopeJIT里面的if
(auto Sym = CompileLayer.findSymbolIn(H, Name, true))應(yīng)該改成false否則會出現(xiàn)問題,這也是個坑),進入initializeModuleAndPassManager()

int main() {
	llvm::InitializeNativeTarget();
	llvm::InitializeNativeTargetAsmPrinter();
	llvm::InitializeNativeTargetAsmParser();

	kaleidoscope::Parser parser;
 
	fprintf(stderr, ">> ");
	parser.getNextToken();

	theJIT = llvm::make_unique<llvm::orc::KaleidoscopeJIT>();
	initializeModuleAndPassManager();
	
	parser.parserDriver();
	return 0;
}

斷點顯示變量都沒問題了,都正常被賦值了,wtf?難道我之前跟蹤錯了?詭異的事情發(fā)生了,我順手給theModule加了個監(jiān)控,雖然在這個initializeModuleAndPassManager里面theModule沒有任何問題,但是進入parser.parserDriver后突然監(jiān)控顯示變量就出現(xiàn)問題了,我真是一臉懵逼,parser.parserDriver()根本沒有對theModule的操作啊,為什么無緣無故變量的值會變,我都不知道看了多少遍源碼,終于發(fā)現(xiàn)AST.h里面的theModule是按照官方demo的寫法是static變量,我隱約記得全局static變量只能在文件內(nèi)使用,而我在codegen的文件內(nèi)直接引用了它,雖然不明白為什么會過編譯但所幸發(fā)現(xiàn)了問題,去掉static后LINKERROR報錯顯示這幾個變量重定義,因為多次include
.h文件變量會多次定義,最后放到.cpp編譯通過輸入"1+2"顯示"evaluate to 3.00000"莫名感動。

回想起這慘痛的debug經(jīng)歷深感慚愧,感覺寫多了業(yè)務(wù)邏輯代碼腦子里好像少了一種思考的東西,忘記了很多基礎(chǔ),遇到問題就無腦baidu google,稍微解決不了就換庫換包, 一直順風(fēng)順水沒怎么自己努力解決過問題,沒想過有些東西換上千百次都不會變。痛定思痛,文以記之。




總結(jié)

以上是生活随笔為你收集整理的关于llvm kaleidoscope: 记一次Debug血泪之路的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。