Kaleidoscope 语言: Adding Debug Information

在本章节中,我们为语言增加 Debug 信息。

去掉提示词 #

第一步我们先去掉所有的提示输入,这样就完全是一个输入输出的二进制程序了。

@@ -1129,7 +1129,6 @@ static void HandleTopLevelExpression() {
 /// top ::= definition | external | expression | ';'
 static void MainLoop() {
   while (1) {
-    fprintf(stderr, "ready> ");
     switch (CurTok) {
     case tok_eof:
       return;
@@ -1184,7 +1183,6 @@ int main() {
   BinopPrecedence['*'] = 40; // highest.

   // Prime the first token.
-  fprintf(stderr, "ready> ");
   getNextToken();

禁用 JIT 和 优化相关 #

@@ -1108,17 +1108,8 @@ static void HandleExtern() {
 static void HandleTopLevelExpression() {
   // Evaluate a top-level expression into an anonymous function.
   if (auto FnAST = ParseTopLevelExpr()) {
-    if (auto *FnIR = FnAST->codegen()) {
-      // We're just doing this to make sure it executes.
-      TheExecutionEngine->finalizeObject();
-      // JIT the function, returning a function pointer.
-      void *FPtr = TheExecutionEngine->getPointerToFunction(FnIR);
-
-      // Cast it to the right type (takes no arguments, returns a double) so we
-      // can call it as a native function.
-      double (*FP)() = (double (*)())(intptr_t)FPtr;
-      // Ignore the return value for this.
-      (void)FP;
+    if (!F->codegen()) {
+      fprintf(stderr, "Error generating code for top level expr");
     }
   } else {
     // Skip token for error recovery.
@@ -1439,11 +1459,11 @@ int main() {
   // target lays out data structures.
   TheModule->setDataLayout(TheExecutionEngine->getDataLayout());
   OurFPM.add(new DataLayoutPass());
+#if 0
   OurFPM.add(createBasicAliasAnalysisPass());
   // Promote allocas to registers.
   OurFPM.add(createPromoteMemoryToRegisterPass());
@@ -1218,7 +1210,7 @@ int main() {
   OurFPM.add(createGVNPass());
   // Simplify the control flow graph (deleting unreachable blocks, etc).
   OurFPM.add(createCFGSimplificationPass());
-
+  #endif
   OurFPM.doInitialization();

   // Set the global so the code gen can use this.

DIBuilder #

llvm 使用 BIBuilder 来构建 Debug 信息

DBuilder = new DIBuilder(*TheModule);

KSDbgInfo.TheCU = DBuilder->createCompileUnit(
    dwarf::DW_LANG_C, DBuilder->createFile("fib.ks", "."),
    "Kaleidoscope Compiler", 0, "", 0);

......


DBuilder->finalize(); // 最终使用 finalize 输出 LR

Source Location #

在原来的逻辑上中,我们直接使用 char 来读取,现在我们需要多记录下当前的位置

struct SourceLocation {
  int Line;
  int Col;
};
static SourceLocation CurLoc;
static SourceLocation LexLoc = {1, 0};

static int advance() {
  int LastChar = getchar();

  if (LastChar == '\n' || LastChar == '\r') {
    LexLoc.Line++;
    LexLoc.Col = 0;
  } else
    LexLoc.Col++;
  return LastChar;
}

ExprAST #

修改 ExprAST 基类,增加 getLinegetCol

class ExprAST {
    SourceLocation Loc;

public:
    ExprAST(SourceLocation Loc = CurLoc) : Loc(Loc) {}
    virtual ~ExprAST() = default;
    virtual llvm::Value* codegen() = 0;

    int getLine() const {
        return Loc.Line;
    }

    int getCol() const {
        return Loc.Col;
    }

    virtual llvm::raw_ostream& dump(llvm::raw_ostream& out, int ind) {
        return out << ':' << getLine() << ':' << getCol() << '\n';
    }
};

inline llvm::raw_ostream& indent(llvm::raw_ostream& O, int size) {
    return O << std::string(size, ' ');
}

DebugInfo #

新增 DebugInfo

inline struct DebugInfo {
    llvm::DICompileUnit* TheCU;
    llvm::DIType* DblTy;
    std::vector<llvm::DIScope*> LexicalBlocks;
    void emitLocation(ExprAST* AST);
    llvm::DIType* getDoubleTy();
} KSDbgInfo;

增加一些基本函数

void DebugInfo::emitLocation(ExprAST* AST) {
    if (!AST)
        return Builder->SetCurrentDebugLocation(llvm::DebugLoc());
    llvm::DIScope* Scope;
    if (LexicalBlocks.empty())
        Scope = TheCU;
    else
        Scope = LexicalBlocks.back();
    Builder->SetCurrentDebugLocation(
        llvm::DILocation::get(
            Scope->getContext(),
            AST->getLine(),
            AST->getCol(),
            Scope));
}

最终效果 #

$ ./cmake-build-debug/kaleidoscope < fib.ks 
; ModuleID = 'My awesome JIT'
source_filename = "My awesome JIT"

define double @fib(double %x) !dbg !4 {
entry:
  %x1 = alloca double, align 8
  call void @llvm.dbg.declare(metadata double* %x1, metadata !9, metadata !DIExpression()), !dbg !10
  store double %x, double* %x1, align 8
  %x2 = load double, double* %x1, align 8, !dbg !11
  %cmptmp = fcmp ult double %x2, 3.000000e+00, !dbg !12
  %booltmp = uitofp i1 %cmptmp to double, !dbg !12
  %ifcond = fcmp one double %booltmp, 0.000000e+00, !dbg !12
  br i1 %ifcond, label %then, label %else, !dbg !12

then:                                             ; preds = %entry
  br label %ifcont, !dbg !13

else:                                             ; preds = %entry
  %x3 = load double, double* %x1, align 8, !dbg !14
  %subtmp = fsub double %x3, 1.000000e+00, !dbg !15
  %calltmp = call double @fib(double %subtmp), !dbg !15
  %x4 = load double, double* %x1, align 8, !dbg !16
  %subtmp5 = fsub double %x4, 2.000000e+00, !dbg !17
  %calltmp6 = call double @fib(double %subtmp5), !dbg !17
  %addtmp = fadd double %calltmp, %calltmp6, !dbg !17
  br label %ifcont, !dbg !17

ifcont:                                           ; preds = %else, %then
  %iftmp = phi double [ 1.000000e+00, %then ], [ %addtmp, %else ], !dbg !17
  ret double %iftmp, !dbg !17
}

; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
declare void @llvm.dbg.declare(metadata, metadata, metadata) #0

define double @main() !dbg !18 {
entry:
  %calltmp = call double @fib(double 1.000000e+01), !dbg !22
  ret double %calltmp, !dbg !22
}

attributes #0 = { nofree nosync nounwind readnone speculatable willreturn }

!llvm.dbg.cu = !{!0, !2}
!llvm.module.flags = !{!3}

!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "Kaleidoscope Compiler", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
!1 = !DIFile(filename: "fib.ks", directory: ".")
!2 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "Kaleidoscope Compiler", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
!3 = !{i32 2, !"Debug Info Version", i32 3}
!4 = distinct !DISubprogram(name: "fib", scope: !1, file: !1, line: 1, type: !5, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !8)
!5 = !DISubroutineType(types: !6)
!6 = !{!7, !7}
!7 = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float)
!8 = <temporary!> !{}
!9 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !1, line: 1, type: !7)
!10 = !DILocation(line: 1, scope: !4)
!11 = !DILocation(line: 2, column: 4, scope: !4)
!12 = !DILocation(line: 2, column: 7, scope: !4)
!13 = !DILocation(line: 2, column: 14, scope: !4)
!14 = !DILocation(line: 3, column: 9, scope: !4)
!15 = !DILocation(line: 3, column: 10, scope: !4)
!16 = !DILocation(line: 3, column: 14, scope: !4)
!17 = !DILocation(line: 3, column: 15, scope: !4)
!18 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 5, type: !19, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !21)
!19 = !DISubroutineType(types: !20)
!20 = !{!7}
!21 = <temporary!> !{}
!22 = !DILocation(line: 5, column: 2, scope: !18)

这里生成的就是 LR 然后再通过 LLC 编译成二进制即可。

Debug #

采用 lldb 直接对生成的二进制进行断点追踪即可。

comments powered by Disqus