marmot playground
Run arrives with marmot’s wasm build. The stages are real.
Sum loop: The loop from the home page: watch it become vector code.
Diagnostics
marmot build: no errors.
MLIR, with the go dialect
Go operations (go.*) still say what Go means. 54 lines
llvm.func @main.Sum(%a0: !llvm.struct<(ptr, i64, i64)>) -> i32 attributes {no_unwind, passthrough = [["frame-pointer", "non-leaf"]]} {
%v3 = llvm.extractvalue %a0[1] : !llvm.struct<(ptr, i64, i64)>
%v4 = llvm.mlir.constant(0 : i32) : i32
%v5 = llvm.mlir.constant(-1 : i64) : i64
llvm.br ^bb1(%v4, %v5 : i32, i64)
^bb1(%v1: i32, %v2: i64):
%v6 = llvm.mlir.constant(1 : i64) : i64
%v7 = llvm.add %v2, %v6 : i64
%v8 = llvm.icmp "slt" %v7, %v3 : i64
llvm.cond_br %v8, ^bb2, ^bb3
^bb2:
%v9 = llvm.extractvalue %a0[0] : !llvm.struct<(ptr, i64, i64)>
%v10 = llvm.extractvalue %a0[1] : !llvm.struct<(ptr, i64, i64)>
%v11 = go.index %v9[%v7] len %v10, i32 : i64
%v12 = llvm.load %v11 {alignment = 4 : i64, go.unsafe} : !llvm.ptr -> i32
%v13 = llvm.add %v1, %v12 : i32
llvm.br ^bb1(%v13, %v7 : i32, i64)
^bb3:
llvm.return %v1 : i32
}
llvm.func @main.main() attributes {no_unwind, passthrough = [["frame-pointer", "non-leaf"]]} {
%v1 = go.new @"type:[4]int32", !llvm.array<4 x i32>
%v2 = llvm.mlir.constant(4 : i64) : i64
%v3 = llvm.mlir.constant(0 : i64) : i64
%v4 = llvm.getelementptr inbounds %v1[%v3] : (!llvm.ptr, i64) -> !llvm.ptr, i32
%v5 = llvm.mlir.constant(1 : i32) : i32
llvm.store %v5, %v4 {alignment = 4 : i64, go.unsafe} : i32, !llvm.ptr
%v6 = llvm.mlir.constant(4 : i64) : i64
%v7 = llvm.mlir.constant(1 : i64) : i64
%v8 = llvm.getelementptr inbounds %v1[%v7] : (!llvm.ptr, i64) -> !llvm.ptr, i32
%v9 = llvm.mlir.constant(2 : i32) : i32
llvm.store %v9, %v8 {alignment = 4 : i64, go.unsafe} : i32, !llvm.ptr
%v10 = llvm.mlir.constant(4 : i64) : i64
%v11 = llvm.mlir.constant(2 : i64) : i64
%v12 = llvm.getelementptr inbounds %v1[%v11] : (!llvm.ptr, i64) -> !llvm.ptr, i32
%v13 = llvm.mlir.constant(3 : i32) : i32
llvm.store %v13, %v12 {alignment = 4 : i64, go.unsafe} : i32, !llvm.ptr
%v14 = llvm.mlir.constant(4 : i64) : i64
%v15 = llvm.mlir.constant(3 : i64) : i64
%v16 = llvm.getelementptr inbounds %v1[%v15] : (!llvm.ptr, i64) -> !llvm.ptr, i32
%v17 = llvm.mlir.constant(4 : i32) : i32
llvm.store %v17, %v16 {alignment = 4 : i64, go.unsafe} : i32, !llvm.ptr
%v18 = llvm.mlir.constant(4 : i64) : i64
%v19 = llvm.mlir.constant(0 : i64) : i64
%v20 = go.slice %v1[%v19 : %v18] cap %v18, i32 len_bound unsigned : i64 -> !llvm.struct<(ptr, i64, i64)>
%v21 = llvm.call @main.Sum(%v20) : (!llvm.struct<(ptr, i64, i64)>) -> i32
llvm.call @runtime.printlock() : () -> ()
%v22 = llvm.sext %v21 : i32 to i64
llvm.call @runtime.printint(%v22) : (i64) -> ()
llvm.call @runtime.printnl() : () -> ()
llvm.call @runtime.printunlock() : () -> ()
llvm.return
}LLVM IR, -O 2
Vector types: a loop works on several values at a time. 104 lines
define i32 @main.Sum({ ptr, i64, i64 } %0) local_unnamed_addr #1 !dbg !20 {
%2 = extractvalue { ptr, i64, i64 } %0, 1
%3 = icmp sgt i64 %2, 0
br i1 %3, label %iter.check, label %._crit_edge
iter.check: ; preds = %1
%4 = extractvalue { ptr, i64, i64 } %0, 0
%min.iters.check = icmp ult i64 %2, 4
br i1 %min.iters.check, label %vec.epilog.scalar.ph.preheader, label %vector.main.loop.iter.check
vector.main.loop.iter.check: ; preds = %iter.check
%min.iters.check8 = icmp ult i64 %2, 16
br i1 %min.iters.check8, label %vec.epilog.ph, label %vector.ph
vector.ph: ; preds = %vector.main.loop.iter.check
%n.mod.vf = and i64 %2, 12
%n.vec = and i64 %2, 9223372036854775792
br label %vector.body
vector.body: ; preds = %vector.body, %vector.ph
%index = phi i64 [ 0, %vector.ph ], [ %index.next, %vector.body ]
%vec.phi = phi <4 x i32> [ zeroinitializer, %vector.ph ], [ %9, %vector.body ]
%vec.phi9 = phi <4 x i32> [ zeroinitializer, %vector.ph ], [ %10, %vector.body ]
%vec.phi10 = phi <4 x i32> [ zeroinitializer, %vector.ph ], [ %11, %vector.body ]
%vec.phi11 = phi <4 x i32> [ zeroinitializer, %vector.ph ], [ %12, %vector.body ]
%5 = getelementptr inbounds nuw i32, ptr %4, i64 %index
%6 = getelementptr inbounds nuw i8, ptr %5, i64 16
%7 = getelementptr inbounds nuw i8, ptr %5, i64 32
%8 = getelementptr inbounds nuw i8, ptr %5, i64 48
%wide.load = load <4 x i32>, ptr %5, align 4
%wide.load12 = load <4 x i32>, ptr %6, align 4
%wide.load13 = load <4 x i32>, ptr %7, align 4
%wide.load14 = load <4 x i32>, ptr %8, align 4
%9 = add <4 x i32> %wide.load, %vec.phi
%10 = add <4 x i32> %wide.load12, %vec.phi9
%11 = add <4 x i32> %wide.load13, %vec.phi10
%12 = add <4 x i32> %wide.load14, %vec.phi11
%index.next = add nuw i64 %index, 16
%13 = icmp eq i64 %index.next, %n.vec
br i1 %13, label %middle.block, label %vector.body
middle.block: ; preds = %vector.body
%bin.rdx = add <4 x i32> %10, %9
%bin.rdx15 = add <4 x i32> %11, %bin.rdx
%bin.rdx16 = add <4 x i32> %12, %bin.rdx15
%14 = tail call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %bin.rdx16)
%cmp.n = icmp eq i64 %2, %n.vec
br i1 %cmp.n, label %._crit_edge, label %vec.epilog.iter.check
vec.epilog.iter.check: ; preds = %middle.block
%min.epilog.iters.check = icmp eq i64 %n.mod.vf, 0
br i1 %min.epilog.iters.check, label %vec.epilog.scalar.ph.preheader, label %vec.epilog.ph, !prof !29
vec.epilog.ph: ; preds = %vector.main.loop.iter.check, %vec.epilog.iter.check
%vec.epilog.resume.val = phi i64 [ %n.vec, %vec.epilog.iter.check ], [ 0, %vector.main.loop.iter.check ]
%bc.merge.rdx = phi i32 [ %14, %vec.epilog.iter.check ], [ 0, %vector.main.loop.iter.check ]
%n.vec18 = and i64 %2, 9223372036854775804
%15 = insertelement <4 x i32> <i32 poison, i32 0, i32 0, i32 0>, i32 %bc.merge.rdx, i64 0
br label %vec.epilog.vector.body
vec.epilog.vector.body: ; preds = %vec.epilog.vector.body, %vec.epilog.ph
%index19 = phi i64 [ %vec.epilog.resume.val, %vec.epilog.ph ], [ %index.next22, %vec.epilog.vector.body ]
%vec.phi20 = phi <4 x i32> [ %15, %vec.epilog.ph ], [ %17, %vec.epilog.vector.body ]
%16 = getelementptr inbounds nuw i32, ptr %4, i64 %index19
%wide.load21 = load <4 x i32>, ptr %16, align 4
%17 = add <4 x i32> %wide.load21, %vec.phi20
%index.next22 = add nuw i64 %index19, 4
%18 = icmp eq i64 %index.next22, %n.vec18
br i1 %18, label %vec.epilog.middle.block, label %vec.epilog.vector.body
vec.epilog.middle.block: ; preds = %vec.epilog.vector.body
%19 = tail call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %17)
%cmp.n23 = icmp eq i64 %2, %n.vec18
br i1 %cmp.n23, label %._crit_edge, label %vec.epilog.scalar.ph.preheader
vec.epilog.scalar.ph.preheader: ; preds = %iter.check, %vec.epilog.iter.check, %vec.epilog.middle.block
%.ph = phi i64 [ 0, %iter.check ], [ %n.vec, %vec.epilog.iter.check ], [ %n.vec18, %vec.epilog.middle.block ]
%.ph25 = phi i32 [ 0, %iter.check ], [ %14, %vec.epilog.iter.check ], [ %19, %vec.epilog.middle.block ]
br label %vec.epilog.scalar.ph
vec.epilog.scalar.ph: ; preds = %vec.epilog.scalar.ph.preheader, %vec.epilog.scalar.ph
%20 = phi i64 [ %25, %vec.epilog.scalar.ph ], [ %.ph, %vec.epilog.scalar.ph.preheader ]
%21 = phi i32 [ %24, %vec.epilog.scalar.ph ], [ %.ph25, %vec.epilog.scalar.ph.preheader ]
%22 = getelementptr inbounds nuw i32, ptr %4, i64 %20
%23 = load i32, ptr %22, align 4
%24 = add i32 %23, %21
%25 = add nuw nsw i64 %20, 1
%exitcond.not = icmp eq i64 %25, %2
br i1 %exitcond.not, label %._crit_edge, label %vec.epilog.scalar.ph
._crit_edge: ; preds = %vec.epilog.scalar.ph, %middle.block, %vec.epilog.middle.block, %1
%.lcssa3 = phi i32 [ 0, %1 ], [ %19, %vec.epilog.middle.block ], [ %14, %middle.block ], [ %24, %vec.epilog.scalar.ph ]
ret i32 %.lcssa3
}
define void @main.main() local_unnamed_addr #3 !dbg !35 {
main.Sum.exit:
%0 = tail call ptr @runtime.newobject(ptr nonnull @"type:[4]int32") #5
tail call void @runtime.printlock() #5
tail call void @runtime.printint(i64 10) #5
tail call void @runtime.printnl() #5
tail call void @runtime.printunlock() #5
ret void
}arm64 machine code
Vector instructions, on wide registers. 76 lines
main.Sum: cmp x1, #0x1 b.lt 0x1c <ltmp0+0x1c> cmp x1, #0x4 b.hs 0x28 <ltmp0+0x28> mov x9, #0x0 ; =0 mov w8, #0x0 ; =0 b 0xd4 <ltmp0+0xd4> mov w8, #0x0 ; =0 mov x0, x8 ret cmp x1, #0x10 b.hs 0x3c <ltmp0+0x3c> mov x9, #0x0 ; =0 mov w8, #0x0 ; =0 b 0x9c <ltmp0+0x9c> and x10, x1, #0xc and x9, x1, #0x7ffffffffffffff0 add x8, x0, #0x20 movi.2d v0, #0000000000000000 and x11, x1, #0x7ffffffffffffff0 movi.2d v1, #0000000000000000 movi.2d v2, #0000000000000000 movi.2d v3, #0000000000000000 ldp q4, q5, [x8, #-0x20] ldp q6, q7, [x8], #0x40 add.4s v0, v4, v0 add.4s v1, v5, v1 add.4s v2, v6, v2 add.4s v3, v7, v3 subs x11, x11, #0x10 b.ne 0x5c <ltmp0+0x5c> add.4s v0, v1, v0 add.4s v0, v2, v0 add.4s v0, v3, v0 addv.4s s0, v0 fmov w8, s0 cmp x1, x9 b.eq 0xec <ltmp0+0xec> cbz x10, 0xd4 <ltmp0+0xd4> mov x10, x9 and x9, x1, #0x7ffffffffffffffc movi.2d v0, #0000000000000000 mov.s v0[0], w8 sub x8, x10, x9 add x10, x0, x10, lsl #2 ldr q1, [x10], #0x10 add.4s v0, v1, v0 adds x8, x8, #0x4 b.ne 0xb4 <ltmp0+0xb4> addv.4s s0, v0 fmov w8, s0 cmp x1, x9 b.eq 0xec <ltmp0+0xec> sub x10, x1, x9 add x9, x0, x9, lsl #2 ldr w11, [x9], #0x4 add w8, w11, w8 subs x10, x10, #0x1 b.ne 0xdc <ltmp0+0xdc> mov x0, x8 ret main.main: stp x29, x30, [sp, #-0x10]! mov x29, sp adrp x0, 0x0 <ltmp0> ldr x0, [x0] bl 0x12c <_main.main+0x10> bl 0x130 <_main.main+0x14> mov w0, #0xa ; =10 bl 0x138 <_main.main+0x1c> bl 0x13c <_main.main+0x20> bl 0x140 <_main.main+0x24> ldp x29, x30, [sp], #0x10 ret