Terminate both binary operator branches with *_end basic blocks #553

Merged
sb10q merged 1 commits from occheung/nac3:nested-binop into master 2024-11-16 12:06:23 +08:00
Member

Current Issue

Compiling boolean operations may cause panic. For example,

True and (True and True)

Boolean operation generates basic blocks a and b. The program may branch to either a or b depending of the first operand's value.
The current implementation then generate unconditional branches to the controlling basic block cont with a PHI instruction to select result, but without knowing the current location.

Hence, the following may happen:

  1. Build a conditional branch into basic block a or b.
  2. Build expression for the second operand, which involves branching to other basic block (e.g. boolean operations) and leaves a and b.
  3. Unconditional branch to cont.
  4. Build PHI instruction to select result, and references a and b as the predecessor blocks.

Patch

This PR renames existing a and b into a_begin and b_begin, and adds terminating basic blocks a_end, b_end. Generated expressions are branched to corresponding *_end basic blocks, which branches back to cont.
Therefore, the PHI instruction in cont has known predecessors.

## Current Issue Compiling boolean operations may cause panic. For example, ```Python True and (True and True) ``` Boolean operation generates basic blocks `a` and `b`. The program may branch to either `a` or `b` depending of the first operand's value. The current implementation then generate unconditional branches to the controlling basic block `cont` with a PHI instruction to select result, but without knowing the current location. Hence, the following may happen: 1. Build a conditional branch into basic block `a` or `b`. 2. Build expression for the second operand, which involves branching to other basic block (e.g. boolean operations) and leaves `a` and `b`. 3. Unconditional branch to `cont`. 4. Build PHI instruction to select result, and references `a` and `b` as the predecessor blocks. ## Patch This PR renames existing `a` and `b` into `a_begin` and `b_begin`, and adds terminating basic blocks `a_end`, `b_end`. Generated expressions are branched to corresponding `*_end` basic blocks, which branches back to `cont`. Therefore, the PHI instruction in `cont` has known predecessors.
occheung added 1 commit 2024-11-12 16:25:53 +08:00
Author
Member

The panic looks like this currently:

; ModuleID = 'main'
source_filename = "main"
target datalayout = "e-m:e-p:32:32-i64:64-n32-S128"
target triple = "riscv32-unknown-linux"

%min_artiq.Core = type { double }
%__main__.NestedBinOp = type { %min_artiq.Core* }

@"140737337638448" = global %min_artiq.Core { double 1.000000e-09 }
@"140737337638064" = global %__main__.NestedBinOp { %min_artiq.Core* @"140737337638448" }

define void @__modinit__() personality i32 (...)* @__nac3_personality !dbg !6 {
init:
  br label %body

body:                                             ; preds = %init
  call void @__main__.NestedBinOp.run.0(%__main__.NestedBinOp* @"140737337638064"), !dbg !11
  ret void, !dbg !11
}

declare i32 @__nac3_personality(...)

define void @__main__.NestedBinOp.run.0(%__main__.NestedBinOp* %0) personality i32 (...)* @__nac3_personality !dbg !12 {
init:
  %self.addr = alloca %__main__.NestedBinOp*, align 4
  store %__main__.NestedBinOp* %0, %__main__.NestedBinOp** %self.addr, align 4
  br label %body

body:                                             ; preds = %init
  br i1 true, label %a, label %b, !dbg !13

a:                                                ; preds = %body
  br i1 true, label %a1, label %b2, !dbg !14

b:                                                ; preds = %body
  br label %cont, !dbg !15

cont:                                             ; preds = %b, %cont3
  %1 = phi i8 [ %2, %a ], [ 0, %b ], !dbg !15
  ret void, !dbg !15

a1:                                               ; preds = %a
  br label %cont3, !dbg !15

b2:                                               ; preds = %a
  br label %cont3, !dbg !15

cont3:                                            ; preds = %b2, %a1
  %2 = phi i8 [ 1, %a1 ], [ 0, %b2 ], !dbg !15
  br label %cont, !dbg !15
}

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

!0 = !{i32 2, !"Debug Info Version", i32 3}
!1 = !{i32 2, !"Dwarf Version", i32 4}
!2 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: "NAC3", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
!3 = !DIFile(filename: "<nac3_synthesized_modinit>", directory: "")
!4 = distinct !DICompileUnit(language: DW_LANG_Python, file: !5, producer: "NAC3", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
!5 = !DIFile(filename: "/home/occheung/nac3/nac3artiq/demo/nested_cond.py", directory: "")
!6 = distinct !DISubprogram(name: "__modinit__", linkageName: "__modinit__", scope: null, file: !3, line: 2, type: !7, scopeLine: 2, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !10)
!7 = !DISubroutineType(flags: DIFlagPublic, types: !8)
!8 = !{!9}
!9 = !DIBasicType(name: "_", flags: DIFlagPublic)
!10 = !{}
!11 = !DILocation(line: 2, column: 13, scope: !6)
!12 = distinct !DISubprogram(name: "__main__.NestedBinOp.run.0", linkageName: "__main__.NestedBinOp.run.0", scope: null, file: !5, line: 14, type: !7, scopeLine: 14, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !4, retainedNodes: !10)
!13 = !DILocation(line: 14, column: 9, scope: !12)
!14 = !DILocation(line: 14, column: 19, scope: !12)
!15 = !DILocation(line: 14, column: 28, scope: !12)

thread 'codegen-worker-main' panicked at /home/occheung/nac3/nac3core/src/codegen/mod.rs:416:13:
PHI node entries do not match predecessors!
  %1 = phi i8 [ %2, %a ], [ 0, %b ], !dbg !15
label %b
label %cont3
Instruction does not dominate all uses!
  %2 = phi i8 [ 1, %a1 ], [ 0, %b2 ], !dbg !15
  %1 = phi i8 [ %2, %a ], [ 0, %b ], !dbg !15
The panic looks like this currently: ``` ; ModuleID = 'main' source_filename = "main" target datalayout = "e-m:e-p:32:32-i64:64-n32-S128" target triple = "riscv32-unknown-linux" %min_artiq.Core = type { double } %__main__.NestedBinOp = type { %min_artiq.Core* } @"140737337638448" = global %min_artiq.Core { double 1.000000e-09 } @"140737337638064" = global %__main__.NestedBinOp { %min_artiq.Core* @"140737337638448" } define void @__modinit__() personality i32 (...)* @__nac3_personality !dbg !6 { init: br label %body body: ; preds = %init call void @__main__.NestedBinOp.run.0(%__main__.NestedBinOp* @"140737337638064"), !dbg !11 ret void, !dbg !11 } declare i32 @__nac3_personality(...) define void @__main__.NestedBinOp.run.0(%__main__.NestedBinOp* %0) personality i32 (...)* @__nac3_personality !dbg !12 { init: %self.addr = alloca %__main__.NestedBinOp*, align 4 store %__main__.NestedBinOp* %0, %__main__.NestedBinOp** %self.addr, align 4 br label %body body: ; preds = %init br i1 true, label %a, label %b, !dbg !13 a: ; preds = %body br i1 true, label %a1, label %b2, !dbg !14 b: ; preds = %body br label %cont, !dbg !15 cont: ; preds = %b, %cont3 %1 = phi i8 [ %2, %a ], [ 0, %b ], !dbg !15 ret void, !dbg !15 a1: ; preds = %a br label %cont3, !dbg !15 b2: ; preds = %a br label %cont3, !dbg !15 cont3: ; preds = %b2, %a1 %2 = phi i8 [ 1, %a1 ], [ 0, %b2 ], !dbg !15 br label %cont, !dbg !15 } !llvm.module.flags = !{!0, !1} !llvm.dbg.cu = !{!2, !4} !0 = !{i32 2, !"Debug Info Version", i32 3} !1 = !{i32 2, !"Dwarf Version", i32 4} !2 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: "NAC3", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug) !3 = !DIFile(filename: "<nac3_synthesized_modinit>", directory: "") !4 = distinct !DICompileUnit(language: DW_LANG_Python, file: !5, producer: "NAC3", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug) !5 = !DIFile(filename: "/home/occheung/nac3/nac3artiq/demo/nested_cond.py", directory: "") !6 = distinct !DISubprogram(name: "__modinit__", linkageName: "__modinit__", scope: null, file: !3, line: 2, type: !7, scopeLine: 2, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !10) !7 = !DISubroutineType(flags: DIFlagPublic, types: !8) !8 = !{!9} !9 = !DIBasicType(name: "_", flags: DIFlagPublic) !10 = !{} !11 = !DILocation(line: 2, column: 13, scope: !6) !12 = distinct !DISubprogram(name: "__main__.NestedBinOp.run.0", linkageName: "__main__.NestedBinOp.run.0", scope: null, file: !5, line: 14, type: !7, scopeLine: 14, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !4, retainedNodes: !10) !13 = !DILocation(line: 14, column: 9, scope: !12) !14 = !DILocation(line: 14, column: 19, scope: !12) !15 = !DILocation(line: 14, column: 28, scope: !12) thread 'codegen-worker-main' panicked at /home/occheung/nac3/nac3core/src/codegen/mod.rs:416:13: PHI node entries do not match predecessors! %1 = phi i8 [ %2, %a ], [ 0, %b ], !dbg !15 label %b label %cont3 Instruction does not dominate all uses! %2 = phi i8 [ 1, %a1 ], [ 0, %b2 ], !dbg !15 %1 = phi i8 [ %2, %a ], [ 0, %b ], !dbg !15 ```
sb10q merged commit 2bd3f63991 into master 2024-11-16 12:06:23 +08:00
Sign in to join this conversation.
No reviewers
No Milestone
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: M-Labs/nac3#553
No description provided.