(Complete newbie here!) The following code has been generated by a toy BF compiler that passes all tests in LLVMJIT and compilation when no optimizations have been taken. It should print the character ‘0’, compiled with
llc out.ll -o out.asm; clang out.asm -o out; ./out
; ModuleID = 'out.ll'
source_filename = "stdin"
define void @program(i8* %0) {
entry:
%index = alloca i32, align 4
%index.0 = load i32, i32* %index, align 4
%incremented = add i32 %index.0, 1
store i32 %incremented, i32* %index, align 4
%index.01 = load i32, i32* %index, align 4
%sep = getelementptr inbounds i8, i8* %0, i32 %index.01
%1 = load i8, i8* %sep, align 1
%2 = add i8 %1, 1
store i8 %2, i8* %sep, align 1
%index.02 = load i32, i32* %index, align 4
%sep3 = getelementptr inbounds i8, i8* %0, i32 %index.02
%3 = load i8, i8* %sep3, align 1
%4 = add i8 %3, 1
store i8 %4, i8* %sep3, align 1
%index.04 = load i32, i32* %index, align 4
%sep5 = getelementptr inbounds i8, i8* %0, i32 %index.04
%5 = load i8, i8* %sep5, align 1
%6 = add i8 %5, 1
store i8 %6, i8* %sep5, align 1
%index.06 = load i32, i32* %index, align 4
%sep7 = getelementptr inbounds i8, i8* %0, i32 %index.06
%7 = load i8, i8* %sep7, align 1
%8 = add i8 %7, 1
store i8 %8, i8* %sep7, align 1
%index.08 = load i32, i32* %index, align 4
%sep9 = getelementptr inbounds i8, i8* %0, i32 %index.08
%9 = load i8, i8* %sep9, align 1
%10 = add i8 %9, 1
store i8 %10, i8* %sep9, align 1
%index.010 = load i32, i32* %index, align 4
%sep11 = getelementptr inbounds i8, i8* %0, i32 %index.010
%11 = load i8, i8* %sep11, align 1
%12 = add i8 %11, 1
store i8 %12, i8* %sep11, align 1
br label %cond0
cond0: ; preds = %block0, %entry
%offset = load i32, i32* %index, align 4
%ptr = getelementptr inbounds i8, i8* %0, i32 %offset
%cell = load i8, i8* %ptr, align 1
%13 = icmp eq i8 %cell, 0
br i1 %13, label %after0, label %block0
block0: ; preds = %cond0
%index.012 = load i32, i32* %index, align 4
%decremented = sub i32 %index.012, 1
store i32 %decremented, i32* %index, align 4
%index.013 = load i32, i32* %index, align 4
%sep14 = getelementptr inbounds i8, i8* %0, i32 %index.013
%14 = load i8, i8* %sep14, align 1
%15 = add i8 %14, 1
store i8 %15, i8* %sep14, align 1
%index.015 = load i32, i32* %index, align 4
%sep16 = getelementptr inbounds i8, i8* %0, i32 %index.015
%16 = load i8, i8* %sep16, align 1
%17 = add i8 %16, 1
store i8 %17, i8* %sep16, align 1
%index.017 = load i32, i32* %index, align 4
%sep18 = getelementptr inbounds i8, i8* %0, i32 %index.017
%18 = load i8, i8* %sep18, align 1
%19 = add i8 %18, 1
store i8 %19, i8* %sep18, align 1
%index.019 = load i32, i32* %index, align 4
%sep20 = getelementptr inbounds i8, i8* %0, i32 %index.019
%20 = load i8, i8* %sep20, align 1
%21 = add i8 %20, 1
store i8 %21, i8* %sep20, align 1
%index.021 = load i32, i32* %index, align 4
%sep22 = getelementptr inbounds i8, i8* %0, i32 %index.021
%22 = load i8, i8* %sep22, align 1
%23 = add i8 %22, 1
store i8 %23, i8* %sep22, align 1
%index.023 = load i32, i32* %index, align 4
%sep24 = getelementptr inbounds i8, i8* %0, i32 %index.023
%24 = load i8, i8* %sep24, align 1
%25 = add i8 %24, 1
store i8 %25, i8* %sep24, align 1
%index.025 = load i32, i32* %index, align 4
%sep26 = getelementptr inbounds i8, i8* %0, i32 %index.025
%26 = load i8, i8* %sep26, align 1
%27 = add i8 %26, 1
store i8 %27, i8* %sep26, align 1
%index.027 = load i32, i32* %index, align 4
%sep28 = getelementptr inbounds i8, i8* %0, i32 %index.027
%28 = load i8, i8* %sep28, align 1
%29 = add i8 %28, 1
store i8 %29, i8* %sep28, align 1
%index.029 = load i32, i32* %index, align 4
%incremented30 = add i32 %index.029, 1
store i32 %incremented30, i32* %index, align 4
%index.031 = load i32, i32* %index, align 4
%sep32 = getelementptr inbounds i8, i8* %0, i32 %index.031
%cell33 = load i8, i8* %sep32, align 1
%dec_cell = sub i8 %cell33, 1
store i8 %dec_cell, i8* %sep32, align 1
br label %cond0
after0: ; preds = %cond0
%index.034 = load i32, i32* %index, align 4
%decremented35 = sub i32 %index.034, 1
store i32 %decremented35, i32* %index, align 4
%index.036 = load i32, i32* %index, align 4
%sep37 = getelementptr inbounds i8, i8* %0, i32 %index.036
%cell38 = load i8, i8* %sep37, align 1
%cast = sext i8 %cell38 to i32
%print = call i32 @putchar(i32 %cast)
ret void
}
; Function Attrs: nofree nounwind
declare noundef i32 @putchar(i32 noundef) #0
; Function Attrs: nofree nounwind
declare noundef i32 @getchar() #0
define i32 @main() {
entry:
%0 = trunc i64 100000 to i32
%mallocsize = mul i32 %0, ptrtoint (i8* getelementptr (i8, i8* null, i32 1) to i32)
%stack = tail call i8* @malloc(i32 %mallocsize)
call void @llvm.memset.p0i8.i64(i8* align 1 %stack, i8 0, i64 100000, i1 false)
call void @program(i8* %stack)
ret i32 0
}
; Function Attrs: inaccessiblememonly mustprogress nofree nounwind willreturn
declare noalias noundef i8* @malloc(i32 noundef) #1
; Function Attrs: argmemonly mustprogress nofree nounwind willreturn writeonly
declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #2
attributes #0 = { nofree nounwind }
attributes #1 = { inaccessiblememonly mustprogress nofree nounwind willreturn }
attributes #2 = { argmemonly mustprogress nofree nounwind willreturn writeonly }
I then tried to optimize with
opt out.ll --O1 -S -o opt.ll
(and eventually -opt-bisect-limit=N)
But the function program
breaks from SROAPass. The last valid bisection (5) for me is the following:
; ModuleID = 'out.ll'
source_filename = "stdin"
define void @program(i8* %0) {
entry:
%index = alloca i32, align 4
%index.0 = load i32, i32* %index, align 4
%incremented = add i32 %index.0, 1
store i32 %incremented, i32* %index, align 4
%index.01 = load i32, i32* %index, align 4
%sep = getelementptr inbounds i8, i8* %0, i32 %index.01
%1 = load i8, i8* %sep, align 1
%2 = add i8 %1, 1
store i8 %2, i8* %sep, align 1
%index.02 = load i32, i32* %index, align 4
%sep3 = getelementptr inbounds i8, i8* %0, i32 %index.02
%3 = load i8, i8* %sep3, align 1
%4 = add i8 %3, 1
store i8 %4, i8* %sep3, align 1
%index.04 = load i32, i32* %index, align 4
%sep5 = getelementptr inbounds i8, i8* %0, i32 %index.04
%5 = load i8, i8* %sep5, align 1
%6 = add i8 %5, 1
store i8 %6, i8* %sep5, align 1
%index.06 = load i32, i32* %index, align 4
%sep7 = getelementptr inbounds i8, i8* %0, i32 %index.06
%7 = load i8, i8* %sep7, align 1
%8 = add i8 %7, 1
store i8 %8, i8* %sep7, align 1
%index.08 = load i32, i32* %index, align 4
%sep9 = getelementptr inbounds i8, i8* %0, i32 %index.08
%9 = load i8, i8* %sep9, align 1
%10 = add i8 %9, 1
store i8 %10, i8* %sep9, align 1
%index.010 = load i32, i32* %index, align 4
%sep11 = getelementptr inbounds i8, i8* %0, i32 %index.010
%11 = load i8, i8* %sep11, align 1
%12 = add i8 %11, 1
store i8 %12, i8* %sep11, align 1
br label %cond0
cond0: ; preds = %block0, %entry
%offset = load i32, i32* %index, align 4
%ptr = getelementptr inbounds i8, i8* %0, i32 %offset
%cell = load i8, i8* %ptr, align 1
%13 = icmp eq i8 %cell, 0
br i1 %13, label %after0, label %block0
block0: ; preds = %cond0
%index.012 = load i32, i32* %index, align 4
%decremented = sub i32 %index.012, 1
store i32 %decremented, i32* %index, align 4
%index.013 = load i32, i32* %index, align 4
%sep14 = getelementptr inbounds i8, i8* %0, i32 %index.013
%14 = load i8, i8* %sep14, align 1
%15 = add i8 %14, 1
store i8 %15, i8* %sep14, align 1
%index.015 = load i32, i32* %index, align 4
%sep16 = getelementptr inbounds i8, i8* %0, i32 %index.015
%16 = load i8, i8* %sep16, align 1
%17 = add i8 %16, 1
store i8 %17, i8* %sep16, align 1
%index.017 = load i32, i32* %index, align 4
%sep18 = getelementptr inbounds i8, i8* %0, i32 %index.017
%18 = load i8, i8* %sep18, align 1
%19 = add i8 %18, 1
store i8 %19, i8* %sep18, align 1
%index.019 = load i32, i32* %index, align 4
%sep20 = getelementptr inbounds i8, i8* %0, i32 %index.019
%20 = load i8, i8* %sep20, align 1
%21 = add i8 %20, 1
store i8 %21, i8* %sep20, align 1
%index.021 = load i32, i32* %index, align 4
%sep22 = getelementptr inbounds i8, i8* %0, i32 %index.021
%22 = load i8, i8* %sep22, align 1
%23 = add i8 %22, 1
store i8 %23, i8* %sep22, align 1
%index.023 = load i32, i32* %index, align 4
%sep24 = getelementptr inbounds i8, i8* %0, i32 %index.023
%24 = load i8, i8* %sep24, align 1
%25 = add i8 %24, 1
store i8 %25, i8* %sep24, align 1
%index.025 = load i32, i32* %index, align 4
%sep26 = getelementptr inbounds i8, i8* %0, i32 %index.025
%26 = load i8, i8* %sep26, align 1
%27 = add i8 %26, 1
store i8 %27, i8* %sep26, align 1
%index.027 = load i32, i32* %index, align 4
%sep28 = getelementptr inbounds i8, i8* %0, i32 %index.027
%28 = load i8, i8* %sep28, align 1
%29 = add i8 %28, 1
store i8 %29, i8* %sep28, align 1
%index.029 = load i32, i32* %index, align 4
%incremented30 = add i32 %index.029, 1
store i32 %incremented30, i32* %index, align 4
%index.031 = load i32, i32* %index, align 4
%sep32 = getelementptr inbounds i8, i8* %0, i32 %index.031
%cell33 = load i8, i8* %sep32, align 1
%dec_cell = sub i8 %cell33, 1
store i8 %dec_cell, i8* %sep32, align 1
br label %cond0
after0: ; preds = %cond0
%index.034 = load i32, i32* %index, align 4
%decremented35 = sub i32 %index.034, 1
store i32 %decremented35, i32* %index, align 4
%index.036 = load i32, i32* %index, align 4
%sep37 = getelementptr inbounds i8, i8* %0, i32 %index.036
%cell38 = load i8, i8* %sep37, align 1
%cast = sext i8 %cell38 to i32
%print = call i32 @putchar(i32 %cast)
ret void
}
; Function Attrs: nofree nounwind
declare noundef i32 @putchar(i32 noundef) #0
; Function Attrs: nofree nounwind
declare noundef i32 @getchar() #0
define i32 @main() {
entry:
%0 = trunc i64 100000 to i32
%mallocsize = mul i32 %0, ptrtoint (i8* getelementptr (i8, i8* null, i32 1) to i32)
%stack = tail call i8* @malloc(i32 %mallocsize)
call void @llvm.memset.p0i8.i64(i8* align 1 %stack, i8 0, i64 100000, i1 false)
call void @program(i8* %stack)
ret i32 0
}
; Function Attrs: inaccessiblememonly mustprogress nofree nounwind willreturn
declare noalias noundef i8* @malloc(i32 noundef) #1
; Function Attrs: argmemonly mustprogress nofree nounwind willreturn writeonly
declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #2
attributes #0 = { nofree nounwind }
attributes #1 = { inaccessiblememonly mustprogress nofree nounwind willreturn }
attributes #2 = { argmemonly mustprogress nofree nounwind willreturn writeonly }
after which SROA (6) removes the alloca generating this module:
; ModuleID = 'out.ll'
source_filename = "stdin"
define void @program(i8* %0) {
entry:
%incremented = add i32 undef, 1
%sep = getelementptr inbounds i8, i8* %0, i32 %incremented
%1 = load i8, i8* %sep, align 1
%2 = add i8 %1, 1
store i8 %2, i8* %sep, align 1
%sep3 = getelementptr inbounds i8, i8* %0, i32 %incremented
%3 = load i8, i8* %sep3, align 1
%4 = add i8 %3, 1
store i8 %4, i8* %sep3, align 1
%sep5 = getelementptr inbounds i8, i8* %0, i32 %incremented
%5 = load i8, i8* %sep5, align 1
%6 = add i8 %5, 1
store i8 %6, i8* %sep5, align 1
%sep7 = getelementptr inbounds i8, i8* %0, i32 %incremented
%7 = load i8, i8* %sep7, align 1
%8 = add i8 %7, 1
store i8 %8, i8* %sep7, align 1
%sep9 = getelementptr inbounds i8, i8* %0, i32 %incremented
%9 = load i8, i8* %sep9, align 1
%10 = add i8 %9, 1
store i8 %10, i8* %sep9, align 1
%sep11 = getelementptr inbounds i8, i8* %0, i32 %incremented
%11 = load i8, i8* %sep11, align 1
%12 = add i8 %11, 1
store i8 %12, i8* %sep11, align 1
br label %cond0
cond0: ; preds = %block0, %entry
%index.0 = phi i32 [ %incremented, %entry ], [ %incremented30, %block0 ]
%ptr = getelementptr inbounds i8, i8* %0, i32 %index.0
%cell = load i8, i8* %ptr, align 1
%13 = icmp eq i8 %cell, 0
br i1 %13, label %after0, label %block0
block0: ; preds = %cond0
%decremented = sub i32 %index.0, 1
%sep14 = getelementptr inbounds i8, i8* %0, i32 %decremented
%14 = load i8, i8* %sep14, align 1
%15 = add i8 %14, 1
store i8 %15, i8* %sep14, align 1
%sep16 = getelementptr inbounds i8, i8* %0, i32 %decremented
%16 = load i8, i8* %sep16, align 1
%17 = add i8 %16, 1
store i8 %17, i8* %sep16, align 1
%sep18 = getelementptr inbounds i8, i8* %0, i32 %decremented
%18 = load i8, i8* %sep18, align 1
%19 = add i8 %18, 1
store i8 %19, i8* %sep18, align 1
%sep20 = getelementptr inbounds i8, i8* %0, i32 %decremented
%20 = load i8, i8* %sep20, align 1
%21 = add i8 %20, 1
store i8 %21, i8* %sep20, align 1
%sep22 = getelementptr inbounds i8, i8* %0, i32 %decremented
%22 = load i8, i8* %sep22, align 1
%23 = add i8 %22, 1
store i8 %23, i8* %sep22, align 1
%sep24 = getelementptr inbounds i8, i8* %0, i32 %decremented
%24 = load i8, i8* %sep24, align 1
%25 = add i8 %24, 1
store i8 %25, i8* %sep24, align 1
%sep26 = getelementptr inbounds i8, i8* %0, i32 %decremented
%26 = load i8, i8* %sep26, align 1
%27 = add i8 %26, 1
store i8 %27, i8* %sep26, align 1
%sep28 = getelementptr inbounds i8, i8* %0, i32 %decremented
%28 = load i8, i8* %sep28, align 1
%29 = add i8 %28, 1
store i8 %29, i8* %sep28, align 1
%incremented30 = add i32 %decremented, 1
%sep32 = getelementptr inbounds i8, i8* %0, i32 %incremented30
%cell33 = load i8, i8* %sep32, align 1
%dec_cell = sub i8 %cell33, 1
store i8 %dec_cell, i8* %sep32, align 1
br label %cond0
after0: ; preds = %cond0
%decremented35 = sub i32 %index.0, 1
%sep37 = getelementptr inbounds i8, i8* %0, i32 %decremented35
%cell38 = load i8, i8* %sep37, align 1
%cast = sext i8 %cell38 to i32
%print = call i32 @putchar(i32 %cast)
ret void
}
; Function Attrs: nofree nounwind
declare noundef i32 @putchar(i32 noundef) #0
; Function Attrs: nofree nounwind
declare noundef i32 @getchar() #0
define i32 @main() {
entry:
%0 = trunc i64 100000 to i32
%mallocsize = mul i32 %0, ptrtoint (i8* getelementptr (i8, i8* null, i32 1) to i32)
%stack = tail call i8* @malloc(i32 %mallocsize)
call void @llvm.memset.p0i8.i64(i8* align 1 %stack, i8 0, i64 100000, i1 false)
call void @program(i8* %stack)
ret i32 0
}
; Function Attrs: inaccessiblememonly mustprogress nofree nounwind willreturn
declare noalias noundef i8* @malloc(i32 noundef) #1
; Function Attrs: argmemonly mustprogress nofree nounwind willreturn writeonly
declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #2
attributes #0 = { nofree nounwind }
attributes #1 = { inaccessiblememonly mustprogress nofree nounwind willreturn }
attributes #2 = { argmemonly mustprogress nofree nounwind willreturn writeonly }
which segfaults when compiled.
Successive “optimizations” then reduce the whole program to this nonsense:
; ModuleID = 'out.ll'
source_filename = "stdin"
; Function Attrs: nofree nounwind
define void @program(i8* nocapture %0) local_unnamed_addr #0 {
entry:
store i8 0, i8* %0, align 1
%print = call i32 @putchar(i32 0)
ret void
}
; Function Attrs: nofree nounwind
declare noundef i32 @putchar(i32 noundef) local_unnamed_addr #0
; Function Attrs: nofree nounwind
define i32 @main() local_unnamed_addr #0 {
entry:
%print.i = call i32 @putchar(i32 0) #1
ret i32 0
}
attributes #0 = { nofree nounwind }
attributes #1 = { nounwind }
Could somebody explain to me what I did wrong here?
(EDIT: Apropos I’m using Rust + Inkwell to generate the code. The base algorithm should be correct in theory, as even large Brain**** programs (such as rendering a Mandelbrot set) work, so it shouldn’t be a logic error. Ergo I’m assuming I violated some SSA invariant?)