void lstm_forward(const int n, const double *ws, const double *hidden,
                  const double *inputs, double *new_hidden, double *outputs,
                  double *store) {
  for (int i = 0; i < n; i++) {
    const double x = 2 * inputs[0] - 1;
    const double g_i = inputs[1];
    const double g_f = inputs[2];
    const double g_o = inputs[3];
    const double h_old = hidden[0];
    const double h_add = x * g_i;
    const double h_keep = h_old * g_f;
    const double h_new = h_keep * ws[0] + h_add * ws[1];
    const double y = h_new * g_o;
    store[0] = x;
    store[1] = g_i;
    store[2] = g_f;
    store[3] = g_o;
    store[4] = h_old;
    store[5] = h_add;
    store[6] = h_keep;
    store[7] = h_new;
    new_hidden[0] = h_new;
    outputs[0] = y;
    inputs += 4;
    hidden += 1;
    ws += 2;
    new_hidden += 1;
    outputs += 1;
    store += 8;
  }
}


x86 assembly (clang, -O0):
<lstm_forward>:
push   %rbp
mov    %rsp,%rbp
mov    0x10(%rbp),%rax
mov    %edi,-0x4(%rbp)
mov    %rsi,-0x10(%rbp)
mov    %rdx,-0x18(%rbp)
mov    %rcx,-0x20(%rbp)
mov    %r8,-0x28(%rbp)
mov    %r9,-0x30(%rbp)
movl   $0x0,-0x34(%rbp)
mov    -0x34(%rbp),%eax
cmp    -0x4(%rbp),%eax
jge    12ba <lstm_forward+0x1ba>
mov    -0x20(%rbp),%rax
movsd  (%rax),%xmm0
addsd  %xmm0,%xmm0
movsd  0xeba(%rip),%xmm1
addsd  %xmm1,%xmm0
movsd  %xmm0,-0x40(%rbp)
mov    -0x20(%rbp),%rax
movsd  0x8(%rax),%xmm0
movsd  %xmm0,-0x48(%rbp)
mov    -0x20(%rbp),%rax
movsd  0x10(%rax),%xmm0
movsd  %xmm0,-0x50(%rbp)
mov    -0x20(%rbp),%rax
movsd  0x18(%rax),%xmm0
movsd  %xmm0,-0x58(%rbp)
mov    -0x18(%rbp),%rax
movsd  (%rax),%xmm0
movsd  %xmm0,-0x60(%rbp)
movsd  -0x40(%rbp),%xmm0
mulsd  -0x48(%rbp),%xmm0
movsd  %xmm0,-0x68(%rbp)
movsd  -0x60(%rbp),%xmm0
mulsd  -0x50(%rbp),%xmm0
movsd  %xmm0,-0x70(%rbp)
movsd  -0x70(%rbp),%xmm0
mov    -0x10(%rbp),%rax
movsd  (%rax),%xmm2
movsd  -0x68(%rbp),%xmm1
mov    -0x10(%rbp),%rax
mulsd  0x8(%rax),%xmm1
mulsd  %xmm2,%xmm0
addsd  %xmm1,%xmm0
movsd  %xmm0,-0x78(%rbp)
movsd  -0x78(%rbp),%xmm0
mulsd  -0x58(%rbp),%xmm0
movsd  %xmm0,-0x80(%rbp)
movsd  -0x40(%rbp),%xmm0
mov    0x10(%rbp),%rax
movsd  %xmm0,(%rax)
movsd  -0x48(%rbp),%xmm0
mov    0x10(%rbp),%rax
movsd  %xmm0,0x8(%rax)
movsd  -0x50(%rbp),%xmm0
mov    0x10(%rbp),%rax
movsd  %xmm0,0x10(%rax)
movsd  -0x58(%rbp),%xmm0
mov    0x10(%rbp),%rax
movsd  %xmm0,0x18(%rax)
movsd  -0x60(%rbp),%xmm0
mov    0x10(%rbp),%rax
movsd  %xmm0,0x20(%rax)
movsd  -0x68(%rbp),%xmm0
mov    0x10(%rbp),%rax
movsd  %xmm0,0x28(%rax)
movsd  -0x70(%rbp),%xmm0
mov    0x10(%rbp),%rax
movsd  %xmm0,0x30(%rax)
movsd  -0x78(%rbp),%xmm0
mov    0x10(%rbp),%rax
movsd  %xmm0,0x38(%rax)
movsd  -0x78(%rbp),%xmm0
mov    -0x28(%rbp),%rax
movsd  %xmm0,(%rax)
movsd  -0x80(%rbp),%xmm0
mov    -0x30(%rbp),%rax
movsd  %xmm0,(%rax)
mov    -0x20(%rbp),%rax
add    $0x20,%rax
mov    %rax,-0x20(%rbp)
mov    -0x18(%rbp),%rax
add    $0x8,%rax
mov    %rax,-0x18(%rbp)
mov    -0x10(%rbp),%rax
add    $0x10,%rax
mov    %rax,-0x10(%rbp)
mov    -0x28(%rbp),%rax
add    $0x8,%rax
mov    %rax,-0x28(%rbp)
mov    -0x30(%rbp),%rax
add    $0x8,%rax
mov    %rax,-0x30(%rbp)
mov    0x10(%rbp),%rax
add    $0x40,%rax
mov    %rax,0x10(%rbp)
mov    -0x34(%rbp),%eax
add    $0x1,%eax
mov    %eax,-0x34(%rbp)
jmp    1126 <lstm_forward+0x26>
pop    %rbp
ret

x86 assembly (clang, -O2):
<lstm_forward>:
test   %edi,%edi
jle    11ab <lstm_forward+0xab>
mov    0x8(%rsp),%r10
xor    %eax,%eax
movsd  0xee9(%rip),%xmm0
nopw   0x0(%rax,%rax,1)
movsd  (%rcx,%rax,4),%xmm1
addsd  %xmm1,%xmm1
addsd  %xmm0,%xmm1
movupd 0x8(%rcx,%rax,4),%xmm2
movsd  0x18(%rcx,%rax,4),%xmm3
movsd  (%rdx,%rax,1),%xmm4
movapd %xmm1,%xmm5
unpcklpd %xmm4,%xmm5
mulpd  %xmm2,%xmm5
movsd  0x8(%rsi,%rax,2),%xmm6
mulsd  %xmm5,%xmm6
movapd %xmm5,%xmm7
unpckhpd %xmm5,%xmm7
mulsd  (%rsi,%rax,2),%xmm7
addsd  %xmm6,%xmm7
movsd  %xmm1,(%r10,%rax,8)
movupd %xmm2,0x8(%r10,%rax,8)
movsd  %xmm3,0x18(%r10,%rax,8)
mulsd  %xmm7,%xmm3
movsd  %xmm4,0x20(%r10,%rax,8)
movupd %xmm5,0x28(%r10,%rax,8)
movsd  %xmm7,0x38(%r10,%rax,8)
movsd  %xmm7,(%r8,%rax,1)
movsd  %xmm3,(%r9,%rax,1)
add    $0x8,%rax
add    $0xffffffff,%edi
jne    1120 <lstm_forward+0x20>
ret

LLVM IR (-O0):
define void @lstm_forward(i32 %0, double* %1, double* %2, double* %3, double* %4, double* %5, double* %6) {
%8 = alloca i32
%9 = alloca double*
%10 = alloca double*
%11 = alloca double*
%12 = alloca double*
%13 = alloca double*
%14 = alloca double*
%15 = alloca i32
%16 = alloca double
%17 = alloca double
%18 = alloca double
%19 = alloca double
%20 = alloca double
%21 = alloca double
%22 = alloca double
%23 = alloca double
%24 = alloca double
store i32 %0, i32* %8
store double* %1, double** %9
store double* %2, double** %10
store double* %3, double** %11
store double* %4, double** %12
store double* %5, double** %13
store double* %6, double** %14
store i32 0, i32* %15
br label %25
25: ; preds = %107, %7
%26 = load i32, i32* %15
%27 = load i32, i32* %8
%28 = icmp slt i32 %26, %27
br i1 %28, label %29, label %110
29: ; preds = %25
%30 = load double*, double** %11
%31 = getelementptr inbounds double, double* %30, i64 0
%32 = load double, double* %31
%33 = call double @llvm.fmuladd.f64(double 2.000000e+00, double %32, double -1.000000e+00)
store double %33, double* %16
%34 = load double*, double** %11
%35 = getelementptr inbounds double, double* %34, i64 1
%36 = load double, double* %35
store double %36, double* %17
%37 = load double*, double** %11
%38 = getelementptr inbounds double, double* %37, i64 2
%39 = load double, double* %38
store double %39, double* %18
%40 = load double*, double** %11
%41 = getelementptr inbounds double, double* %40, i64 3
%42 = load double, double* %41
store double %42, double* %19
%43 = load double*, double** %10
%44 = getelementptr inbounds double, double* %43, i64 0
%45 = load double, double* %44
store double %45, double* %20
%46 = load double, double* %16
%47 = load double, double* %17
%48 = fmul double %46, %47
store double %48, double* %21
%49 = load double, double* %20
%50 = load double, double* %18
%51 = fmul double %49, %50
store double %51, double* %22
%52 = load double, double* %22
%53 = load double*, double** %9
%54 = getelementptr inbounds double, double* %53, i64 0
%55 = load double, double* %54
%56 = load double, double* %21
%57 = load double*, double** %9
%58 = getelementptr inbounds double, double* %57, i64 1
%59 = load double, double* %58
%60 = fmul double %56, %59
%61 = call double @llvm.fmuladd.f64(double %52, double %55, double %60)
store double %61, double* %23
%62 = load double, double* %23
%63 = load double, double* %19
%64 = fmul double %62, %63
store double %64, double* %24
%65 = load double, double* %16
%66 = load double*, double** %14
%67 = getelementptr inbounds double, double* %66, i64 0
store double %65, double* %67
%68 = load double, double* %17
%69 = load double*, double** %14
%70 = getelementptr inbounds double, double* %69, i64 1
store double %68, double* %70
%71 = load double, double* %18
%72 = load double*, double** %14
%73 = getelementptr inbounds double, double* %72, i64 2
store double %71, double* %73
%74 = load double, double* %19
%75 = load double*, double** %14
%76 = getelementptr inbounds double, double* %75, i64 3
store double %74, double* %76
%77 = load double, double* %20
%78 = load double*, double** %14
%79 = getelementptr inbounds double, double* %78, i64 4
store double %77, double* %79
%80 = load double, double* %21
%81 = load double*, double** %14
%82 = getelementptr inbounds double, double* %81, i64 5
store double %80, double* %82
%83 = load double, double* %22
%84 = load double*, double** %14
%85 = getelementptr inbounds double, double* %84, i64 6
store double %83, double* %85
%86 = load double, double* %23
%87 = load double*, double** %14
%88 = getelementptr inbounds double, double* %87, i64 7
store double %86, double* %88
%89 = load double, double* %23
%90 = load double*, double** %12
%91 = getelementptr inbounds double, double* %90, i64 0
store double %89, double* %91
%92 = load double, double* %24
%93 = load double*, double** %13
%94 = getelementptr inbounds double, double* %93, i64 0
store double %92, double* %94
%95 = load double*, double** %11
%96 = getelementptr inbounds double, double* %95, i64 4
store double* %96, double** %11
%97 = load double*, double** %10
%98 = getelementptr inbounds double, double* %97, i64 1
store double* %98, double** %10
%99 = load double*, double** %9
%100 = getelementptr inbounds double, double* %99, i64 2
store double* %100, double** %9
%101 = load double*, double** %12
%102 = getelementptr inbounds double, double* %101, i64 1
store double* %102, double** %12
%103 = load double*, double** %13
%104 = getelementptr inbounds double, double* %103, i64 1
store double* %104, double** %13
%105 = load double*, double** %14
%106 = getelementptr inbounds double, double* %105, i64 8
store double* %106, double** %14
br label %107
107: ; preds = %29
%108 = load i32, i32* %15
%109 = add nsw i32 %108, 1
store i32 %109, i32* %15
br label %25
110: ; preds = %25
ret void
}

LLVM IR (-O2):
define void @lstm_forward(i32 %0, double* nocapture %1, double* nocapture %2, double* nocapture %3, double* nocapture writeonly %4, double* nocapture writeonly %5, double* nocapture writeonly %6) {
%8 = icmp sgt i32 %0, 0
br i1 %8, label %10, label %9
9: ; preds = %10, %7
ret void
10: ; preds = %7, %10
%11 = phi double* [ %46, %10 ], [ %1, %7 ]
%12 = phi double* [ %45, %10 ], [ %2, %7 ]
%13 = phi double* [ %44, %10 ], [ %3, %7 ]
%14 = phi double* [ %47, %10 ], [ %4, %7 ]
%15 = phi double* [ %48, %10 ], [ %5, %7 ]
%16 = phi double* [ %49, %10 ], [ %6, %7 ]
%17 = phi i32 [ %50, %10 ], [ 0, %7 ]
%18 = load double, double* %13
%19 = tail call double @llvm.fmuladd.f64(double %18, double 2.000000e+00, double -1.000000e+00)
%20 = getelementptr inbounds double, double* %13, i64 1
%21 = bitcast double* %20 to <2 x double>*
%22 = load <2 x double>, <2 x double>* %21
%23 = getelementptr inbounds double, double* %13, i64 3
%24 = load double, double* %23
%25 = load double, double* %12
%26 = insertelement <2 x double> poison, double %19, i64 0
%27 = insertelement <2 x double> %26, double %25, i64 1
%28 = fmul <2 x double> %27, %22
%29 = load double, double* %11
%30 = getelementptr inbounds double, double* %11, i64 1
%31 = load double, double* %30
%32 = extractelement <2 x double> %28, i64 0
%33 = fmul double %32, %31
%34 = extractelement <2 x double> %28, i64 1
%35 = tail call double @llvm.fmuladd.f64(double %34, double %29, double %33)
%36 = fmul double %24, %35
store double %19, double* %16
%37 = getelementptr inbounds double, double* %16, i64 1
%38 = bitcast double* %37 to <2 x double>*
store <2 x double> %22, <2 x double>* %38
%39 = getelementptr inbounds double, double* %16, i64 3
store double %24, double* %39
%40 = getelementptr inbounds double, double* %16, i64 4
store double %25, double* %40
%41 = getelementptr inbounds double, double* %16, i64 5
%42 = bitcast double* %41 to <2 x double>*
store <2 x double> %28, <2 x double>* %42
%43 = getelementptr inbounds double, double* %16, i64 7
store double %35, double* %43
store double %35, double* %14
store double %36, double* %15
%44 = getelementptr inbounds double, double* %13, i64 4
%45 = getelementptr inbounds double, double* %12, i64 1
%46 = getelementptr inbounds double, double* %11, i64 2
%47 = getelementptr inbounds double, double* %14, i64 1
%48 = getelementptr inbounds double, double* %15, i64 1
%49 = getelementptr inbounds double, double* %16, i64 8
%50 = add nuw nsw i32 %17, 1
%51 = icmp eq i32 %50, %0
br i1 %51, label %9, label %10
}